Fix compile time issues
[collectd.git] / src / filecount.c
1 /**
2  * collectd - src/filecount.c
3  * Copyright (C) 2008  Alessandro Iurlano
4  * Copyright (C) 2008  Florian octo Forster
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Alessandro Iurlano <alessandro.iurlano at gmail.com>
21  *   Florian octo Forster <octo at collectd.org>
22  **/
23
24 #include "collectd.h"
25
26 #include "plugin.h"
27 #include "utils/common/common.h"
28
29 #include <dirent.h>
30 #include <fcntl.h>
31 #include <fnmatch.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #define FC_RECURSIVE 1
36 #define FC_HIDDEN 2
37 #define FC_REGULAR 4
38
39 struct fc_directory_conf_s {
40   char *path;
41   char *plugin_name;
42   char *instance;
43   char *files_size_type;
44   char *files_num_type;
45   char *type_instance;
46
47   int options;
48
49   /* Data counters */
50   uint64_t files_num;
51   uint64_t files_size;
52
53   /* Selectors */
54   char *name;
55   int64_t mtime;
56   int64_t size;
57
58   /* Helper for the recursive functions */
59   time_t now;
60 };
61 typedef struct fc_directory_conf_s fc_directory_conf_t;
62
63 static fc_directory_conf_t **directories;
64 static size_t directories_num;
65
66 static void fc_free_dir(fc_directory_conf_t *dir) {
67   sfree(dir->path);
68   sfree(dir->plugin_name);
69   sfree(dir->instance);
70   sfree(dir->files_size_type);
71   sfree(dir->files_num_type);
72   sfree(dir->type_instance);
73   sfree(dir->name);
74
75   sfree(dir);
76 } /* void fc_free_dir */
77
78 static void fc_submit_dir(const fc_directory_conf_t *dir) {
79   value_list_t vl = VALUE_LIST_INIT;
80
81   sstrncpy(vl.plugin, dir->plugin_name, sizeof(vl.plugin));
82   if (dir->instance != NULL)
83     sstrncpy(vl.plugin_instance, dir->instance, sizeof(vl.plugin_instance));
84   if (dir->type_instance != NULL)
85     sstrncpy(vl.type_instance, dir->type_instance, sizeof(vl.type_instance));
86
87   vl.values_len = 1;
88
89   if (dir->files_num_type != NULL) {
90     vl.values = &(value_t){.gauge = (gauge_t)dir->files_num};
91     sstrncpy(vl.type, dir->files_num_type, sizeof(vl.type));
92     plugin_dispatch_values(&vl);
93   }
94
95   if (dir->files_size_type != NULL) {
96     vl.values = &(value_t){.gauge = (gauge_t)dir->files_size};
97     sstrncpy(vl.type, dir->files_size_type, sizeof(vl.type));
98     plugin_dispatch_values(&vl);
99   }
100 } /* void fc_submit_dir */
101
102 /*
103  * Config:
104  * <Plugin filecount>
105  *   <Directory /path/to/dir>
106  *     Plugin "foo"
107  *     Instance "foobar"
108  *     Name "*.conf"
109  *     MTime -3600
110  *     Size "+10M"
111  *     Recursive true
112  *     IncludeHidden false
113  *     FilesSizeType "bytes"
114  *     FilesCountType "files"
115  *     TypeInstance "instance"
116  *   </Directory>
117  * </Plugin>
118  *
119  * Collect:
120  * - Number of files
121  * - Total size
122  */
123
124 static int fc_config_set_instance(fc_directory_conf_t *dir, const char *str) {
125   char buffer[1024];
126   char *ptr;
127
128   sstrncpy(buffer, str, sizeof(buffer));
129   for (ptr = buffer; *ptr != 0; ptr++)
130     if (*ptr == '/')
131       *ptr = '_';
132
133   for (ptr = buffer; *ptr == '_'; ptr++)
134     /* do nothing */;
135
136   char *copy = strdup(ptr);
137   if (copy == NULL)
138     return -1;
139
140   sfree(dir->instance);
141   dir->instance = copy;
142
143   return 0;
144 } /* int fc_config_set_instance */
145
146 static int fc_config_add_dir_instance(fc_directory_conf_t *dir,
147                                       oconfig_item_t *ci) {
148   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
149     WARNING("filecount plugin: The `Instance' config option needs exactly "
150             "one string argument.");
151     return -1;
152   }
153
154   return fc_config_set_instance(dir, ci->values[0].value.string);
155 } /* int fc_config_add_dir_instance */
156
157 static int fc_config_add_dir_mtime(fc_directory_conf_t *dir,
158                                    oconfig_item_t *ci) {
159   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
160                                 (ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
161     WARNING("filecount plugin: The `MTime' config option needs exactly one "
162             "string or numeric argument.");
163     return -1;
164   }
165
166   if (ci->values[0].type == OCONFIG_TYPE_NUMBER) {
167     dir->mtime = (int64_t)ci->values[0].value.number;
168     return 0;
169   }
170
171   errno = 0;
172   char *endptr = NULL;
173   double temp = strtod(ci->values[0].value.string, &endptr);
174   if ((errno != 0) || (endptr == NULL) ||
175       (endptr == ci->values[0].value.string)) {
176     WARNING("filecount plugin: Converting `%s' to a number failed.",
177             ci->values[0].value.string);
178     return -1;
179   }
180
181   switch (*endptr) {
182   case 0:
183   case 's':
184   case 'S':
185     break;
186
187   case 'm':
188   case 'M':
189     temp *= 60;
190     break;
191
192   case 'h':
193   case 'H':
194     temp *= 3600;
195     break;
196
197   case 'd':
198   case 'D':
199     temp *= 86400;
200     break;
201
202   case 'w':
203   case 'W':
204     temp *= 7 * 86400;
205     break;
206
207   case 'y':
208   case 'Y':
209     temp *= 31557600; /* == 365.25 * 86400 */
210     break;
211
212   default:
213     WARNING("filecount plugin: Invalid suffix for `MTime': `%c'", *endptr);
214     return -1;
215   } /* switch (*endptr) */
216
217   dir->mtime = (int64_t)temp;
218
219   return 0;
220 } /* int fc_config_add_dir_mtime */
221
222 static int fc_config_add_dir_size(fc_directory_conf_t *dir,
223                                   oconfig_item_t *ci) {
224   if ((ci->values_num != 1) || ((ci->values[0].type != OCONFIG_TYPE_STRING) &&
225                                 (ci->values[0].type != OCONFIG_TYPE_NUMBER))) {
226     WARNING("filecount plugin: The `Size' config option needs exactly one "
227             "string or numeric argument.");
228     return -1;
229   }
230
231   if (ci->values[0].type == OCONFIG_TYPE_NUMBER) {
232     dir->size = (int64_t)ci->values[0].value.number;
233     return 0;
234   }
235
236   errno = 0;
237   char *endptr = NULL;
238   double temp = strtod(ci->values[0].value.string, &endptr);
239   if ((errno != 0) || (endptr == NULL) ||
240       (endptr == ci->values[0].value.string)) {
241     WARNING("filecount plugin: Converting `%s' to a number failed.",
242             ci->values[0].value.string);
243     return -1;
244   }
245
246   switch (*endptr) {
247   case 0:
248   case 'b':
249   case 'B':
250     break;
251
252   case 'k':
253   case 'K':
254     temp *= 1000.0;
255     break;
256
257   case 'm':
258   case 'M':
259     temp *= 1000.0 * 1000.0;
260     break;
261
262   case 'g':
263   case 'G':
264     temp *= 1000.0 * 1000.0 * 1000.0;
265     break;
266
267   case 't':
268   case 'T':
269     temp *= 1000.0 * 1000.0 * 1000.0 * 1000.0;
270     break;
271
272   case 'p':
273   case 'P':
274     temp *= 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0;
275     break;
276
277   default:
278     WARNING("filecount plugin: Invalid suffix for `Size': `%c'", *endptr);
279     return -1;
280   } /* switch (*endptr) */
281
282   dir->size = (int64_t)temp;
283
284   return 0;
285 } /* int fc_config_add_dir_size */
286
287 static int fc_config_add_dir_option(fc_directory_conf_t *dir,
288                                     oconfig_item_t *ci, int bit) {
289   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) {
290     WARNING("filecount plugin: The `Recursive' config options needs exactly "
291             "one boolean argument.");
292     return -1;
293   }
294
295   if (ci->values[0].value.boolean)
296     dir->options |= bit;
297   else
298     dir->options &= ~bit;
299
300   return 0;
301 } /* int fc_config_add_dir_option */
302
303 static int fc_config_add_dir(oconfig_item_t *ci) {
304   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
305     WARNING("filecount plugin: `Directory' needs exactly one string "
306             "argument.");
307     return -1;
308   }
309
310   /* Initialize `dir' */
311   fc_directory_conf_t *dir = calloc(1, sizeof(*dir));
312   if (dir == NULL) {
313     ERROR("filecount plugin: calloc failed.");
314     return -1;
315   }
316
317   dir->path = strdup(ci->values[0].value.string);
318   if (dir->path == NULL) {
319     ERROR("filecount plugin: strdup failed.");
320     fc_free_dir(dir);
321     return -1;
322   }
323
324   dir->options = FC_RECURSIVE | FC_REGULAR;
325
326   dir->name = NULL;
327   dir->plugin_name = strdup("filecount");
328   dir->instance = NULL;
329   dir->type_instance = NULL;
330   dir->mtime = 0;
331   dir->size = 0;
332
333   dir->files_size_type = strdup("bytes");
334   dir->files_num_type = strdup("files");
335
336   if (dir->plugin_name == NULL || dir->files_size_type == NULL ||
337       dir->files_num_type == NULL) {
338     ERROR("filecount plugin: strdup failed.");
339     fc_free_dir(dir);
340     return -1;
341   }
342
343   int status = 0;
344   for (int i = 0; i < ci->children_num; i++) {
345     oconfig_item_t *option = ci->children + i;
346
347     if (strcasecmp("Plugin", option->key) == 0)
348       status = cf_util_get_string(option, &dir->plugin_name);
349     else if (strcasecmp("Instance", option->key) == 0)
350       status = fc_config_add_dir_instance(dir, option);
351     else if (strcasecmp("Name", option->key) == 0)
352       status = cf_util_get_string(option, &dir->name);
353     else if (strcasecmp("MTime", option->key) == 0)
354       status = fc_config_add_dir_mtime(dir, option);
355     else if (strcasecmp("Size", option->key) == 0)
356       status = fc_config_add_dir_size(dir, option);
357     else if (strcasecmp("Recursive", option->key) == 0)
358       status = fc_config_add_dir_option(dir, option, FC_RECURSIVE);
359     else if (strcasecmp("IncludeHidden", option->key) == 0)
360       status = fc_config_add_dir_option(dir, option, FC_HIDDEN);
361     else if (strcasecmp("RegularOnly", option->key) == 0)
362       status = fc_config_add_dir_option(dir, option, FC_REGULAR);
363     else if (strcasecmp("FilesSizeType", option->key) == 0)
364       status = cf_util_get_string(option, &dir->files_size_type);
365     else if (strcasecmp("FilesCountType", option->key) == 0)
366       status = cf_util_get_string(option, &dir->files_num_type);
367     else if (strcasecmp("TypeInstance", option->key) == 0)
368       status = cf_util_get_string(option, &dir->type_instance);
369     else {
370       WARNING("filecount plugin: fc_config_add_dir: "
371               "Option `%s' not allowed here.",
372               option->key);
373       status = -1;
374     }
375
376     if (status != 0)
377       break;
378   } /* for (ci->children) */
379
380   if (status != 0) {
381     fc_free_dir(dir);
382     return -1;
383   }
384
385   /* Set default plugin instance */
386   if (dir->instance == NULL) {
387     fc_config_set_instance(dir, dir->path);
388     if (dir->instance == NULL || strlen(dir->instance) == 0) {
389       ERROR("filecount plugin: failed to build plugin instance name.");
390       fc_free_dir(dir);
391       return -1;
392     }
393   }
394
395   /* Handle disabled types */
396   if (strlen(dir->instance) == 0)
397     sfree(dir->instance);
398
399   if (strlen(dir->files_size_type) == 0)
400     sfree(dir->files_size_type);
401
402   if (strlen(dir->files_num_type) == 0)
403     sfree(dir->files_num_type);
404
405   if (dir->files_size_type == NULL && dir->files_num_type == NULL) {
406     WARNING("filecount plugin: Both `FilesSizeType' and `FilesCountType ' "
407             "are disabled for '%s'. There's no types to report.",
408             dir->path);
409     fc_free_dir(dir);
410     return -1;
411   }
412
413   /* Ready to add it to list */
414   fc_directory_conf_t **temp =
415       realloc(directories, sizeof(*directories) * (directories_num + 1));
416   if (temp == NULL) {
417     ERROR("filecount plugin: realloc failed.");
418     fc_free_dir(dir);
419     return -1;
420   }
421
422   directories = temp;
423   directories[directories_num] = dir;
424   directories_num++;
425
426   return 0;
427 } /* int fc_config_add_dir */
428
429 static int fc_config(oconfig_item_t *ci) {
430   for (int i = 0; i < ci->children_num; i++) {
431     oconfig_item_t *child = ci->children + i;
432     if (strcasecmp("Directory", child->key) == 0)
433       fc_config_add_dir(child);
434     else {
435       WARNING("filecount plugin: Ignoring unknown config option `%s'.",
436               child->key);
437     }
438   } /* for (ci->children) */
439
440   return 0;
441 } /* int fc_config */
442
443 static int fc_init(void) {
444   if (directories_num < 1) {
445     WARNING("filecount plugin: No directories have been configured.");
446     return -1;
447   }
448
449   return 0;
450 } /* int fc_init */
451
452 static int fc_read_dir_callback(const char *dirname, const char *filename,
453                                 void *user_data) {
454   fc_directory_conf_t *dir = user_data;
455   char abs_path[PATH_MAX];
456   struct stat statbuf;
457
458   if (dir == NULL)
459     return -1;
460
461   snprintf(abs_path, sizeof(abs_path), "%s/%s", dirname, filename);
462
463   int status = lstat(abs_path, &statbuf);
464   if (status != 0) {
465     ERROR("filecount plugin: stat (%s) failed.", abs_path);
466     return -1;
467   }
468
469   if (S_ISDIR(statbuf.st_mode) && (dir->options & FC_RECURSIVE)) {
470     status = walk_directory(
471         abs_path, fc_read_dir_callback, dir,
472         /* include hidden = */ (dir->options & FC_HIDDEN) ? 1 : 0);
473     return status;
474   } else if ((dir->options & FC_REGULAR) && !S_ISREG(statbuf.st_mode)) {
475     return 0;
476   }
477
478   if (dir->name != NULL) {
479     status = fnmatch(dir->name, filename, /* flags = */ 0);
480     if (status != 0)
481       return 0;
482   }
483
484   if (!S_ISREG(statbuf.st_mode)) {
485     dir->files_num++;
486     return 0;
487   }
488
489   if (dir->mtime != 0) {
490     time_t mtime = dir->now;
491
492     if (dir->mtime < 0)
493       mtime += dir->mtime;
494     else
495       mtime -= dir->mtime;
496
497     DEBUG("filecount plugin: Only collecting files that were touched %s %u.",
498           (dir->mtime < 0) ? "after" : "before", (unsigned int)mtime);
499
500     if (((dir->mtime < 0) && (statbuf.st_mtime < mtime)) ||
501         ((dir->mtime > 0) && (statbuf.st_mtime > mtime)))
502       return 0;
503   }
504
505   if (dir->size != 0) {
506     off_t size;
507
508     if (dir->size < 0)
509       size = (off_t)((-1) * dir->size);
510     else
511       size = (off_t)dir->size;
512
513     if (((dir->size < 0) && (statbuf.st_size > size)) ||
514         ((dir->size > 0) && (statbuf.st_size < size)))
515       return 0;
516   }
517
518   dir->files_num++;
519   dir->files_size += (uint64_t)statbuf.st_size;
520
521   return 0;
522 } /* int fc_read_dir_callback */
523
524 static int fc_read_dir(fc_directory_conf_t *dir) {
525   dir->files_num = 0;
526   dir->files_size = 0;
527
528   if (dir->mtime != 0)
529     dir->now = time(NULL);
530
531   int status =
532       walk_directory(dir->path, fc_read_dir_callback, dir,
533                      /* include hidden */ (dir->options & FC_HIDDEN) ? 1 : 0);
534   if (status != 0) {
535     WARNING("filecount plugin: walk_directory (%s) failed.", dir->path);
536     return -1;
537   }
538
539   fc_submit_dir(dir);
540
541   return 0;
542 } /* int fc_read_dir */
543
544 static int fc_read(void) {
545   for (size_t i = 0; i < directories_num; i++)
546     fc_read_dir(directories[i]);
547
548   return 0;
549 } /* int fc_read */
550
551 void module_register(void) {
552   plugin_register_complex_config("filecount", fc_config);
553   plugin_register_init("filecount", fc_init);
554   plugin_register_read("filecount", fc_read);
555 } /* void module_register */