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