Code

screen_lyrics: use screen_text
[ncmpc.git] / src / plugin.c
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);
104 static void
105 next_plugin(struct plugin_cycle *cycle);
107 static void
108 plugin_eof(struct plugin_cycle *cycle)
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         }
131 static gboolean
132 plugin_data(G_GNUC_UNUSED GIOChannel *source,
133             G_GNUC_UNUSED GIOCondition condition, gpointer data)
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;
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)
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;
178 static int
179 start_plugin(struct plugin_cycle *cycle, const char *plugin_path)
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;
233 static void
234 next_plugin(struct plugin_cycle *cycle)
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         }
259 static char **
260 make_argv(const char*const* args)
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;
284 struct plugin_cycle *
285 plugin_run(struct plugin_list *list, const char *const*args,
286            plugin_callback_t callback, void *callback_data)
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;
306 void
307 plugin_stop(struct plugin_cycle *cycle)
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);