1 /*
2 * Copyright (c) 2011 Tim van der Molen <tim@kariliq.nl>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #ifdef __OpenBSD__
18 #include <sys/queue.h>
19 #else
20 #include "compat/queue.h"
21 #endif
22
23 #include <dlfcn.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <strings.h>
27
28 #include "siren.h"
29
30 struct plugin_ip_entry {
31 void *handle;
32 const struct ip *ip;
33 SLIST_ENTRY(plugin_ip_entry) entries;
34 };
35
36 struct plugin_op_entry {
37 void *handle;
38 const struct op *op;
39 SLIST_ENTRY(plugin_op_entry) entries;
40 };
41
42 static void plugin_load_dir(const char *, const char *,
43 int (*)(void *, void *));
44
45 static SLIST_HEAD(, plugin_ip_entry) plugin_ip_list =
46 SLIST_HEAD_INITIALIZER(plugin_ip_list);
47 static SLIST_HEAD(, plugin_op_entry) plugin_op_list =
48 SLIST_HEAD_INITIALIZER(plugin_op_list);
49
50 static int
plugin_add_ip(void * handle,void * ip)51 plugin_add_ip(void *handle, void *ip)
52 {
53 struct plugin_ip_entry *ipe;
54
55 ipe = xmalloc(sizeof *ipe);
56 ipe->handle = handle;
57 ipe->ip = ip;
58 LOG_INFO("loaded %s", ipe->ip->name);
59
60 if (ipe->ip->init != NULL && ipe->ip->init() != 0) {
61 free(ipe);
62 return -1;
63 }
64
65 SLIST_INSERT_HEAD(&plugin_ip_list, ipe, entries);
66 return 0;
67 }
68
69 static int
plugin_add_op(void * handle,void * op)70 plugin_add_op(void *handle, void *op)
71 {
72 struct plugin_op_entry *ope;
73
74 ope = xmalloc(sizeof *ope);
75 ope->handle = handle;
76 ope->op = op;
77 LOG_INFO("loaded %s", ope->op->name);
78
79 if (ope->op->init != NULL && ope->op->init() != 0) {
80 free(ope);
81 return -1;
82 }
83
84 SLIST_INSERT_HEAD(&plugin_op_list, ope, entries);
85 return 0;
86 }
87
88 #ifdef HAVE_PLEDGE
89 void
plugin_append_promises(char ** promises)90 plugin_append_promises(char **promises)
91 {
92 struct plugin_op_entry *ope;
93 char *tmp;
94
95 SLIST_FOREACH(ope, &plugin_op_list, entries)
96 if (ope->op->promises != NULL) {
97 xasprintf(&tmp, "%s %s", *promises, ope->op->promises);
98 free(*promises);
99 *promises = tmp;
100 }
101 }
102 #endif
103
104 void
plugin_end(void)105 plugin_end(void)
106 {
107 struct plugin_ip_entry *ipe;
108 struct plugin_op_entry *ope;
109
110 while ((ipe = SLIST_FIRST(&plugin_ip_list)) != NULL) {
111 SLIST_REMOVE_HEAD(&plugin_ip_list, entries);
112 dlclose(ipe->handle);
113 free(ipe);
114 }
115
116 while ((ope = SLIST_FIRST(&plugin_op_list)) != NULL) {
117 SLIST_REMOVE_HEAD(&plugin_op_list, entries);
118 dlclose(ope->handle);
119 free(ope);
120 }
121 }
122
123 /*
124 * Find an input plug-in based on the extension of the specified file.
125 */
126 const struct ip *
plugin_find_ip(const char * file)127 plugin_find_ip(const char *file)
128 {
129 struct plugin_ip_entry *ipe;
130 const struct ip *ip;
131 int i;
132 char *ext;
133
134 if ((ext = strrchr(file, '.')) == NULL || *++ext == '\0')
135 return NULL;
136
137 ip = NULL;
138 SLIST_FOREACH(ipe, &plugin_ip_list, entries)
139 for (i = 0; ipe->ip->extensions[i] != NULL; i++)
140 if (!strcasecmp(ext, ipe->ip->extensions[i])) {
141 if (ip == NULL ||
142 ip->priority > ipe->ip->priority)
143 ip = ipe->ip;
144 break;
145 }
146
147 return ip;
148 }
149
150 /*
151 * Find an output plug-in by name or, if the name is "default", by priority.
152 */
153 const struct op *
plugin_find_op(const char * name)154 plugin_find_op(const char *name)
155 {
156 struct plugin_op_entry *ope;
157 const struct op *op;
158
159 op = NULL;
160 if (!strcmp(name, "default")) {
161 /* Find plug-in by priority. */
162 SLIST_FOREACH(ope, &plugin_op_list, entries)
163 if (op == NULL || op->priority > ope->op->priority)
164 op = ope->op;
165 } else
166 /* Find plug-in by name. */
167 SLIST_FOREACH(ope, &plugin_op_list, entries)
168 if (!strcmp(name, ope->op->name)) {
169 op = ope->op;
170 break;
171 }
172
173 return op;
174 }
175
176 void
plugin_init(void)177 plugin_init(void)
178 {
179 plugin_load_dir(PLUGIN_IP_DIR, "ip", plugin_add_ip);
180 if (SLIST_EMPTY(&plugin_ip_list)) {
181 LOG_ERRX("%s: no input plug-ins found", PLUGIN_IP_DIR);
182 msg_errx("No input plug-ins found");
183 }
184
185 plugin_load_dir(PLUGIN_OP_DIR, "op", plugin_add_op);
186 if (SLIST_EMPTY(&plugin_op_list)) {
187 LOG_ERRX("%s: no output plug-ins found", PLUGIN_OP_DIR);
188 msg_errx("No output plug-ins found");
189 }
190 }
191
192 static void
plugin_load_dir(const char * dir,const char * symbol,int (* add)(void *,void *))193 plugin_load_dir(const char *dir, const char *symbol,
194 int (*add)(void *, void *))
195 {
196 struct dir *d;
197 struct dir_entry *de;
198 char *ext;
199 void *handle, *plugin;
200
201 if ((d = dir_open(dir)) == NULL) {
202 LOG_ERR("%s", dir);
203 msg_err("%s", dir);
204 return;
205 }
206
207 while ((de = dir_get_entry(d)) != NULL) {
208 if (de->type != FILE_TYPE_REGULAR)
209 continue;
210
211 if ((ext = strrchr(de->name, '.')) == NULL ||
212 strcmp(ext, ".so"))
213 continue;
214
215 if ((handle = dlopen(de->path, RTLD_LAZY | RTLD_LOCAL)) ==
216 NULL) {
217 LOG_ERRX("dlopen: %s: %s", de->path, dlerror());
218 continue;
219 }
220
221 if ((plugin = dlsym(handle, symbol)) == NULL) {
222 LOG_ERRX("dlsym: %s: %s", de->path, dlerror());
223 dlclose(handle);
224 continue;
225 }
226
227 if (add(handle, plugin) == -1)
228 dlclose(handle);
229 }
230
231 dir_close(d);
232 }
233