nfs plugin: Ported to the new plugin structure.
[collectd.git] / src / common.c
1 /**
2  * collectd - src/common.c
3  * Copyright (C) 2005,2006  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/
6  * or modify it under the terms of the GNU General Public Li-
7  * cence as published by the Free Software Foundation; either
8  * version 2 of the Licence, or any later version.
9  *
10  * This program is distributed in the hope that it will be use-
11  * ful, but WITHOUT ANY WARRANTY; without even the implied war-
12  * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public Licence for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * Licence along with this program; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
18  * USA.
19  *
20  * Authors:
21  *   Florian octo Forster <octo at verplant.org>
22  *   Niki W. Waibel <niki.waibel@gmx.net>
23 **/
24
25 #include "common.h"
26 #include "utils_debug.h"
27
28 #ifdef HAVE_MATH_H
29 #  include <math.h>
30 #endif
31
32 /* for ntohl and htonl */
33 #if HAVE_ARPA_INET_H
34 # include <arpa/inet.h>
35 #endif
36
37 extern int operating_mode;
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                 DBG ("Not enough memory.");
59                 exit(3);
60         }
61
62         return (r);
63 }
64
65 void *smalloc (size_t size)
66 {
67         void *r;
68
69         if ((r = malloc (size)) == NULL)
70         {
71                 DBG("Not enough memory.");
72                 exit(3);
73         }
74
75         return r;
76 }
77
78 #if 0
79 void sfree (void **ptr)
80 {
81         if (ptr == NULL)
82                 return;
83
84         if (*ptr != NULL)
85                 free (*ptr);
86
87         *ptr = NULL;
88 }
89 #endif
90
91 ssize_t sread (int fd, void *buf, size_t count)
92 {
93         char    *ptr;
94         size_t   nleft;
95         ssize_t  status;
96
97         ptr   = (char *) buf;
98         nleft = count;
99
100         while (nleft > 0)
101         {
102                 status = read (fd, (void *) ptr, nleft);
103
104                 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
105                         continue;
106
107                 if (status < 0)
108                         return (status);
109
110                 if (status == 0)
111                 {
112                         DBG ("Received EOF from fd %i. "
113                                         "Closing fd and returning error.",
114                                         fd);
115                         close (fd);
116                         return (-1);
117                 }
118
119                 assert (nleft >= status);
120
121                 nleft = nleft - status;
122                 ptr   = ptr   + status;
123         }
124
125         return (0);
126 }
127
128
129 ssize_t swrite (int fd, const void *buf, size_t count)
130 {
131         const char *ptr;
132         size_t      nleft;
133         ssize_t     status;
134
135         ptr   = (const char *) buf;
136         nleft = count;
137
138         while (nleft > 0)
139         {
140                 status = write (fd, (const void *) ptr, nleft);
141
142                 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
143                         continue;
144
145                 if (status < 0)
146                         return (status);
147
148                 nleft = nleft - status;
149                 ptr   = ptr   + status;
150         }
151
152         return (0);
153 }
154
155 int strsplit (char *string, char **fields, size_t size)
156 {
157         size_t i;
158         char *ptr;
159
160         i = 0;
161         ptr = string;
162         while ((fields[i] = strtok (ptr, " \t")) != NULL)
163         {
164                 ptr = NULL;
165                 i++;
166
167                 if (i >= size)
168                         break;
169         }
170
171         return (i);
172 }
173
174 int strjoin (char *dst, size_t dst_len,
175                 char **fields, size_t fields_num,
176                 const char *sep)
177 {
178         int field_len;
179         int sep_len;
180         int i;
181
182         memset (dst, '\0', dst_len);
183
184         if (fields_num <= 0)
185                 return (-1);
186
187         sep_len = 0;
188         if (sep != NULL)
189                 sep_len = strlen (sep);
190
191         for (i = 0; i < fields_num; i++)
192         {
193                 if ((i > 0) && (sep_len > 0))
194                 {
195                         if (dst_len <= sep_len)
196                                 return (-1);
197
198                         strncat (dst, sep, dst_len);
199                         dst_len -= sep_len;
200                 }
201
202                 field_len = strlen (fields[i]);
203
204                 if (dst_len <= field_len)
205                         return (-1);
206
207                 strncat (dst, fields[i], dst_len);
208                 dst_len -= field_len;
209         }
210
211         return (strlen (dst));
212 }
213
214 int strsubstitute (char *str, char c_from, char c_to)
215 {
216         int ret;
217
218         if (str == NULL)
219                 return (-1);
220
221         ret = 0;
222         while (*str != '\0')
223         {
224                 if (*str == c_from)
225                 {
226                         *str = c_to;
227                         ret++;
228                 }
229                 str++;
230         }
231
232         return (ret);
233 }
234
235 int escape_slashes (char *buf, int buf_len)
236 {
237         int i;
238
239         if (strcmp (buf, "/") == 0)
240         {
241                 if (buf_len < 5)
242                         return (-1);
243
244                 strncpy (buf, "root", buf_len);
245                 return (0);
246         }
247
248         /* Move one to the left */
249         memmove (buf, buf + 1, buf_len - 1);
250
251         for (i = 0; i < buf_len - 1; i++)
252         {
253                 if (buf[i] == '\0')
254                         break;
255                 else if (buf[i] == '/')
256                         buf[i] = '_';
257         }
258         buf[i] = '\0';
259
260         return (0);
261 }
262
263 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
264 {
265         if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
266                 return (-2);
267
268         if ((tv0->tv_sec < tv1->tv_sec)
269                         || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
270                 return (-1);
271
272         ret->tv_sec  = tv0->tv_sec - tv1->tv_sec;
273         ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
274
275         if (ret->tv_nsec < 0)
276         {
277                 assert (ret->tv_sec > 0);
278
279                 ret->tv_nsec += 1000000000;
280                 ret->tv_sec  -= 1;
281         }
282
283         return (0);
284 }
285
286 int check_create_dir (const char *file_orig)
287 {
288         struct stat statbuf;
289
290         char  file_copy[512];
291         char  dir[512];
292         int   dir_len = 512;
293         char *fields[16];
294         int   fields_num;
295         char *ptr;
296         int   last_is_file = 1;
297         int   len;
298         int   i;
299
300         /*
301          * Sanity checks first
302          */
303         if (file_orig == NULL)
304                 return (-1);
305
306         if ((len = strlen (file_orig)) < 1)
307                 return (-1);
308         else if (len >= 512)
309                 return (-1);
310
311         /*
312          * If `file_orig' ends in a slash the last component is a directory,
313          * otherwise it's a file. Act accordingly..
314          */
315         if (file_orig[len - 1] == '/')
316                 last_is_file = 0;
317
318         /*
319          * Create a copy for `strtok' to destroy
320          */
321         strncpy (file_copy, file_orig, 512);
322         file_copy[511] = '\0';
323
324         /*
325          * Break into components. This will eat up several slashes in a row and
326          * remove leading and trailing slashes..
327          */
328         ptr = file_copy;
329         fields_num = 0;
330         while ((fields[fields_num] = strtok (ptr, "/")) != NULL)
331         {
332                 ptr = NULL;
333                 fields_num++;
334
335                 if (fields_num >= 16)
336                         break;
337         }
338
339         /*
340          * For each component, do..
341          */
342         for (i = 0; i < (fields_num - last_is_file); i++)
343         {
344                 /*
345                  * Do not create directories that start with a dot. This
346                  * prevents `../../' attacks and other likely malicious
347                  * behavior.
348                  */
349                 if (fields[i][0] == '.')
350                 {
351                         syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
352                         return (-2);
353                 }
354
355                 /*
356                  * Join the components together again
357                  */
358                 if (strjoin (dir, dir_len, fields, i + 1, "/") < 0)
359                 {
360                         syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i);
361                         return (-1);
362                 }
363
364                 if (stat (dir, &statbuf) == -1)
365                 {
366                         if (errno == ENOENT)
367                         {
368                                 if (mkdir (dir, 0755) == -1)
369                                 {
370                                         syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
371                                         return (-1);
372                                 }
373                         }
374                         else
375                         {
376                                 syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno));
377                                 return (-1);
378                         }
379                 }
380                 else if (!S_ISDIR (statbuf.st_mode))
381                 {
382                         syslog (LOG_ERR, "stat (%s): Not a directory!", dir);
383                         return (-1);
384                 }
385         }
386
387         return (0);
388 }
389
390 static int log_create_file (char *filename, char **ds_def, int ds_num)
391 {
392         FILE *log;
393         int i;
394
395         if (check_create_dir (filename))
396                 return (-1);
397
398         log = fopen (filename, "w");
399         if (log == NULL)
400         {
401                 syslog (LOG_WARNING, "Failed to create %s: %s", filename,
402                                 strerror(errno));
403                 return (-1);
404         }
405
406         fprintf (log, "epoch");
407         for (i = 0; i < ds_num; i++)
408         {
409                 char *name;
410                 char *tmp;
411
412                 name = strchr (ds_def[i], ':');
413                 if (name == NULL)
414                 {
415                         syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
416                                         ds_def[i], filename);
417                         fclose(log);
418                         remove(filename);
419                         return (-1);
420                 }
421
422                 name += 1;
423                 tmp = strchr (name, ':');
424                 if (tmp == NULL)
425                 {
426                         syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
427                                         ds_def[i], filename);
428                         fclose(log);
429                         remove(filename);
430                         return (-1);
431                 }
432
433                 /* The `%.*s' is needed because there is no null-byte behind
434                  * the name. */
435                 fprintf(log, ",%.*s", (int) (tmp - name), name);
436         }
437         fprintf(log, "\n");
438         fclose(log);
439
440         return 0;
441 }
442
443 int log_update_file (char *host, char *file, char *values,
444                 char **ds_def, int ds_num)
445 {
446         char *tmp;
447         FILE *fp;
448         struct stat statbuf;
449         char full_file[1024];
450
451         /* Cook the values a bit: Substitute colons with commas */
452         strsubstitute (values, ':', ',');
453
454         /* host == NULL => local mode */
455         if (host != NULL)
456         {
457                 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
458                         return (-1);
459         }
460         else
461         {
462                 if (snprintf (full_file, 1024, "%s", file) >= 1024)
463                         return (-1);
464         }
465
466         strncpy (full_file, file, 1024);
467
468         tmp = full_file + strlen (full_file) - 4;
469         assert ((tmp != NULL) && (tmp > full_file));
470
471         /* Change the filename for logfiles. */
472         if (strncmp (tmp, ".rrd", 4) == 0)
473         {
474                 time_t now;
475                 struct tm *tm;
476
477                 /* TODO: Find a way to minimize the calls to `localtime', since
478                  * they are pretty expensive.. */
479                 now = time (NULL);
480                 tm = localtime (&now);
481
482                 strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
483
484                 /* `localtime(3)' returns a pointer to static data,
485                  * therefore the pointer may not be free'd. */
486         }
487         else
488                 DBG ("The filename ends with `%s' which is unexpected.", tmp);
489
490         if (stat (full_file, &statbuf) == -1)
491         {
492                 if (errno == ENOENT)
493                 {
494                         if (log_create_file (full_file, ds_def, ds_num))
495                                 return (-1);
496                 }
497                 else
498                 {
499                         syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
500                         return (-1);
501                 }
502         }
503         else if (!S_ISREG (statbuf.st_mode))
504         {
505                 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
506                 return (-1);
507         }
508
509
510         fp = fopen (full_file, "a");
511         if (fp == NULL)
512         {
513                 syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
514                                 strerror(errno));
515                 return (-1);
516         }
517         fprintf(fp, "%s\n", values);
518         fclose(fp);
519
520         return (0);
521 } /* int log_update_file */
522
523
524 #ifdef HAVE_LIBKSTAT
525 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
526 {
527         char ident[128];
528         
529         if (kc == NULL)
530                 return (-1);
531
532         snprintf (ident, 128, "%s,%i,%s", module, instance, name);
533         ident[127] = '\0';
534
535         if (*ksp_ptr == NULL)
536         {
537                 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
538                 {
539                         syslog (LOG_ERR, "Cound not find kstat %s", ident);
540                         return (-1);
541                 }
542
543                 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
544                 {
545                         syslog (LOG_WARNING, "kstat %s has wrong type", ident);
546                         *ksp_ptr = NULL;
547                         return (-1);
548                 }
549         }
550
551 #ifdef assert
552         assert (*ksp_ptr != NULL);
553         assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
554 #endif
555
556         if (kstat_read (kc, *ksp_ptr, NULL) == -1)
557         {
558                 syslog (LOG_WARNING, "kstat %s could not be read", ident);
559                 return (-1);
560         }
561
562         if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
563         {
564                 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
565                 return (-1);
566         }
567
568         return (0);
569 }
570
571 long long get_kstat_value (kstat_t *ksp, char *name)
572 {
573         kstat_named_t *kn;
574         long long retval = -1LL;
575
576 #ifdef assert
577         assert (ksp != NULL);
578         assert (ksp->ks_type == KSTAT_TYPE_NAMED);
579 #else
580         if (ksp == NULL)
581         {
582                 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
583                 return (-1LL);
584         }
585         else if (ksp->ks_type != KSTAT_TYPE_NAMED)
586         {
587                 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
588                 return (-1LL);
589         }
590 #endif
591
592         if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
593                 return (retval);
594
595         if (kn->data_type == KSTAT_DATA_INT32)
596                 retval = (long long) kn->value.i32;
597         else if (kn->data_type == KSTAT_DATA_UINT32)
598                 retval = (long long) kn->value.ui32;
599         else if (kn->data_type == KSTAT_DATA_INT64)
600                 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
601         else if (kn->data_type == KSTAT_DATA_UINT64)
602                 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
603         else
604                 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
605                  
606         return (retval);
607 }
608 #endif /* HAVE_LIBKSTAT */
609
610 unsigned long long ntohll (unsigned long long n)
611 {
612 #if __BYTE_ORDER == __BIG_ENDIAN
613         return (n);
614 #else
615         return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
616 #endif
617 }
618
619 unsigned long long htonll (unsigned long long n)
620 {
621 #if __BYTE_ORDER == __BIG_ENDIAN
622         return (n);
623 #else
624         return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
625 #endif
626 }