lyrics: lyrics area regex fix for LyricWikia
[ncmpc.git] / src / plugin.c
blob64e61ab73d2b1c31ade1bce65fcd8c5cf3664083
1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
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.
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.
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "plugin.h"
21 #include "Compiler.h"
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/signal.h>
29 #include <sys/wait.h>
31 struct plugin_pipe {
32 /** the pipe to the plugin process, or -1 if none is currently
33 open */
34 int fd;
35 /** the GLib channel of #fd */
36 GIOChannel *channel;
37 /** the GLib IO watch of #channel */
38 guint event_id;
39 /** the output of the current plugin */
40 GString *data;
43 struct plugin_cycle {
44 /** the plugin list; used for traversing to the next plugin */
45 struct plugin_list *list;
47 /** arguments passed to execv() */
48 char **argv;
50 /** caller defined callback function */
51 plugin_callback_t callback;
52 /** caller defined pointer passed to #callback */
53 void *callback_data;
55 /** the index of the next plugin which is going to be
56 invoked */
57 guint next_plugin;
59 /** the pid of the plugin process, or -1 if none is currently
60 running */
61 pid_t pid;
63 /** the stdout pipe */
64 struct plugin_pipe pipe_stdout;
65 /** the stderr pipe */
66 struct plugin_pipe pipe_stderr;
68 /** list of all error messages from failed plugins */
69 GString *all_errors;
72 static bool
73 register_plugin(struct plugin_list *list, char *path)
75 struct stat st;
76 if (stat(path, &st) < 0)
77 return false;
79 g_ptr_array_add(list->plugins, path);
80 return true;
83 static gint
84 plugin_compare_func_alpha(gconstpointer plugin1, gconstpointer plugin2)
86 return strcmp(* (char * const *) plugin1, * (char * const *) plugin2);
89 static void
90 plugin_list_sort(struct plugin_list *list, GCompareFunc compare_func)
92 g_ptr_array_sort(list->plugins,
93 compare_func);
96 bool
97 plugin_list_load_directory(struct plugin_list *list, const char *path)
99 GDir *dir = g_dir_open(path, 0, NULL);
100 if (dir == NULL)
101 return false;
103 const char *name;
104 while ((name = g_dir_read_name(dir)) != NULL) {
105 char *plugin = g_build_filename(path, name, NULL);
106 if (!register_plugin(list, plugin))
107 g_free(plugin);
110 g_dir_close(dir);
112 plugin_list_sort(list, plugin_compare_func_alpha);
114 return true;
117 void plugin_list_deinit(struct plugin_list *list)
119 for (guint i = 0; i < list->plugins->len; ++i)
120 free(g_ptr_array_index(list->plugins, i));
121 g_ptr_array_free(list->plugins, TRUE);
124 static void
125 next_plugin(struct plugin_cycle *cycle);
127 static void
128 plugin_eof(struct plugin_cycle *cycle, struct plugin_pipe *p)
130 g_io_channel_unref(p->channel);
131 close(p->fd);
132 p->fd = -1;
134 /* Only if both pipes are have EOF status we are done */
135 if (cycle->pipe_stdout.fd != -1 || cycle->pipe_stderr.fd != -1)
136 return;
138 int status, ret = waitpid(cycle->pid, &status, 0);
139 cycle->pid = -1;
141 if (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
142 /* If we encountered an error other than service unavailable
143 * (69), log it for later. If all plugins fail, we may get
144 * some hints for debugging.*/
145 if (cycle->pipe_stderr.data->len > 0 &&
146 WEXITSTATUS(status) != 69)
147 g_string_append_printf(cycle->all_errors,
148 "*** %s ***\n\n%s\n",
149 cycle->argv[0],
150 cycle->pipe_stderr.data->str);
152 /* the plugin has failed */
153 g_string_free(cycle->pipe_stdout.data, TRUE);
154 cycle->pipe_stdout.data = NULL;
155 g_string_free(cycle->pipe_stderr.data, TRUE);
156 cycle->pipe_stderr.data = NULL;
158 next_plugin(cycle);
159 } else {
160 /* success: invoke the callback */
161 cycle->callback(cycle->pipe_stdout.data, true,
162 cycle->argv[0], cycle->callback_data);
166 static gboolean
167 plugin_data(gcc_unused GIOChannel *source,
168 gcc_unused GIOCondition condition, gpointer data)
170 struct plugin_cycle *cycle = data;
171 assert(cycle != NULL);
172 assert(cycle->pid > 0);
174 struct plugin_pipe *p = NULL;
175 if (source == cycle->pipe_stdout.channel)
176 p = &cycle->pipe_stdout;
177 else if (source == cycle->pipe_stderr.channel)
178 p = &cycle->pipe_stderr;
179 assert(p != NULL);
180 assert(p->fd >= 0);
182 char buffer[256];
183 ssize_t nbytes = condition & G_IO_IN
184 ? read(p->fd, buffer, sizeof(buffer))
185 : 0;
186 if (nbytes <= 0) {
187 plugin_eof(cycle, p);
188 return FALSE;
191 g_string_append_len(p->data, buffer, nbytes);
192 return TRUE;
196 * This is a timer callback which calls the plugin callback "some time
197 * later". This solves the problem that plugin_run() may fail
198 * immediately, leaving its return value in an undefined state.
199 * Instead, install a timer which calls the plugin callback in the
200 * moment after.
202 static gboolean
203 plugin_delayed_fail(gpointer data)
205 struct plugin_cycle *cycle = data;
207 assert(cycle != NULL);
208 assert(cycle->pipe_stdout.fd < 0);
209 assert(cycle->pipe_stderr.fd < 0);
210 assert(cycle->pid < 0);
212 cycle->callback(cycle->all_errors, false, NULL, cycle->callback_data);
214 return FALSE;
217 static void
218 plugin_fd_add(struct plugin_cycle *cycle, struct plugin_pipe *p, int fd)
220 p->fd = fd;
221 p->data = g_string_new(NULL);
222 p->channel = g_io_channel_unix_new(fd);
223 p->event_id = g_io_add_watch(p->channel, G_IO_IN|G_IO_HUP,
224 plugin_data, cycle);
227 static int
228 start_plugin(struct plugin_cycle *cycle, const char *plugin_path)
230 assert(cycle != NULL);
231 assert(cycle->pid < 0);
232 assert(cycle->pipe_stdout.fd < 0);
233 assert(cycle->pipe_stderr.fd < 0);
234 assert(cycle->pipe_stdout.data == NULL);
235 assert(cycle->pipe_stderr.data == NULL);
237 /* set new program name, but free the one from the previous
238 plugin */
239 g_free(cycle->argv[0]);
240 cycle->argv[0] = g_path_get_basename(plugin_path);
242 int fds_stdout[2];
243 if (pipe(fds_stdout) < 0)
244 return -1;
246 int fds_stderr[2];
247 if (pipe(fds_stderr) < 0) {
248 close(fds_stdout[0]);
249 close(fds_stdout[1]);
250 return -1;
253 pid_t pid = fork();
255 if (pid < 0) {
256 close(fds_stdout[0]);
257 close(fds_stdout[1]);
258 close(fds_stderr[0]);
259 close(fds_stderr[1]);
260 return -1;
263 if (pid == 0) {
264 dup2(fds_stdout[1], 1);
265 dup2(fds_stderr[1], 2);
266 close(fds_stdout[0]);
267 close(fds_stdout[1]);
268 close(fds_stderr[0]);
269 close(fds_stderr[1]);
270 close(0);
271 /* XXX close other fds? */
273 execv(plugin_path, cycle->argv);
274 _exit(1);
277 close(fds_stdout[1]);
278 close(fds_stderr[1]);
280 cycle->pid = pid;
282 /* XXX CLOEXEC? */
284 plugin_fd_add(cycle, &cycle->pipe_stdout, fds_stdout[0]);
285 plugin_fd_add(cycle, &cycle->pipe_stderr, fds_stderr[0]);
287 return 0;
290 static void
291 next_plugin(struct plugin_cycle *cycle)
293 assert(cycle->pid < 0);
294 assert(cycle->pipe_stdout.fd < 0);
295 assert(cycle->pipe_stderr.fd < 0);
296 assert(cycle->pipe_stdout.data == NULL);
297 assert(cycle->pipe_stderr.data == NULL);
299 if (cycle->next_plugin >= cycle->list->plugins->len) {
300 /* no plugins left */
301 g_timeout_add(0, plugin_delayed_fail, cycle);
302 return;
305 const char *plugin_path = g_ptr_array_index(cycle->list->plugins,
306 cycle->next_plugin++);
307 if (start_plugin(cycle, plugin_path) < 0) {
308 /* system error */
309 g_timeout_add(0, plugin_delayed_fail, cycle);
310 return;
314 static char **
315 make_argv(const char*const* args)
317 unsigned num = 0;
318 while (args[num] != NULL)
319 ++num;
320 num += 2;
322 char **ret = g_new(char *, num);
324 /* reserve space for the program name */
325 *ret++ = NULL;
327 while (*args != NULL)
328 *ret++ = g_strdup(*args++);
330 /* end of argument vector */
331 *ret++ = NULL;
333 return ret - num;
336 struct plugin_cycle *
337 plugin_run(struct plugin_list *list, const char *const*args,
338 plugin_callback_t callback, void *callback_data)
340 struct plugin_cycle *cycle = g_new(struct plugin_cycle, 1);
342 assert(args != NULL);
344 cycle->list = list;
345 cycle->argv = make_argv(args);
346 cycle->callback = callback;
347 cycle->callback_data = callback_data;
348 cycle->next_plugin = 0;
349 cycle->pid = -1;
350 cycle->pipe_stdout.fd = -1;
351 cycle->pipe_stderr.fd = -1;
352 cycle->pipe_stdout.data = NULL;
353 cycle->pipe_stderr.data = NULL;
355 cycle->all_errors = g_string_new(NULL);
356 next_plugin(cycle);
358 return cycle;
361 static void
362 plugin_fd_remove(struct plugin_pipe *p)
364 if (p->fd >= 0) {
365 g_source_remove(p->event_id);
366 g_io_channel_unref(p->channel);
367 close(p->fd);
371 void
372 plugin_stop(struct plugin_cycle *cycle)
374 plugin_fd_remove(&cycle->pipe_stdout);
375 plugin_fd_remove(&cycle->pipe_stderr);
377 if (cycle->pid > 0) {
378 /* kill the plugin process */
379 int status;
381 kill(cycle->pid, SIGTERM);
382 waitpid(cycle->pid, &status, 0);
385 /* free data that has been received */
386 if (cycle->pipe_stdout.data != NULL)
387 g_string_free(cycle->pipe_stdout.data, TRUE);
388 if (cycle->pipe_stderr.data != NULL)
389 g_string_free(cycle->pipe_stderr.data, TRUE);
390 if (cycle->all_errors != NULL)
391 g_string_free(cycle->all_errors, TRUE);
393 /* free argument list */
394 for (guint i = 0; i == 0 || cycle->argv[i] != NULL; ++i)
395 g_free(cycle->argv[i]);
396 g_free(cycle->argv);
398 g_free(cycle);