1 /*
2 * (c) 2004-2008 The Music Player Daemon Project
3 * http://www.musicpd.org/
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
19 #include "plugin.h"
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <sys/signal.h>
27 #include <sys/wait.h>
29 struct plugin_cycle {
30 /** the plugin list; used for traversing to the next plugin */
31 struct plugin_list *list;
33 /** arguments passed to execv() */
34 char **argv;
36 /** caller defined callback function */
37 plugin_callback_t callback;
38 /** caller defined pointer passed to #callback */
39 void *callback_data;
41 /** the index of the next plugin which is going to be
42 invoked */
43 guint next_plugin;
45 /** the pid of the plugin process, or -1 if none is currently
46 running */
47 pid_t pid;
48 /** the pipe to the plugin process, or -1 if none is currently
49 open */
50 int fd;
51 /** the GLib channel of #fd */
52 GIOChannel *channel;
53 /** the GLib IO watch of #channel */
54 guint event_id;
56 /** the output of the current plugin */
57 GString *data;
58 };
60 static bool
61 register_plugin(struct plugin_list *list, char *path)
62 {
63 int ret;
64 struct stat st;
66 ret = stat(path, &st);
67 if (ret < 0)
68 return false;
70 g_ptr_array_add(list->plugins, path);
71 return true;
72 }
74 bool
75 plugin_list_load_directory(struct plugin_list *list, const char *path)
76 {
77 GDir *dir;
78 const char *name;
79 char *plugin;
80 bool ret;
82 dir = g_dir_open(path, 0, NULL);
83 if (dir == NULL)
84 return false;
86 while ((name = g_dir_read_name(dir)) != NULL) {
87 plugin = g_build_filename(path, name, NULL);
88 ret = register_plugin(list, plugin);
89 if (!ret)
90 g_free(plugin);
91 }
93 g_dir_close(dir);
94 return true;
95 }
97 void plugin_list_deinit(struct plugin_list *list)
98 {
99 for (guint i = 0; i < list->plugins->len; ++i)
100 free(g_ptr_array_index(list->plugins, i));
101 g_ptr_array_free(list->plugins, TRUE);
102 }
104 static void
105 next_plugin(struct plugin_cycle *cycle);
107 static void
108 plugin_eof(struct plugin_cycle *cycle)
109 {
110 int ret, status;
112 g_io_channel_unref(cycle->channel);
113 close(cycle->fd);
114 cycle->fd = -1;
116 ret = waitpid(cycle->pid, &status, 0);
117 cycle->pid = -1;
119 if (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
120 /* the plugin has failed */
121 g_string_free(cycle->data, TRUE);
122 cycle->data = NULL;
124 next_plugin(cycle);
125 } else {
126 /* success: invoke the callback */
127 cycle->callback(cycle->data, cycle->callback_data);
128 }
129 }
131 static gboolean
132 plugin_data(G_GNUC_UNUSED GIOChannel *source,
133 G_GNUC_UNUSED GIOCondition condition, gpointer data)
134 {
135 struct plugin_cycle *cycle = data;
136 char buffer[256];
137 ssize_t nbytes;
139 assert(cycle != NULL);
140 assert(cycle->fd >= 0);
141 assert(cycle->pid > 0);
142 assert(source == cycle->channel);
144 nbytes = condition & G_IO_IN
145 ? read(cycle->fd, buffer, sizeof(buffer))
146 : 0;
147 if (nbytes <= 0) {
148 plugin_eof(cycle);
149 return FALSE;
150 }
152 g_string_append_len(cycle->data, buffer, nbytes);
153 return TRUE;
154 }
156 /**
157 * This is a timer callback which calls the plugin callback "some time
158 * later". This solves the problem that plugin_run() may fail
159 * immediately, leaving its return value in an undefined state.
160 * Instead, install a timer which calls the plugin callback in the
161 * moment after.
162 */
163 static gboolean
164 plugin_delayed_fail(gpointer data)
165 {
166 struct plugin_cycle *cycle = data;
168 assert(cycle != NULL);
169 assert(cycle->fd < 0);
170 assert(cycle->pid < 0);
171 assert(cycle->data == NULL);
173 cycle->callback(NULL, cycle->callback_data);
175 return FALSE;
176 }
178 static int
179 start_plugin(struct plugin_cycle *cycle, const char *plugin_path)
180 {
181 int ret, fds[2];
182 pid_t pid;
184 assert(cycle != NULL);
185 assert(cycle->pid < 0);
186 assert(cycle->fd < 0);
187 assert(cycle->data == NULL);
189 /* set new program name, but free the one from the previous
190 plugin */
191 g_free(cycle->argv[0]);
192 cycle->argv[0] = g_path_get_basename(plugin_path);
194 ret = pipe(fds);
195 if (ret < 0)
196 return -1;
198 pid = fork();
200 if (pid < 0) {
201 close(fds[0]);
202 close(fds[1]);
203 return -1;
204 }
206 if (pid == 0) {
207 dup2(fds[1], 1);
208 dup2(fds[1], 1);
209 close(fds[0]);
210 close(fds[1]);
211 close(0);
212 /* XXX close other fds? */
214 execv(plugin_path, cycle->argv);
215 _exit(1);
216 }
218 close(fds[1]);
220 cycle->pid = pid;
221 cycle->fd = fds[0];
222 cycle->data = g_string_new(NULL);
224 /* XXX CLOEXEC? */
226 cycle->channel = g_io_channel_unix_new(cycle->fd);
227 cycle->event_id = g_io_add_watch(cycle->channel, G_IO_IN|G_IO_HUP,
228 plugin_data, cycle);
230 return 0;
231 }
233 static void
234 next_plugin(struct plugin_cycle *cycle)
235 {
236 const char *plugin_path;
237 int ret = -1;
239 assert(cycle->pid < 0);
240 assert(cycle->fd < 0);
241 assert(cycle->data == NULL);
243 if (cycle->next_plugin >= cycle->list->plugins->len) {
244 /* no plugins left */
245 g_timeout_add(0, plugin_delayed_fail, cycle);
246 return;
247 }
249 plugin_path = g_ptr_array_index(cycle->list->plugins,
250 cycle->next_plugin++);
251 ret = start_plugin(cycle, plugin_path);
252 if (ret < 0) {
253 /* system error */
254 g_timeout_add(0, plugin_delayed_fail, cycle);
255 return;
256 }
257 }
259 static char **
260 make_argv(const char*const* args)
261 {
262 unsigned num = 0;
263 char **ret;
265 while (args[num] != NULL)
266 ++num;
267 num += 2;
269 ret = g_new(char*, num);
271 /* reserve space for the program name */
272 *ret++ = NULL;
274 do {
275 *ret++ = g_strdup(*args++);
276 } while (*args != NULL);
278 /* end of argument vector */
279 *ret++ = NULL;
281 return ret - num;
282 }
284 struct plugin_cycle *
285 plugin_run(struct plugin_list *list, const char *const*args,
286 plugin_callback_t callback, void *callback_data)
287 {
288 struct plugin_cycle *cycle = g_new(struct plugin_cycle, 1);
290 assert(args != NULL);
292 cycle->list = list;
293 cycle->argv = make_argv(args);
294 cycle->callback = callback;
295 cycle->callback_data = callback_data;
296 cycle->next_plugin = 0;
297 cycle->pid = -1;
298 cycle->fd = -1;
299 cycle->data = NULL;
301 next_plugin(cycle);
303 return cycle;
304 }
306 void
307 plugin_stop(struct plugin_cycle *cycle)
308 {
309 if (cycle->fd >= 0) {
310 /* close the pipe to the plugin */
311 g_source_remove(cycle->event_id);
312 g_io_channel_unref(cycle->channel);
313 close(cycle->fd);
314 }
316 if (cycle->pid > 0) {
317 /* kill the plugin process */
318 int status;
320 kill(cycle->pid, SIGTERM);
321 waitpid(cycle->pid, &status, 0);
322 }
324 if (cycle->data != NULL)
325 /* free data that has been received */
326 g_string_free(cycle->data, TRUE);
328 /* free argument list */
329 for (guint i = 0; i == 0 || cycle->argv[i] != NULL; ++i)
330 g_free(cycle->argv[i]);
331 g_free(cycle->argv);
333 g_free(cycle);
334 }