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