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