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.
28 #include <sys/signal.h>
32 /** the pipe to the plugin process, or -1 if none is currently
35 /** the GLib channel of #fd */
37 /** the GLib IO watch of #channel */
39 /** the output of the current plugin */
44 /** the plugin list; used for traversing to the next plugin */
45 struct plugin_list
*list
;
47 /** arguments passed to execv() */
50 /** caller defined callback function */
51 plugin_callback_t callback
;
52 /** caller defined pointer passed to #callback */
55 /** the index of the next plugin which is going to be
59 /** the pid of the plugin process, or -1 if none is currently
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 */
73 register_plugin(struct plugin_list
*list
, char *path
)
76 if (stat(path
, &st
) < 0)
79 g_ptr_array_add(list
->plugins
, path
);
84 plugin_compare_func_alpha(gconstpointer plugin1
, gconstpointer plugin2
)
86 return strcmp(* (char * const *) plugin1
, * (char * const *) plugin2
);
90 plugin_list_sort(struct plugin_list
*list
, GCompareFunc compare_func
)
92 g_ptr_array_sort(list
->plugins
,
97 plugin_list_load_directory(struct plugin_list
*list
, const char *path
)
99 GDir
*dir
= g_dir_open(path
, 0, NULL
);
104 while ((name
= g_dir_read_name(dir
)) != NULL
) {
105 char *plugin
= g_build_filename(path
, name
, NULL
);
106 if (!register_plugin(list
, plugin
))
112 plugin_list_sort(list
, plugin_compare_func_alpha
);
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
);
125 next_plugin(struct plugin_cycle
*cycle
);
128 plugin_eof(struct plugin_cycle
*cycle
, struct plugin_pipe
*p
)
130 g_io_channel_unref(p
->channel
);
134 /* Only if both pipes are have EOF status we are done */
135 if (cycle
->pipe_stdout
.fd
!= -1 || cycle
->pipe_stderr
.fd
!= -1)
138 int status
, ret
= waitpid(cycle
->pid
, &status
, 0);
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",
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
;
160 /* success: invoke the callback */
161 cycle
->callback(cycle
->pipe_stdout
.data
, true,
162 cycle
->argv
[0], cycle
->callback_data
);
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
;
183 ssize_t nbytes
= condition
& G_IO_IN
184 ? read(p
->fd
, buffer
, sizeof(buffer
))
187 plugin_eof(cycle
, p
);
191 g_string_append_len(p
->data
, buffer
, nbytes
);
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
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
);
218 plugin_fd_add(struct plugin_cycle
*cycle
, struct plugin_pipe
*p
, int 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
,
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
239 g_free(cycle
->argv
[0]);
240 cycle
->argv
[0] = g_path_get_basename(plugin_path
);
243 if (pipe(fds_stdout
) < 0)
247 if (pipe(fds_stderr
) < 0) {
248 close(fds_stdout
[0]);
249 close(fds_stdout
[1]);
256 close(fds_stdout
[0]);
257 close(fds_stdout
[1]);
258 close(fds_stderr
[0]);
259 close(fds_stderr
[1]);
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]);
271 /* XXX close other fds? */
273 execv(plugin_path
, cycle
->argv
);
277 close(fds_stdout
[1]);
278 close(fds_stderr
[1]);
284 plugin_fd_add(cycle
, &cycle
->pipe_stdout
, fds_stdout
[0]);
285 plugin_fd_add(cycle
, &cycle
->pipe_stderr
, fds_stderr
[0]);
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
);
305 const char *plugin_path
= g_ptr_array_index(cycle
->list
->plugins
,
306 cycle
->next_plugin
++);
307 if (start_plugin(cycle
, plugin_path
) < 0) {
309 g_timeout_add(0, plugin_delayed_fail
, cycle
);
315 make_argv(const char*const* args
)
318 while (args
[num
] != NULL
)
322 char **ret
= g_new(char *, num
);
324 /* reserve space for the program name */
327 while (*args
!= NULL
)
328 *ret
++ = g_strdup(*args
++);
330 /* end of argument vector */
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
);
345 cycle
->argv
= make_argv(args
);
346 cycle
->callback
= callback
;
347 cycle
->callback_data
= callback_data
;
348 cycle
->next_plugin
= 0;
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
);
362 plugin_fd_remove(struct plugin_pipe
*p
)
365 g_source_remove(p
->event_id
);
366 g_io_channel_unref(p
->channel
);
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 */
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
]);