2cf59684bd9c910979dd8837656c23011c9e8c2b
[collectd.git] / src / plugin.c
1 /**
2  * collectd - src/plugin.c
3  * Copyright (C) 2005,2006  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23
24 #include <ltdl.h>
25
26 #include "plugin.h"
27 #include "configfile.h"
28 #include "utils_llist.h"
29 #include "utils_debug.h"
30
31 /*
32  * Private variables
33  */
34 static llist_t *list_init;
35 static llist_t *list_read;
36 static llist_t *list_write;
37 static llist_t *list_shutdown;
38 static llist_t *list_data_set;
39
40 static char *plugindir = NULL;
41
42 /*
43  * Static functions
44  */
45 static const char *plugin_get_dir (void)
46 {
47         if (plugindir == NULL)
48                 return (PLUGINDIR);
49         else
50                 return (plugindir);
51 }
52
53 static int register_callback (llist_t **list, const char *name, void *callback)
54 {
55         llentry_t *le;
56
57         if ((*list == NULL)
58                         && ((*list = llist_create ()) == NULL))
59                 return (-1);
60
61         le = llist_search (*list, name);
62         if (le == NULL)
63         {
64                 le = llentry_create (name, callback);
65                 if (le == NULL)
66                         return (-1);
67
68                 llist_append (*list, le);
69         }
70         else
71         {
72                 le->value = callback;
73         }
74
75         return (0);
76 } /* int register_callback */
77
78 /*
79  * (Try to) load the shared object `file'. Won't complain if it isn't a shared
80  * object, but it will bitch about a shared object not having a
81  * ``module_register'' symbol..
82  */
83 static int plugin_load_file (char *file)
84 {
85         lt_dlhandle dlh;
86         void (*reg_handle) (void);
87
88         DBG ("file = %s", file);
89
90         lt_dlinit ();
91         lt_dlerror (); /* clear errors */
92
93         if ((dlh = lt_dlopen (file)) == NULL)
94         {
95                 const char *error = lt_dlerror ();
96
97                 syslog (LOG_ERR, "lt_dlopen failed: %s", error);
98                 DBG ("lt_dlopen failed: %s", error);
99                 return (1);
100         }
101
102         if ((reg_handle = (void (*) (void)) lt_dlsym (dlh, "module_register")) == NULL)
103         {
104                 syslog (LOG_WARNING, "Couldn't find symbol ``module_register'' in ``%s'': %s\n",
105                                 file, lt_dlerror ());
106                 lt_dlclose (dlh);
107                 return (-1);
108         }
109
110         (*reg_handle) ();
111
112         return (0);
113 }
114
115 /*
116  * Public functions
117  */
118 void plugin_set_dir (const char *dir)
119 {
120         if (plugindir != NULL)
121                 free (plugindir);
122
123         if (dir == NULL)
124                 plugindir = NULL;
125         else if ((plugindir = strdup (dir)) == NULL)
126                 syslog (LOG_ERR, "strdup failed: %s", strerror (errno));
127 }
128
129 #define BUFSIZE 512
130 int plugin_load (const char *type)
131 {
132         DIR  *dh;
133         const char *dir;
134         char  filename[BUFSIZE];
135         char  typename[BUFSIZE];
136         int   typename_len;
137         int   ret;
138         struct stat    statbuf;
139         struct dirent *de;
140
141         DBG ("type = %s", type);
142
143         dir = plugin_get_dir ();
144         ret = 1;
145
146         /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
147          * type when matching the filename */
148         if (snprintf (typename, BUFSIZE, "%s.so", type) >= BUFSIZE)
149         {
150                 syslog (LOG_WARNING, "snprintf: truncated: `%s.so'", type);
151                 return (-1);
152         }
153         typename_len = strlen (typename);
154
155         if ((dh = opendir (dir)) == NULL)
156         {
157                 syslog (LOG_ERR, "opendir (%s): %s", dir, strerror (errno));
158                 return (-1);
159         }
160
161         while ((de = readdir (dh)) != NULL)
162         {
163                 if (strncasecmp (de->d_name, typename, typename_len))
164                         continue;
165
166                 if (snprintf (filename, BUFSIZE, "%s/%s", dir, de->d_name) >= BUFSIZE)
167                 {
168                         syslog (LOG_WARNING, "snprintf: truncated: `%s/%s'", dir, de->d_name);
169                         continue;
170                 }
171
172                 if (lstat (filename, &statbuf) == -1)
173                 {
174                         syslog (LOG_WARNING, "stat %s: %s", filename, strerror (errno));
175                         continue;
176                 }
177                 else if (!S_ISREG (statbuf.st_mode))
178                 {
179                         /* don't follow symlinks */
180                         continue;
181                 }
182
183                 if (plugin_load_file (filename) == 0)
184                 {
185                         /* success */
186                         ret = 0;
187                         break;
188                 }
189         }
190
191         closedir (dh);
192
193         return (ret);
194 }
195
196 /*
197  * The `register_*' functions follow
198  */
199 int plugin_register_config (const char *name,
200                 int (*callback) (const char *key, const char *val),
201                 const char **keys, int keys_num)
202 {
203         cf_register (name, callback, keys, keys_num);
204         return (0);
205 } /* int plugin_register_config */
206
207 int plugin_register_init (const char *name,
208                 int (*callback) (void))
209 {
210         return (register_callback (&list_init, name, (void *) callback));
211 } /* plugin_register_init */
212
213 int plugin_register_read (const char *name,
214                 int (*callback) (void))
215 {
216         return (register_callback (&list_read, name, (void *) callback));
217 } /* int plugin_register_read */
218
219 int plugin_register_write (const char *name,
220                 int (*callback) (const data_set_t *ds, const value_list_t *vl))
221 {
222         return (register_callback (&list_write, name, (void *) callback));
223 } /* int plugin_register_write */
224
225 int plugin_register_shutdown (char *name,
226                 int (*callback) (void))
227 {
228         return (register_callback (&list_shutdown, name, (void *) callback));
229 } /* int plugin_register_shutdown */
230
231 int plugin_register_data_set (const data_set_t *ds)
232 {
233         return (register_callback (&list_data_set, ds->type, (void *) ds));
234 } /* int plugin_register_data_set */
235
236 void plugin_init_all (void)
237 {
238         int (*callback) (void);
239         llentry_t *le;
240
241         if (list_init == NULL)
242                 return;
243
244         le = llist_head (list_init);
245         while (le != NULL)
246         {
247                 callback = le->value;
248                 (*callback) ();
249
250                 le = le->next;
251         }
252 } /* void plugin_init_all */
253
254 void plugin_read_all (const int *loop)
255 {
256         int (*callback) (void);
257         llentry_t *le;
258
259         if (list_read == NULL)
260                 return;
261
262         le = llist_head (list_read);
263         while ((*loop == 0) && (le != NULL))
264         {
265                 callback = le->value;
266                 (*callback) ();
267
268                 le = le->next;
269         }
270 } /* void plugin_read_all */
271
272 void plugin_shutdown_all (void)
273 {
274         int (*callback) (void);
275         llentry_t *le;
276
277         if (list_shutdown == NULL)
278                 return;
279
280         le = llist_head (list_shutdown);
281         while (le != NULL)
282         {
283                 callback = le->value;
284                 (*callback) ();
285
286                 le = le->next;
287         }
288 } /* void plugin_shutdown_all */
289
290 int plugin_dispatch_values (const char *name, const value_list_t *vl)
291 {
292         int (*callback) (const data_set_t *, const value_list_t *);
293         data_set_t *ds;
294         llentry_t *le;
295
296         if (list_write == NULL)
297                 return (-1);
298
299         le = llist_search (list_data_set, name);
300         if (le == NULL)
301                 return (-1);
302
303         ds = (data_set_t *) le->value;
304
305         le = llist_head (list_write);
306         while (le != NULL)
307         {
308                 callback = le->value;
309                 (*callback) (ds, vl);
310
311                 le = le->next;
312         }
313
314         return (0);
315 }
316
317 void plugin_complain (int level, complain_t *c, const char *format, ...)
318 {
319         char message[512];
320         va_list ap;
321         int step;
322
323         if (c->delay > 0)
324         {
325                 c->delay--;
326                 return;
327         }
328
329         step = atoi (COLLECTD_STEP);
330         assert (step > 0);
331
332         if (c->interval < step)
333                 c->interval = step;
334         else
335                 c->interval *= 2;
336
337         if (c->interval > 86400)
338                 c->interval = 86400;
339
340         c->delay = c->interval / step;
341
342         va_start (ap, format);
343         vsnprintf (message, 512, format, ap);
344         message[511] = '\0';
345         va_end (ap);
346
347         syslog (level, message);
348 }
349
350 void plugin_relief (int level, complain_t *c, const char *format, ...)
351 {
352         char message[512];
353         va_list ap;
354
355         if (c->interval == 0)
356                 return;
357
358         c->interval = 0;
359
360         va_start (ap, format);
361         vsnprintf (message, 512, format, ap);
362         message[511] = '\0';
363         va_end (ap);
364
365         syslog (level, message);
366 }