bcdf59aafdbd8a52a36579073f49ff2bf9b6c8f8
[collectd.git] / src / common.c
1 /**
2  * collectd - src/common.c
3  * Copyright (C) 2005-2007  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  *   Niki W. Waibel <niki.waibel@gmx.net>
21 **/
22
23 #if HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include "common.h"
28 #include "plugin.h"
29
30 #ifdef HAVE_MATH_H
31 #  include <math.h>
32 #endif
33
34 /* for ntohl and htonl */
35 #if HAVE_ARPA_INET_H
36 # include <arpa/inet.h>
37 #endif
38
39 #ifdef HAVE_LIBKSTAT
40 extern kstat_ctl_t *kc;
41 #endif
42
43 void sstrncpy (char *d, const char *s, int len)
44 {
45         strncpy (d, s, len);
46         d[len - 1] = '\0';
47 }
48
49 char *sstrdup (const char *s)
50 {
51         char *r;
52
53         if (s == NULL)
54                 return (NULL);
55
56         if((r = strdup (s)) == NULL)
57         {
58                 DEBUG ("Not enough memory.");
59                 exit(3);
60         }
61
62         return (r);
63 }
64
65 /* Don't use the return value of `strerror_r', because the GNU-people got
66  * inventive there.. -octo */
67 char *sstrerror (int errnum, char *buf, size_t buflen)
68 {
69         buf[0] = '\0';
70         strerror_r (errnum, buf, buflen);
71         return (buf);
72 } /* char *sstrerror */
73
74 void *smalloc (size_t size)
75 {
76         void *r;
77
78         if ((r = malloc (size)) == NULL)
79         {
80                 DEBUG("Not enough memory.");
81                 exit(3);
82         }
83
84         return r;
85 }
86
87 #if 0
88 void sfree (void **ptr)
89 {
90         if (ptr == NULL)
91                 return;
92
93         if (*ptr != NULL)
94                 free (*ptr);
95
96         *ptr = NULL;
97 }
98 #endif
99
100 ssize_t sread (int fd, void *buf, size_t count)
101 {
102         char    *ptr;
103         size_t   nleft;
104         ssize_t  status;
105
106         ptr   = (char *) buf;
107         nleft = count;
108
109         while (nleft > 0)
110         {
111                 status = read (fd, (void *) ptr, nleft);
112
113                 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
114                         continue;
115
116                 if (status < 0)
117                         return (status);
118
119                 if (status == 0)
120                 {
121                         DEBUG ("Received EOF from fd %i. "
122                                         "Closing fd and returning error.",
123                                         fd);
124                         close (fd);
125                         return (-1);
126                 }
127
128                 assert (nleft >= status);
129
130                 nleft = nleft - status;
131                 ptr   = ptr   + status;
132         }
133
134         return (0);
135 }
136
137
138 ssize_t swrite (int fd, const void *buf, size_t count)
139 {
140         const char *ptr;
141         size_t      nleft;
142         ssize_t     status;
143
144         ptr   = (const char *) buf;
145         nleft = count;
146
147         while (nleft > 0)
148         {
149                 status = write (fd, (const void *) ptr, nleft);
150
151                 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
152                         continue;
153
154                 if (status < 0)
155                         return (status);
156
157                 nleft = nleft - status;
158                 ptr   = ptr   + status;
159         }
160
161         return (0);
162 }
163
164 int strsplit (char *string, char **fields, size_t size)
165 {
166         size_t i;
167         char *ptr;
168         char *saveptr;
169
170         i = 0;
171         ptr = string;
172         saveptr = NULL;
173         while ((fields[i] = strtok_r (ptr, " \t", &saveptr)) != NULL)
174         {
175                 ptr = NULL;
176                 i++;
177
178                 if (i >= size)
179                         break;
180         }
181
182         return (i);
183 }
184
185 int strjoin (char *dst, size_t dst_len,
186                 char **fields, size_t fields_num,
187                 const char *sep)
188 {
189         int field_len;
190         int sep_len;
191         int i;
192
193         memset (dst, '\0', dst_len);
194
195         if (fields_num <= 0)
196                 return (-1);
197
198         sep_len = 0;
199         if (sep != NULL)
200                 sep_len = strlen (sep);
201
202         for (i = 0; i < fields_num; i++)
203         {
204                 if ((i > 0) && (sep_len > 0))
205                 {
206                         if (dst_len <= sep_len)
207                                 return (-1);
208
209                         strncat (dst, sep, dst_len);
210                         dst_len -= sep_len;
211                 }
212
213                 field_len = strlen (fields[i]);
214
215                 if (dst_len <= field_len)
216                         return (-1);
217
218                 strncat (dst, fields[i], dst_len);
219                 dst_len -= field_len;
220         }
221
222         return (strlen (dst));
223 }
224
225 int strsubstitute (char *str, char c_from, char c_to)
226 {
227         int ret;
228
229         if (str == NULL)
230                 return (-1);
231
232         ret = 0;
233         while (*str != '\0')
234         {
235                 if (*str == c_from)
236                 {
237                         *str = c_to;
238                         ret++;
239                 }
240                 str++;
241         }
242
243         return (ret);
244 }
245
246 int escape_slashes (char *buf, int buf_len)
247 {
248         int i;
249
250         if (strcmp (buf, "/") == 0)
251         {
252                 if (buf_len < 5)
253                         return (-1);
254
255                 strncpy (buf, "root", buf_len);
256                 return (0);
257         }
258
259         /* Move one to the left */
260         memmove (buf, buf + 1, buf_len - 1);
261
262         for (i = 0; i < buf_len - 1; i++)
263         {
264                 if (buf[i] == '\0')
265                         break;
266                 else if (buf[i] == '/')
267                         buf[i] = '_';
268         }
269         buf[i] = '\0';
270
271         return (0);
272 }
273
274 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
275 {
276         if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
277                 return (-2);
278
279         if ((tv0->tv_sec < tv1->tv_sec)
280                         || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
281                 return (-1);
282
283         ret->tv_sec  = tv0->tv_sec - tv1->tv_sec;
284         ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
285
286         if (ret->tv_nsec < 0)
287         {
288                 assert (ret->tv_sec > 0);
289
290                 ret->tv_nsec += 1000000000;
291                 ret->tv_sec  -= 1;
292         }
293
294         return (0);
295 }
296
297 int check_create_dir (const char *file_orig)
298 {
299         struct stat statbuf;
300
301         char  file_copy[512];
302         char  dir[512];
303         int   dir_len = 512;
304         char *fields[16];
305         int   fields_num;
306         char *ptr;
307         char *saveptr;
308         int   last_is_file = 1;
309         int   path_is_absolute = 0;
310         int   len;
311         int   i;
312
313         /*
314          * Sanity checks first
315          */
316         if (file_orig == NULL)
317                 return (-1);
318
319         if ((len = strlen (file_orig)) < 1)
320                 return (-1);
321         else if (len >= 512)
322                 return (-1);
323
324         /*
325          * If `file_orig' ends in a slash the last component is a directory,
326          * otherwise it's a file. Act accordingly..
327          */
328         if (file_orig[len - 1] == '/')
329                 last_is_file = 0;
330         if (file_orig[0] == '/')
331                 path_is_absolute = 1;
332
333         /*
334          * Create a copy for `strtok_r' to destroy
335          */
336         strncpy (file_copy, file_orig, 512);
337         file_copy[511] = '\0';
338
339         /*
340          * Break into components. This will eat up several slashes in a row and
341          * remove leading and trailing slashes..
342          */
343         ptr = file_copy;
344         saveptr = NULL;
345         fields_num = 0;
346         while ((fields[fields_num] = strtok_r (ptr, "/", &saveptr)) != NULL)
347         {
348                 ptr = NULL;
349                 fields_num++;
350
351                 if (fields_num >= 16)
352                         break;
353         }
354
355         /*
356          * For each component, do..
357          */
358         for (i = 0; i < (fields_num - last_is_file); i++)
359         {
360                 /*
361                  * Do not create directories that start with a dot. This
362                  * prevents `../../' attacks and other likely malicious
363                  * behavior.
364                  */
365                 if (fields[i][0] == '.')
366                 {
367                         ERROR ("Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
368                         return (-2);
369                 }
370
371                 /*
372                  * Join the components together again
373                  */
374                 dir[0] = '/';
375                 if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
376                                         fields, i + 1, "/") < 0)
377                 {
378                         ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
379                         return (-1);
380                 }
381
382                 if (stat (dir, &statbuf) == -1)
383                 {
384                         if (errno == ENOENT)
385                         {
386                                 if (mkdir (dir, 0755) == -1)
387                                 {
388                                         char errbuf[1024];
389                                         ERROR ("mkdir (%s): %s", dir,
390                                                         sstrerror (errno,
391                                                                 errbuf, sizeof (errbuf)));
392                                         return (-1);
393                                 }
394                         }
395                         else
396                         {
397                                 char errbuf[1024];
398                                 ERROR ("stat (%s): %s", dir,
399                                                 sstrerror (errno, errbuf,
400                                                         sizeof (errbuf)));
401                                 return (-1);
402                         }
403                 }
404                 else if (!S_ISDIR (statbuf.st_mode))
405                 {
406                         ERROR ("stat (%s): Not a directory!", dir);
407                         return (-1);
408                 }
409         }
410
411         return (0);
412 }
413
414 #ifdef HAVE_LIBKSTAT
415 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
416 {
417         char ident[128];
418         
419         if (kc == NULL)
420                 return (-1);
421
422         snprintf (ident, 128, "%s,%i,%s", module, instance, name);
423         ident[127] = '\0';
424
425         if (*ksp_ptr == NULL)
426         {
427                 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
428                 {
429                         ERROR ("Cound not find kstat %s", ident);
430                         return (-1);
431                 }
432
433                 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
434                 {
435                         WARNING ("kstat %s has wrong type", ident);
436                         *ksp_ptr = NULL;
437                         return (-1);
438                 }
439         }
440
441 #ifdef assert
442         assert (*ksp_ptr != NULL);
443         assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
444 #endif
445
446         if (kstat_read (kc, *ksp_ptr, NULL) == -1)
447         {
448                 WARNING ("kstat %s could not be read", ident);
449                 return (-1);
450         }
451
452         if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
453         {
454                 WARNING ("kstat %s has wrong type", ident);
455                 return (-1);
456         }
457
458         return (0);
459 }
460
461 long long get_kstat_value (kstat_t *ksp, char *name)
462 {
463         kstat_named_t *kn;
464         long long retval = -1LL;
465
466 #ifdef assert
467         assert (ksp != NULL);
468         assert (ksp->ks_type == KSTAT_TYPE_NAMED);
469 #else
470         if (ksp == NULL)
471         {
472                 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
473                 return (-1LL);
474         }
475         else if (ksp->ks_type != KSTAT_TYPE_NAMED)
476         {
477                 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
478                 return (-1LL);
479         }
480 #endif
481
482         if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
483                 return (retval);
484
485         if (kn->data_type == KSTAT_DATA_INT32)
486                 retval = (long long) kn->value.i32;
487         else if (kn->data_type == KSTAT_DATA_UINT32)
488                 retval = (long long) kn->value.ui32;
489         else if (kn->data_type == KSTAT_DATA_INT64)
490                 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
491         else if (kn->data_type == KSTAT_DATA_UINT64)
492                 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
493         else
494                 WARNING ("get_kstat_value: Not a numeric value: %s", name);
495                  
496         return (retval);
497 }
498 #endif /* HAVE_LIBKSTAT */
499
500 unsigned long long ntohll (unsigned long long n)
501 {
502 #if __BYTE_ORDER == __BIG_ENDIAN
503         return (n);
504 #else
505         return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
506 #endif
507 } /* unsigned long long ntohll */
508
509 unsigned long long htonll (unsigned long long n)
510 {
511 #if __BYTE_ORDER == __BIG_ENDIAN
512         return (n);
513 #else
514         return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
515 #endif
516 } /* unsigned long long htonll */
517
518 int format_name (char *ret, int ret_len,
519                 const char *hostname,
520                 const char *plugin, const char *plugin_instance,
521                 const char *type, const char *type_instance)
522 {
523         int  status;
524
525         assert (plugin != NULL);
526         assert (type != NULL);
527
528         if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
529         {
530                 if ((type_instance == NULL) || (strlen (type_instance) == 0))
531                         status = snprintf (ret, ret_len, "%s/%s/%s",
532                                         hostname, plugin, type);
533                 else
534                         status = snprintf (ret, ret_len, "%s/%s/%s-%s",
535                                         hostname, plugin, type,
536                                         type_instance);
537         }
538         else
539         {
540                 if ((type_instance == NULL) || (strlen (type_instance) == 0))
541                         status = snprintf (ret, ret_len, "%s/%s-%s/%s",
542                                         hostname, plugin, plugin_instance,
543                                         type);
544                 else
545                         status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s",
546                                         hostname, plugin, plugin_instance,
547                                         type, type_instance);
548         }
549
550         if ((status < 1) || (status >= ret_len))
551                 return (-1);
552         return (0);
553 } /* int format_name */