filesystem.[ch]: Move filesystem accessing functions into a separate module.
[collection4.git] / filesystem.c
1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <errno.h>
6 #include <limits.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <dirent.h>
10
11 #define DATA_DIR "/var/lib/collectd/rrd"
12
13 #include "filesystem.h"
14
15 struct fs_scan_dir_data_s /* {{{ */
16 {
17   fs_ident_cb_t callback;
18   void *user_data;
19
20   char *host;
21   char *plugin;
22   char *plugin_instance;
23   char *type;
24   char *type_instance;
25 }; /* }}} */
26 typedef struct fs_scan_dir_data_s fs_scan_dir_data_t;
27
28 typedef int (*callback_type_t)   (const char *type,   void *user_data);
29 typedef int (*callback_plugin_t) (const char *plugin, void *user_data);
30 typedef int (*callback_host_t)   (const char *host,   void *user_data);
31
32 /*
33  * Directory and file walking functions
34  */
35 static int foreach_rrd_file (const char *dir, /* {{{ */
36     int (*callback) (const char *, void *),
37     void *user_data)
38 {
39   DIR *dh;
40   struct dirent *entry;
41   int status;
42
43   if (callback == NULL)
44     return (EINVAL);
45
46   dh = opendir (dir);
47   if (dh == NULL)
48     return (errno);
49
50   while ((entry = readdir (dh)) != NULL)
51   {
52     struct stat statbuf;
53     char abspath[PATH_MAX + 1];
54     size_t d_name_len;
55
56     if (entry->d_name[0] == '.')
57       continue;
58
59     d_name_len = strlen (entry->d_name);
60     if (d_name_len <= 4)
61       continue;
62
63     if (strcasecmp (".rrd", entry->d_name + (d_name_len - 4)) != 0)
64       continue;
65
66     snprintf (abspath, sizeof (abspath), "%s/%s", dir, entry->d_name);
67     abspath[sizeof (abspath) - 1] = 0;
68
69     memset (&statbuf, 0, sizeof (statbuf));
70
71     status = stat (abspath, &statbuf);
72     if (status != 0)
73       continue;
74
75     if (!S_ISREG (statbuf.st_mode))
76       continue;
77
78     entry->d_name[d_name_len - 4] = 0;
79
80     status = (*callback) (entry->d_name, user_data);
81     if (status != 0)
82       break;
83   } /* while (readdir) */
84
85   closedir (dh);
86   return (status);
87 } /* }}} int foreach_rrd_file */
88
89 static int foreach_dir (const char *dir, /* {{{ */
90     int (*callback) (const char *, void *),
91     void *user_data)
92 {
93   DIR *dh;
94   struct dirent *entry;
95   int status;
96
97   if (callback == NULL)
98     return (EINVAL);
99
100   dh = opendir (dir);
101   if (dh == NULL)
102     return (errno);
103
104   while ((entry = readdir (dh)) != NULL)
105   {
106     struct stat statbuf;
107     char abspath[PATH_MAX + 1];
108
109     if (entry->d_name[0] == '.')
110       continue;
111
112     snprintf (abspath, sizeof (abspath), "%s/%s", dir, entry->d_name);
113     abspath[sizeof (abspath) - 1] = 0;
114
115     memset (&statbuf, 0, sizeof (statbuf));
116
117     status = stat (abspath, &statbuf);
118     if (status != 0)
119       continue;
120
121     if (!S_ISDIR (statbuf.st_mode))
122       continue;
123
124     status = (*callback) (entry->d_name, user_data);
125     if (status != 0)
126       break;
127   } /* while (readdir) */
128
129   closedir (dh);
130   return (status);
131 } /* }}} int foreach_dir */
132
133 static int foreach_type (const char *host, const char *plugin, /* {{{ */
134     callback_type_t callback, void *user_data)
135 {
136   char abspath[PATH_MAX + 1];
137
138   if ((host == NULL) || (plugin == NULL))
139     return (EINVAL);
140
141   snprintf (abspath, sizeof (abspath), "%s/%s/%s", DATA_DIR, host, plugin);
142   abspath[sizeof (abspath) - 1] = 0;
143
144   return (foreach_rrd_file (abspath, callback, user_data));
145 } /* }}} int foreach_type */
146
147 static int foreach_plugin (const char *host, /* {{{ */
148     callback_plugin_t callback,
149     void *user_data)
150 {
151   char abspath[PATH_MAX + 1];
152
153   if (host == NULL)
154     return (EINVAL);
155
156   snprintf (abspath, sizeof (abspath), "%s/%s", DATA_DIR, host);
157   abspath[sizeof (abspath) - 1] = 0;
158
159   return (foreach_dir (abspath, callback, user_data));
160 } /* }}} int foreach_plugin */
161
162 static int foreach_host (callback_host_t callback, /* {{{ */
163     void *user_data)
164 {
165   return (foreach_dir (DATA_DIR, callback, user_data));
166 } /* }}} int foreach_host */
167
168 /*
169  * Functions building "fs_scan_dir_data_t" and calling the user-supplied
170  * callback eventually.
171  */
172 static int scan_type (const char *type, void *user_data) /* {{{ */
173 {
174   fs_scan_dir_data_t *data = user_data;
175   graph_ident_t *ident;
176   int status;
177
178   if ((type == NULL) || (data == NULL))
179     return (EINVAL);
180
181   if ((data->type != NULL) || (data->type_instance != NULL))
182     return (EINVAL);
183
184   data->type = strdup (type);
185   if (data->type == NULL)
186     return (ENOMEM);
187
188   data->type_instance = strchr (data->type, '-');
189   if (data->type_instance != NULL)
190   {
191     *data->type_instance = 0;
192     data->type_instance++;
193   }
194   else
195   {
196     data->type_instance = data->type + strlen (data->type);
197   }
198
199   ident = ident_create (data->host,
200       data->plugin, data->plugin_instance,
201       data->type, data->type_instance);
202   if (ident == NULL)
203   {
204     status = -1;
205   }
206   else
207   {
208     status = (*data->callback) (ident, data->user_data);
209     ident_destroy (ident);
210   }
211
212   free (data->type);
213   data->type = NULL;
214   data->type_instance = NULL;
215
216   return (status);
217 } /* }}} int scan_type */
218
219 static int scan_plugin (const char *plugin, void *user_data) /* {{{ */
220 {
221   fs_scan_dir_data_t *data = user_data;
222   int status;
223
224   if ((plugin == NULL) || (data == NULL))
225     return (EINVAL);
226
227   if ((data->plugin != NULL) || (data->plugin_instance != NULL))
228     return (EINVAL);
229
230   data->plugin = strdup (plugin);
231   if (data->plugin == NULL)
232     return (ENOMEM);
233
234   data->plugin_instance = strchr (data->plugin, '-');
235   if (data->plugin_instance != NULL)
236   {
237     *data->plugin_instance = 0;
238     data->plugin_instance++;
239   }
240   else
241   {
242     data->plugin_instance = data->plugin + strlen (data->plugin);
243   }
244
245   status = foreach_type (data->host, plugin, scan_type, data);
246
247   free (data->plugin);
248   data->plugin = NULL;
249   data->plugin_instance = NULL;
250
251   return (status);
252 } /* }}} int scan_plugin */
253
254 static int scan_host (const char *host, void *user_data) /* {{{ */
255 {
256   fs_scan_dir_data_t *data = user_data;
257   int status;
258
259   if ((host == NULL) || (data == NULL))
260     return (EINVAL);
261
262   if (data->host != NULL)
263     return (EINVAL);
264
265   data->host = strdup (host);
266   if (data->host == NULL)
267     return (ENOMEM);
268
269   status =  foreach_plugin (host, scan_plugin, data);
270
271   free (data->host);
272   data->host = NULL;
273
274   return (status);
275 } /* }}} int scan_host */
276
277 /*
278  * Public function
279  */
280 int fs_scan (fs_ident_cb_t callback, void *user_data) /* {{{ */
281 {
282   fs_scan_dir_data_t data;
283
284   memset (&data, 0, sizeof (data));
285   data.callback = callback;
286   data.user_data = user_data;
287
288   data.host = NULL;
289   data.plugin = NULL;
290   data.plugin_instance = NULL;
291   data.type = NULL;
292   data.type_instance = NULL;
293
294   foreach_host (scan_host, &data);
295
296   return (0);
297 } /* }}} int fs_scan */
298
299 /* vim: set sw=2 sts=2 et fdm=marker : */