Code

Spelling corrections
[ncmpc.git] / src / plugin.c
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2009 The Music Player Daemon Project
3  * Project homepage: http://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.
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.
18 */
20 #include "plugin.h"
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/signal.h>
28 #include <sys/wait.h>
30 struct plugin_cycle {
31         /** the plugin list; used for traversing to the next plugin */
32         struct plugin_list *list;
34         /** arguments passed to execv() */
35         char **argv;
37         /** caller defined callback function */
38         plugin_callback_t callback;
39         /** caller defined pointer passed to #callback */
40         void *callback_data;
42         /** the index of the next plugin which is going to be
43             invoked */
44         guint next_plugin;
46         /** the pid of the plugin process, or -1 if none is currently
47             running */
48         pid_t pid;
49         /** the pipe to the plugin process, or -1 if none is currently
50             open */
51         int fd;
52         /** the GLib channel of #fd */
53         GIOChannel *channel;
54         /** the GLib IO watch of #channel */
55         guint event_id;
57         /** the output of the current plugin */
58         GString *data;
59 };
61 static bool
62 register_plugin(struct plugin_list *list, char *path)
63 {
64         int ret;
65         struct stat st;
67         ret = stat(path, &st);
68         if (ret < 0)
69                 return false;
71         g_ptr_array_add(list->plugins, path);
72         return true;
73 }
75 bool
76 plugin_list_load_directory(struct plugin_list *list, const char *path)
77 {
78         GDir *dir;
79         const char *name;
80         char *plugin;
81         bool ret;
83         dir = g_dir_open(path, 0, NULL);
84         if (dir == NULL)
85                 return false;
87         while ((name = g_dir_read_name(dir)) != NULL) {
88                 plugin = g_build_filename(path, name, NULL);
89                 ret = register_plugin(list, plugin);
90                 if (!ret)
91                         g_free(plugin);
92         }
94         g_dir_close(dir);
95         return true;
96 }
98 void plugin_list_deinit(struct plugin_list *list)
99 {
100         for (guint i = 0; i < list->plugins->len; ++i)
101                 free(g_ptr_array_index(list->plugins, i));
102         g_ptr_array_free(list->plugins, TRUE);
105 static void
106 next_plugin(struct plugin_cycle *cycle);
108 static void
109 plugin_eof(struct plugin_cycle *cycle)
111         int ret, status;
113         g_io_channel_unref(cycle->channel);
114         close(cycle->fd);
115         cycle->fd = -1;
117         ret = waitpid(cycle->pid, &status, 0);
118         cycle->pid = -1;
120         if (ret < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
121                 /* the plugin has failed */
122                 g_string_free(cycle->data, TRUE);
123                 cycle->data = NULL;
125                 next_plugin(cycle);
126         } else {
127                 /* success: invoke the callback */
128                 cycle->callback(cycle->data, cycle->callback_data);
129         }
132 static gboolean
133 plugin_data(G_GNUC_UNUSED GIOChannel *source,
134             G_GNUC_UNUSED GIOCondition condition, gpointer data)
136         struct plugin_cycle *cycle = data;
137         char buffer[256];
138         ssize_t nbytes;
140         assert(cycle != NULL);
141         assert(cycle->fd >= 0);
142         assert(cycle->pid > 0);
143         assert(source == cycle->channel);
145         nbytes = condition & G_IO_IN
146                 ? read(cycle->fd, buffer, sizeof(buffer))
147                 : 0;
148         if (nbytes <= 0) {
149                 plugin_eof(cycle);
150                 return FALSE;
151         }
153         g_string_append_len(cycle->data, buffer, nbytes);
154         return TRUE;
157 /**
158  * This is a timer callback which calls the plugin callback "some time
159  * later".  This solves the problem that plugin_run() may fail
160  * immediately, leaving its return value in an undefined state.
161  * Instead, install a timer which calls the plugin callback in the
162  * moment after.
163  */
164 static gboolean
165 plugin_delayed_fail(gpointer data)
167         struct plugin_cycle *cycle = data;
169         assert(cycle != NULL);
170         assert(cycle->fd < 0);
171         assert(cycle->pid < 0);
172         assert(cycle->data == NULL);
174         cycle->callback(NULL, cycle->callback_data);
176         return FALSE;
179 static int
180 start_plugin(struct plugin_cycle *cycle, const char *plugin_path)
182         int ret, fds[2];
183         pid_t pid;
185         assert(cycle != NULL);
186         assert(cycle->pid < 0);
187         assert(cycle->fd < 0);
188         assert(cycle->data == NULL);
190         /* set new program name, but free the one from the previous
191            plugin */
192         g_free(cycle->argv[0]);
193         cycle->argv[0] = g_path_get_basename(plugin_path);
195         ret = pipe(fds);
196         if (ret < 0)
197                 return -1;
199         pid = fork();
201         if (pid < 0) {
202                 close(fds[0]);
203                 close(fds[1]);
204                 return -1;
205         }
207         if (pid == 0) {
208                 dup2(fds[1], 1);
209                 dup2(fds[1], 1);
210                 close(fds[0]);
211                 close(fds[1]);
212                 close(0);
213                 /* XXX close other fds? */
215                 execv(plugin_path, cycle->argv);
216                 _exit(1);
217         }
219         close(fds[1]);
221         cycle->pid = pid;
222         cycle->fd = fds[0];
223         cycle->data = g_string_new(NULL);
225         /* XXX CLOEXEC? */
227         cycle->channel = g_io_channel_unix_new(cycle->fd);
228         cycle->event_id = g_io_add_watch(cycle->channel, G_IO_IN|G_IO_HUP,
229                                          plugin_data, cycle);
231         return 0;
234 static void
235 next_plugin(struct plugin_cycle *cycle)
237         const char *plugin_path;
238         int ret = -1;
240         assert(cycle->pid < 0);
241         assert(cycle->fd < 0);
242         assert(cycle->data == NULL);
244         if (cycle->next_plugin >= cycle->list->plugins->len) {
245                 /* no plugins left */
246                 g_timeout_add(0, plugin_delayed_fail, cycle);
247                 return;
248         }
250         plugin_path = g_ptr_array_index(cycle->list->plugins,
251                                         cycle->next_plugin++);
252         ret = start_plugin(cycle, plugin_path);
253         if (ret < 0) {
254                 /* system error */
255                 g_timeout_add(0, plugin_delayed_fail, cycle);
256                 return;
257         }
260 static char **
261 make_argv(const char*const* args)
263         unsigned num = 0;
264         char **ret;
266         while (args[num] != NULL)
267                 ++num;
268         num += 2;
270         ret = g_new(char*, num);
272         /* reserve space for the program name */
273         *ret++ = NULL;
275         do {
276                 *ret++ = g_strdup(*args++);
277         } while (*args != NULL);
279         /* end of argument vector */
280         *ret++ = NULL;
282         return ret - num;
285 struct plugin_cycle *
286 plugin_run(struct plugin_list *list, const char *const*args,
287            plugin_callback_t callback, void *callback_data)
289         struct plugin_cycle *cycle = g_new(struct plugin_cycle, 1);
291         assert(args != NULL);
293         cycle->list = list;
294         cycle->argv = make_argv(args);
295         cycle->callback = callback;
296         cycle->callback_data = callback_data;
297         cycle->next_plugin = 0;
298         cycle->pid = -1;
299         cycle->fd = -1;
300         cycle->data = NULL;
302         next_plugin(cycle);
304         return cycle;
307 void
308 plugin_stop(struct plugin_cycle *cycle)
310         if (cycle->fd >= 0) {
311                 /* close the pipe to the plugin */
312                 g_source_remove(cycle->event_id);
313                 g_io_channel_unref(cycle->channel);
314                 close(cycle->fd);
315         }
317         if (cycle->pid > 0) {
318                 /* kill the plugin process */
319                 int status;
321                 kill(cycle->pid, SIGTERM);
322                 waitpid(cycle->pid, &status, 0);
323         }
325         if (cycle->data != NULL)
326                 /* free data that has been received */
327                 g_string_free(cycle->data, TRUE);
329         /* free argument list */
330         for (guint i = 0; i == 0 || cycle->argv[i] != NULL; ++i)
331                 g_free(cycle->argv[i]);
332         g_free(cycle->argv);
334         g_free(cycle);