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