Merge branch 'collectd-3.11' into collectd-4.0
[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 #if HAVE_PTHREAD_H
31 # include <pthread.h>
32 #endif
33
34 #ifdef HAVE_MATH_H
35 # include <math.h>
36 #endif
37
38 /* for ntohl and htonl */
39 #if HAVE_ARPA_INET_H
40 # include <arpa/inet.h>
41 #endif
42
43 #ifdef HAVE_LIBKSTAT
44 extern kstat_ctl_t *kc;
45 #endif
46
47 #if !HAVE_GETPWNAM_R
48 static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
49 #endif
50
51 void sstrncpy (char *d, const char *s, int len)
52 {
53         strncpy (d, s, len);
54         d[len - 1] = '\0';
55 }
56
57 char *sstrdup (const char *s)
58 {
59         char *r;
60
61         if (s == NULL)
62                 return (NULL);
63
64         if((r = strdup (s)) == NULL)
65         {
66                 DEBUG ("Not enough memory.");
67                 exit(3);
68         }
69
70         return (r);
71 }
72
73 /* Even though Posix requires "strerror_r" to return an "int",
74  * some systems (e.g. the GNU libc) return a "char *" _and_
75  * ignore the second argument ... -tokkee */
76 char *sstrerror (int errnum, char *buf, size_t buflen)
77 {
78         buf[0] = '\0';
79 #ifdef STRERROR_R_CHAR_P
80         {
81                 char *temp;
82                 temp = strerror_r (errnum, buf, buflen);
83                 if (buf[0] == '\0')
84                 {
85                         if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
86                                 strncpy (buf, temp, buflen);
87                         else
88                                 strncpy (buf, "strerror_r did not return "
89                                                 "an error message", buflen);
90                         buf[buflen - 1] = '\0';
91                 }
92         }
93 #else
94         if (strerror_r (errnum, buf, buflen) != 0)
95         {
96                 snprintf (buf, buflen, "Error #%i; "
97                                 "Additionally, strerror_r failed.",
98                                 errnum);
99         }
100 #endif /* STRERROR_R_CHAR_P */
101         buf[buflen - 1] = '\0';
102         return (buf);
103 } /* char *sstrerror */
104
105 void *smalloc (size_t size)
106 {
107         void *r;
108
109         if ((r = malloc (size)) == NULL)
110         {
111                 DEBUG("Not enough memory.");
112                 exit(3);
113         }
114
115         return r;
116 }
117
118 #if 0
119 void sfree (void **ptr)
120 {
121         if (ptr == NULL)
122                 return;
123
124         if (*ptr != NULL)
125                 free (*ptr);
126
127         *ptr = NULL;
128 }
129 #endif
130
131 ssize_t sread (int fd, void *buf, size_t count)
132 {
133         char    *ptr;
134         size_t   nleft;
135         ssize_t  status;
136
137         ptr   = (char *) buf;
138         nleft = count;
139
140         while (nleft > 0)
141         {
142                 status = read (fd, (void *) ptr, nleft);
143
144                 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
145                         continue;
146
147                 if (status < 0)
148                         return (status);
149
150                 if (status == 0)
151                 {
152                         DEBUG ("Received EOF from fd %i. "
153                                         "Closing fd and returning error.",
154                                         fd);
155                         close (fd);
156                         return (-1);
157                 }
158
159                 assert (nleft >= status);
160
161                 nleft = nleft - status;
162                 ptr   = ptr   + status;
163         }
164
165         return (0);
166 }
167
168
169 ssize_t swrite (int fd, const void *buf, size_t count)
170 {
171         const char *ptr;
172         size_t      nleft;
173         ssize_t     status;
174
175         ptr   = (const char *) buf;
176         nleft = count;
177
178         while (nleft > 0)
179         {
180                 status = write (fd, (const void *) ptr, nleft);
181
182                 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
183                         continue;
184
185                 if (status < 0)
186                         return (status);
187
188                 nleft = nleft - status;
189                 ptr   = ptr   + status;
190         }
191
192         return (0);
193 }
194
195 int strsplit (char *string, char **fields, size_t size)
196 {
197         size_t i;
198         char *ptr;
199         char *saveptr;
200
201         i = 0;
202         ptr = string;
203         saveptr = NULL;
204         while ((fields[i] = strtok_r (ptr, " \t", &saveptr)) != NULL)
205         {
206                 ptr = NULL;
207                 i++;
208
209                 if (i >= size)
210                         break;
211         }
212
213         return (i);
214 }
215
216 int strjoin (char *dst, size_t dst_len,
217                 char **fields, size_t fields_num,
218                 const char *sep)
219 {
220         int field_len;
221         int sep_len;
222         int i;
223
224         memset (dst, '\0', dst_len);
225
226         if (fields_num <= 0)
227                 return (-1);
228
229         sep_len = 0;
230         if (sep != NULL)
231                 sep_len = strlen (sep);
232
233         for (i = 0; i < fields_num; i++)
234         {
235                 if ((i > 0) && (sep_len > 0))
236                 {
237                         if (dst_len <= sep_len)
238                                 return (-1);
239
240                         strncat (dst, sep, dst_len);
241                         dst_len -= sep_len;
242                 }
243
244                 field_len = strlen (fields[i]);
245
246                 if (dst_len <= field_len)
247                         return (-1);
248
249                 strncat (dst, fields[i], dst_len);
250                 dst_len -= field_len;
251         }
252
253         return (strlen (dst));
254 }
255
256 int strsubstitute (char *str, char c_from, char c_to)
257 {
258         int ret;
259
260         if (str == NULL)
261                 return (-1);
262
263         ret = 0;
264         while (*str != '\0')
265         {
266                 if (*str == c_from)
267                 {
268                         *str = c_to;
269                         ret++;
270                 }
271                 str++;
272         }
273
274         return (ret);
275 } /* int strsubstitute */
276
277 int escape_slashes (char *buf, int buf_len)
278 {
279         int i;
280
281         if (strcmp (buf, "/") == 0)
282         {
283                 if (buf_len < 5)
284                         return (-1);
285
286                 strncpy (buf, "root", buf_len);
287                 return (0);
288         }
289
290         if (buf_len <= 1)
291                 return (0);
292
293         /* Move one to the left */
294         if (buf[0] == '/')
295                 memmove (buf, buf + 1, buf_len - 1);
296
297         for (i = 0; i < buf_len - 1; i++)
298         {
299                 if (buf[i] == '\0')
300                         break;
301                 else if (buf[i] == '/')
302                         buf[i] = '_';
303         }
304         buf[i] = '\0';
305
306         return (0);
307 } /* int escape_slashes */
308
309 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
310 {
311         if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
312                 return (-2);
313
314         if ((tv0->tv_sec < tv1->tv_sec)
315                         || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
316                 return (-1);
317
318         ret->tv_sec  = tv0->tv_sec - tv1->tv_sec;
319         ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
320
321         if (ret->tv_nsec < 0)
322         {
323                 assert (ret->tv_sec > 0);
324
325                 ret->tv_nsec += 1000000000;
326                 ret->tv_sec  -= 1;
327         }
328
329         return (0);
330 }
331
332 int check_create_dir (const char *file_orig)
333 {
334         struct stat statbuf;
335
336         char  file_copy[512];
337         char  dir[512];
338         int   dir_len = 512;
339         char *fields[16];
340         int   fields_num;
341         char *ptr;
342         char *saveptr;
343         int   last_is_file = 1;
344         int   path_is_absolute = 0;
345         int   len;
346         int   i;
347
348         /*
349          * Sanity checks first
350          */
351         if (file_orig == NULL)
352                 return (-1);
353
354         if ((len = strlen (file_orig)) < 1)
355                 return (-1);
356         else if (len >= 512)
357                 return (-1);
358
359         /*
360          * If `file_orig' ends in a slash the last component is a directory,
361          * otherwise it's a file. Act accordingly..
362          */
363         if (file_orig[len - 1] == '/')
364                 last_is_file = 0;
365         if (file_orig[0] == '/')
366                 path_is_absolute = 1;
367
368         /*
369          * Create a copy for `strtok_r' to destroy
370          */
371         strncpy (file_copy, file_orig, 512);
372         file_copy[511] = '\0';
373
374         /*
375          * Break into components. This will eat up several slashes in a row and
376          * remove leading and trailing slashes..
377          */
378         ptr = file_copy;
379         saveptr = NULL;
380         fields_num = 0;
381         while ((fields[fields_num] = strtok_r (ptr, "/", &saveptr)) != NULL)
382         {
383                 ptr = NULL;
384                 fields_num++;
385
386                 if (fields_num >= 16)
387                         break;
388         }
389
390         /*
391          * For each component, do..
392          */
393         for (i = 0; i < (fields_num - last_is_file); i++)
394         {
395                 /*
396                  * Do not create directories that start with a dot. This
397                  * prevents `../../' attacks and other likely malicious
398                  * behavior.
399                  */
400                 if (fields[i][0] == '.')
401                 {
402                         ERROR ("Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
403                         return (-2);
404                 }
405
406                 /*
407                  * Join the components together again
408                  */
409                 dir[0] = '/';
410                 if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
411                                         fields, i + 1, "/") < 0)
412                 {
413                         ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
414                         return (-1);
415                 }
416
417                 if (stat (dir, &statbuf) == -1)
418                 {
419                         if (errno == ENOENT)
420                         {
421                                 if (mkdir (dir, 0755) == -1)
422                                 {
423                                         char errbuf[1024];
424                                         ERROR ("check_create_dir: mkdir (%s): %s", dir,
425                                                         sstrerror (errno,
426                                                                 errbuf, sizeof (errbuf)));
427                                         return (-1);
428                                 }
429                         }
430                         else
431                         {
432                                 char errbuf[1024];
433                                 ERROR ("stat (%s): %s", dir,
434                                                 sstrerror (errno, errbuf,
435                                                         sizeof (errbuf)));
436                                 return (-1);
437                         }
438                 }
439                 else if (!S_ISDIR (statbuf.st_mode))
440                 {
441                         ERROR ("stat (%s): Not a directory!", dir);
442                         return (-1);
443                 }
444         }
445
446         return (0);
447 } /* check_create_dir */
448
449 #ifdef HAVE_LIBKSTAT
450 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
451 {
452         char ident[128];
453         
454         if (kc == NULL)
455                 return (-1);
456
457         snprintf (ident, 128, "%s,%i,%s", module, instance, name);
458         ident[127] = '\0';
459
460         if (*ksp_ptr == NULL)
461         {
462                 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
463                 {
464                         ERROR ("Cound not find kstat %s", ident);
465                         return (-1);
466                 }
467
468                 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
469                 {
470                         WARNING ("kstat %s has wrong type", ident);
471                         *ksp_ptr = NULL;
472                         return (-1);
473                 }
474         }
475
476 #ifdef assert
477         assert (*ksp_ptr != NULL);
478         assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
479 #endif
480
481         if (kstat_read (kc, *ksp_ptr, NULL) == -1)
482         {
483                 WARNING ("kstat %s could not be read", ident);
484                 return (-1);
485         }
486
487         if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
488         {
489                 WARNING ("kstat %s has wrong type", ident);
490                 return (-1);
491         }
492
493         return (0);
494 }
495
496 long long get_kstat_value (kstat_t *ksp, char *name)
497 {
498         kstat_named_t *kn;
499         long long retval = -1LL;
500
501 #ifdef assert
502         assert (ksp != NULL);
503         assert (ksp->ks_type == KSTAT_TYPE_NAMED);
504 #else
505         if (ksp == NULL)
506         {
507                 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
508                 return (-1LL);
509         }
510         else if (ksp->ks_type != KSTAT_TYPE_NAMED)
511         {
512                 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
513                 return (-1LL);
514         }
515 #endif
516
517         if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
518                 return (retval);
519
520         if (kn->data_type == KSTAT_DATA_INT32)
521                 retval = (long long) kn->value.i32;
522         else if (kn->data_type == KSTAT_DATA_UINT32)
523                 retval = (long long) kn->value.ui32;
524         else if (kn->data_type == KSTAT_DATA_INT64)
525                 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
526         else if (kn->data_type == KSTAT_DATA_UINT64)
527                 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
528         else
529                 WARNING ("get_kstat_value: Not a numeric value: %s", name);
530                  
531         return (retval);
532 }
533 #endif /* HAVE_LIBKSTAT */
534
535 unsigned long long ntohll (unsigned long long n)
536 {
537 #if __BYTE_ORDER == __BIG_ENDIAN
538         return (n);
539 #else
540         return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
541 #endif
542 } /* unsigned long long ntohll */
543
544 unsigned long long htonll (unsigned long long n)
545 {
546 #if __BYTE_ORDER == __BIG_ENDIAN
547         return (n);
548 #else
549         return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
550 #endif
551 } /* unsigned long long htonll */
552
553 int format_name (char *ret, int ret_len,
554                 const char *hostname,
555                 const char *plugin, const char *plugin_instance,
556                 const char *type, const char *type_instance)
557 {
558         int  status;
559
560         assert (plugin != NULL);
561         assert (type != NULL);
562
563         if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
564         {
565                 if ((type_instance == NULL) || (strlen (type_instance) == 0))
566                         status = snprintf (ret, ret_len, "%s/%s/%s",
567                                         hostname, plugin, type);
568                 else
569                         status = snprintf (ret, ret_len, "%s/%s/%s-%s",
570                                         hostname, plugin, type,
571                                         type_instance);
572         }
573         else
574         {
575                 if ((type_instance == NULL) || (strlen (type_instance) == 0))
576                         status = snprintf (ret, ret_len, "%s/%s-%s/%s",
577                                         hostname, plugin, plugin_instance,
578                                         type);
579                 else
580                         status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s",
581                                         hostname, plugin, plugin_instance,
582                                         type, type_instance);
583         }
584
585         if ((status < 1) || (status >= ret_len))
586                 return (-1);
587         return (0);
588 } /* int format_name */
589
590 int parse_identifier (char *str, char **ret_host,
591                 char **ret_plugin, char **ret_plugin_instance,
592                 char **ret_type, char **ret_type_instance)
593 {
594         char *hostname = NULL;
595         char *plugin = NULL;
596         char *plugin_instance = NULL;
597         char *type = NULL;
598         char *type_instance = NULL;
599
600         hostname = str;
601         if (hostname == NULL)
602                 return (-1);
603
604         plugin = strchr (hostname, '/');
605         if (plugin == NULL)
606                 return (-1);
607         *plugin = '\0'; plugin++;
608
609         type = strchr (plugin, '/');
610         if (type == NULL)
611                 return (-1);
612         *type = '\0'; type++;
613
614         plugin_instance = strchr (plugin, '-');
615         if (plugin_instance != NULL)
616         {
617                 *plugin_instance = '\0';
618                 plugin_instance++;
619         }
620
621         type_instance = strchr (type, '-');
622         if (type_instance != NULL)
623         {
624                 *type_instance = '\0';
625                 type_instance++;
626         }
627
628         *ret_host = hostname;
629         *ret_plugin = plugin;
630         *ret_plugin_instance = plugin_instance;
631         *ret_type = type;
632         *ret_type_instance = type_instance;
633         return (0);
634 } /* int parse_identifier */
635
636 int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
637 {
638         int i;
639         char *dummy;
640         char *ptr;
641         char *saveptr;
642
643         i = -1;
644         dummy = buffer;
645         saveptr = NULL;
646         while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
647         {
648                 dummy = NULL;
649
650                 if (i >= vl->values_len)
651                         break;
652
653                 if (i == -1)
654                 {
655                         if (strcmp ("N", ptr) == 0)
656                                 vl->time = time (NULL);
657                         else
658                                 vl->time = (time_t) atoi (ptr);
659                 }
660                 else
661                 {
662                         if (strcmp ("U", ptr) == 0)
663                                 vl->values[i].gauge = NAN;
664                         else if (ds->ds[i].type == DS_TYPE_COUNTER)
665                                 vl->values[i].counter = atoll (ptr);
666                         else if (ds->ds[i].type == DS_TYPE_GAUGE)
667                                 vl->values[i].gauge = atof (ptr);
668                 }
669
670                 i++;
671         } /* while (strtok_r) */
672
673         if ((ptr != NULL) || (i != vl->values_len))
674                 return (-1);
675         return (0);
676 } /* int parse_values */
677
678 #if !HAVE_GETPWNAM_R
679 int getpwnam_r (const char *name, struct passwd *pwbuf, char *buf,
680                 size_t buflen, struct passwd **pwbufp)
681 {
682         int status = 0;
683         struct passwd *pw;
684
685         memset (pwbuf, '\0', sizeof (struct passwd));
686
687         pthread_mutex_lock (&getpwnam_r_lock);
688
689         do
690         {
691                 pw = getpwnam (name);
692                 if (pw == NULL)
693                 {
694                         status = (errno != 0) ? errno : ENOENT;
695                         break;
696                 }
697
698 #define GETPWNAM_COPY_MEMBER(member) \
699                 if (pw->member != NULL) \
700                 { \
701                         int len = strlen (pw->member); \
702                         if (len >= buflen) \
703                         { \
704                                 status = ENOMEM; \
705                                 break; \
706                         } \
707                         sstrncpy (buf, pw->member, buflen); \
708                         pwbuf->member = buf; \
709                         buf    += (len + 1); \
710                         buflen -= (len + 1); \
711                 }
712                 GETPWNAM_COPY_MEMBER(pw_name);
713                 GETPWNAM_COPY_MEMBER(pw_passwd);
714                 GETPWNAM_COPY_MEMBER(pw_gecos);
715                 GETPWNAM_COPY_MEMBER(pw_dir);
716                 GETPWNAM_COPY_MEMBER(pw_shell);
717
718                 pwbuf->pw_uid = pw->pw_uid;
719                 pwbuf->pw_gid = pw->pw_gid;
720
721                 if (pwbufp != NULL)
722                         *pwbufp = pwbuf;
723         } while (0);
724
725         pthread_mutex_unlock (&getpwnam_r_lock);
726
727         return (status);
728 } /* int getpwnam_r */
729 #endif