1 /**
2 * collectd - src/plugin.c
3 * Copyright (C) 2005 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; either version 2 of the License, or (at your
8 * option) any later version.
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 * Florian octo Forster <octo at verplant.org>
21 **/
23 #include "collectd.h"
25 #include <ltdl.h>
27 #include "plugin.h"
28 #include "network.h"
30 typedef struct plugin
31 {
32 char *type;
33 void (*init) (void);
34 void (*read) (void);
35 void (*write) (char *host, char *inst, char *val);
36 struct plugin *next;
37 } plugin_t;
39 static plugin_t *first_plugin = NULL;
41 extern int operating_mode;
43 static char *plugindir = NULL;
45 char *plugin_get_dir (void)
46 {
47 if (plugindir == NULL)
48 return (PLUGINDIR);
49 else
50 return (plugindir);
51 }
53 void plugin_set_dir (const char *dir)
54 {
55 if (plugindir != NULL)
56 free (plugindir);
58 if (dir == NULL)
59 plugindir = NULL;
60 else if ((plugindir = strdup (dir)) == NULL)
61 syslog (LOG_ERR, "strdup: %s", strerror (errno));
62 }
64 /*
65 * Returns the number of plugins registered
66 */
67 int plugin_count (void)
68 {
69 int i;
70 plugin_t *p;
72 for (i = 0, p = first_plugin; p != NULL; p = p->next)
73 i++;
75 return (i);
76 }
78 /*
79 * Returns the plugins with the type `type' or NULL if it's not found.
80 */
81 plugin_t *plugin_search (const char *type)
82 {
83 plugin_t *ret;
85 if (type == NULL)
86 return (NULL);
88 for (ret = first_plugin; ret != NULL; ret = ret->next)
89 if (strcmp (ret->type, type) == 0)
90 break;
92 return (ret);
93 }
95 /*
96 * Returns true if the plugin is loaded (i.e. `exists') and false otherwise.
97 * This is used in `configfile.c' to skip sections that are not needed..
98 */
99 int plugin_exists (char *type)
100 {
101 if (plugin_search (type) == NULL)
102 return (0);
103 else
104 return (1);
105 }
107 /*
108 * (Try to) load the shared object `file'. Won't complain if it isn't a shared
109 * object, but it will bitch about a shared object not having a
110 * ``module_register'' symbol..
111 */
112 int plugin_load_file (char *file)
113 {
114 lt_dlhandle dlh;
115 void (*reg_handle) (void);
117 lt_dlinit ();
118 lt_dlerror (); /* clear errors */
120 if ((dlh = lt_dlopen (file)) == NULL)
121 return (1);
123 if ((reg_handle = lt_dlsym (dlh, "module_register")) == NULL)
124 {
125 syslog (LOG_WARNING, "Couldn't find symbol ``module_register'' in ``%s'': %s\n",
126 file, lt_dlerror ());
127 lt_dlclose (dlh);
128 return (-1);
129 }
131 (*reg_handle) ();
133 return (0);
134 }
136 #define BUFSIZE 512
137 int plugin_load (const char *type)
138 {
139 DIR *dh;
140 char *dir;
141 char filename[BUFSIZE];
142 char typename[BUFSIZE];
143 int typename_len;
144 int ret;
145 struct stat statbuf;
146 struct dirent *de;
148 dir = plugin_get_dir ();
149 ret = 1;
151 /* don't load twice */
152 if (plugin_search (type) != NULL)
153 return (0);
155 /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
156 * type when matching the filename */
157 if (snprintf (typename, BUFSIZE, "%s.so", type) >= BUFSIZE)
158 {
159 syslog (LOG_WARNING, "snprintf: truncated: `%s.so'", type);
160 return (-1);
161 }
162 typename_len = strlen (typename);
164 if ((dh = opendir (dir)) == NULL)
165 {
166 syslog (LOG_ERR, "opendir (%s): %s", dir, strerror (errno));
167 return (-1);
168 }
170 while ((de = readdir (dh)) != NULL)
171 {
172 if (strncasecmp (de->d_name, typename, typename_len))
173 continue;
175 if (snprintf (filename, BUFSIZE, "%s/%s", dir, de->d_name) >= BUFSIZE)
176 {
177 syslog (LOG_WARNING, "snprintf: truncated: `%s/%s'", dir, de->d_name);
178 continue;
179 }
181 if (lstat (filename, &statbuf) == -1)
182 {
183 syslog (LOG_WARNING, "stat %s: %s", filename, strerror (errno));
184 continue;
185 }
186 else if (!S_ISREG (statbuf.st_mode))
187 {
188 /* don't follow symlinks */
189 continue;
190 }
192 if (plugin_load_file (filename) == 0)
193 {
194 /* success */
195 ret = 0;
196 break;
197 }
198 }
200 closedir (dh);
202 return (ret);
203 }
205 /*
206 * (Try to) load all plugins in `dir'. Returns the number of loaded plugins..
207 */
208 int plugin_load_all (char *dir)
209 {
210 DIR *dh;
211 struct dirent *de;
212 char filename[BUFSIZE];
213 struct stat statbuf;
215 if (dir == NULL)
216 dir = plugin_get_dir ();
217 else
218 plugin_set_dir (dir);
220 if ((dh = opendir (dir)) == NULL)
221 {
222 syslog (LOG_ERR, "opendir (%s): %s", dir, strerror (errno));
223 return (0);
224 }
226 while ((de = readdir (dh)) != NULL)
227 {
228 if (snprintf (filename, BUFSIZE, "%s/%s", dir, de->d_name) >= BUFSIZE)
229 {
230 syslog (LOG_WARNING, "snprintf: truncated: %s/%s", dir, de->d_name);
231 continue;
232 }
234 if (lstat (filename, &statbuf) == -1)
235 {
236 syslog (LOG_WARNING, "stat %s: %s", filename, strerror (errno));
237 continue;
238 }
239 else if (!S_ISREG (statbuf.st_mode))
240 {
241 continue;
242 }
244 plugin_load_file (filename);
245 }
247 closedir (dh);
249 return (plugin_count ());
250 }
251 #undef BUFSIZE
253 /*
254 * Call `init' on all plugins (if given)
255 */
256 void plugin_init_all (void)
257 {
258 plugin_t *p;
260 for (p = first_plugin; p != NULL; p = p->next)
261 if (p->init != NULL)
262 (*p->init) ();
263 }
265 /*
266 * Call `read' on all plugins (if given)
267 */
268 void plugin_read_all (void)
269 {
270 plugin_t *p;
272 for (p = first_plugin; p != NULL; p = p->next)
273 if (p->read != NULL)
274 (*p->read) ();
275 }
277 /*
278 * Add plugin to the linked list of registered plugins.
279 */
280 void plugin_register (char *type,
281 void (*init) (void),
282 void (*read) (void),
283 void (*write) (char *, char *, char *))
284 {
285 plugin_t *p;
287 if (plugin_search (type) != NULL)
288 return;
290 #ifdef HAVE_LIBRRD
291 if (operating_mode != MODE_SERVER)
292 #endif
293 if ((init != NULL) && (read == NULL))
294 syslog (LOG_NOTICE, "Plugin `%s' doesn't provide a read function.", type);
296 if ((p = (plugin_t *) malloc (sizeof (plugin_t))) == NULL)
297 return;
299 if ((p->type = strdup (type)) == NULL)
300 {
301 free (p);
302 return;
303 }
305 p->init = init;
306 p->read = read;
307 p->write = write;
309 p->next = first_plugin;
310 first_plugin = p;
311 }
313 /*
314 * Send received data back to the plugin/module which will append DS
315 * definitions and pass it on to ``rrd_update_file''.
316 */
317 void plugin_write (char *host, char *type, char *inst, char *val)
318 {
319 plugin_t *p;
321 if ((p = plugin_search (type)) == NULL)
322 return;
324 if (p->write == NULL)
325 return;
327 (*p->write) (host, inst, val);
328 }
330 /*
331 * Receive data from the plugin/module and get it somehow to ``plugin_write'':
332 * Either using ``network_send'' (when in network/client mode) or call it
333 * directly (in local mode).
334 */
335 void plugin_submit (char *type, char *inst, char *val)
336 {
337 if (operating_mode == MODE_CLIENT)
338 network_send (type, inst, val);
339 else
340 plugin_write (NULL, type, inst, val);
341 }