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