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.
27 #include <sys/signal.h>
31 /** the pipe to the plugin process, or -1 if none is currently
34 /** the GLib channel of #fd */
36 /** the GLib IO watch of #channel */
38 /** the output of the current plugin */
43 /** the plugin list; used for traversing to the next plugin */
44 struct plugin_list
*list
;
46 /** arguments passed to execv() */
49 /** caller defined callback function */
50 plugin_callback_t callback
;
51 /** caller defined pointer passed to #callback */
54 /** the index of the next plugin which is going to be
58 /** the pid of the plugin process, or -1 if none is currently
62 /** the stdout pipe */
63 struct plugin_pipe pipe_stdout
;
64 /** the stderr pipe */
65 struct plugin_pipe pipe_stderr
;
67 /** list of all error messages from failed plugins */
72 register_plugin(struct plugin_list
*list
, char *path
)
77 ret
= stat(path
, &st
);
81 g_ptr_array_add(list
->plugins
, path
);
86 plugin_compare_func_alpha(gconstpointer plugin1
, gconstpointer plugin2
)
88 return strcmp(* (char * const *) plugin1
, * (char * const *) plugin2
);
92 plugin_list_sort(struct plugin_list
*list
, GCompareFunc compare_func
)
94 g_ptr_array_sort(list
->plugins
,
99 plugin_list_load_directory(struct plugin_list
*list
, const char *path
)
106 dir
= g_dir_open(path
, 0, NULL
);
110 while ((name
= g_dir_read_name(dir
)) != NULL
) {
111 plugin
= g_build_filename(path
, name
, NULL
);
112 ret
= register_plugin(list
, plugin
);
119 plugin_list_sort(list
, plugin_compare_func_alpha
);
124 void plugin_list_deinit(struct plugin_list
*list
)
126 for (guint i
= 0; i
< list
->plugins
->len
; ++i
)
127 free(g_ptr_array_index(list
->plugins
, i
));
128 g_ptr_array_free(list
->plugins
, TRUE
);
132 next_plugin(struct plugin_cycle
*cycle
);
135 plugin_eof(struct plugin_cycle
*cycle
, struct plugin_pipe
*p
)
139 g_io_channel_unref(p
->channel
);
143 /* Only if both pipes are have EOF status we are done */
144 if (cycle
->pipe_stdout
.fd
!= -1 || cycle
->pipe_stderr
.fd
!= -1)
147 ret
= waitpid(cycle
->pid
, &status
, 0);
150 if (ret
< 0 || !WIFEXITED(status
) || WEXITSTATUS(status
) != 0) {
151 /* If we encountered an error other than service unavailable
152 * (69), log it for later. If all plugins fail, we may get
153 * some hints for debugging.*/
154 if (cycle
->pipe_stderr
.data
->len
> 0 &&
155 WEXITSTATUS(status
) != 69)
156 g_string_append_printf(cycle
->all_errors
,
157 "*** %s ***\n\n%s\n",
159 cycle
->pipe_stderr
.data
->str
);
161 /* the plugin has failed */
162 g_string_free(cycle
->pipe_stdout
.data
, TRUE
);
163 cycle
->pipe_stdout
.data
= NULL
;
164 g_string_free(cycle
->pipe_stderr
.data
, TRUE
);
165 cycle
->pipe_stderr
.data
= NULL
;
169 /* success: invoke the callback */
170 cycle
->callback(cycle
->pipe_stdout
.data
, true,
171 cycle
->argv
[0], cycle
->callback_data
);
176 plugin_data(G_GNUC_UNUSED GIOChannel
*source
,
177 G_GNUC_UNUSED GIOCondition condition
, gpointer data
)
179 struct plugin_cycle
*cycle
= data
;
180 struct plugin_pipe
*p
= NULL
;
184 assert(cycle
!= NULL
);
185 assert(cycle
->pid
> 0);
186 if (source
== cycle
->pipe_stdout
.channel
)
187 p
= &cycle
->pipe_stdout
;
188 else if (source
== cycle
->pipe_stderr
.channel
)
189 p
= &cycle
->pipe_stderr
;
193 nbytes
= condition
& G_IO_IN
? read(p
->fd
, buffer
, sizeof(buffer
)) : 0;
195 plugin_eof(cycle
, p
);
199 g_string_append_len(p
->data
, buffer
, nbytes
);
204 * This is a timer callback which calls the plugin callback "some time
205 * later". This solves the problem that plugin_run() may fail
206 * immediately, leaving its return value in an undefined state.
207 * Instead, install a timer which calls the plugin callback in the
211 plugin_delayed_fail(gpointer data
)
213 struct plugin_cycle
*cycle
= data
;
215 assert(cycle
!= NULL
);
216 assert(cycle
->pipe_stdout
.fd
< 0);
217 assert(cycle
->pipe_stderr
.fd
< 0);
218 assert(cycle
->pid
< 0);
220 cycle
->callback(cycle
->all_errors
, false, NULL
, cycle
->callback_data
);
226 plugin_fd_add(struct plugin_cycle
*cycle
, struct plugin_pipe
*p
, int fd
)
229 p
->data
= g_string_new(NULL
);
230 p
->channel
= g_io_channel_unix_new(fd
);
231 p
->event_id
= g_io_add_watch(p
->channel
, G_IO_IN
|G_IO_HUP
,
236 start_plugin(struct plugin_cycle
*cycle
, const char *plugin_path
)
238 int ret
, fds_stdout
[2], fds_stderr
[2];
241 assert(cycle
!= NULL
);
242 assert(cycle
->pid
< 0);
243 assert(cycle
->pipe_stdout
.fd
< 0);
244 assert(cycle
->pipe_stderr
.fd
< 0);
245 assert(cycle
->pipe_stdout
.data
== NULL
);
246 assert(cycle
->pipe_stderr
.data
== NULL
);
248 /* set new program name, but free the one from the previous
250 g_free(cycle
->argv
[0]);
251 cycle
->argv
[0] = g_path_get_basename(plugin_path
);
253 ret
= pipe(fds_stdout
);
256 ret
= pipe(fds_stderr
);
258 close(fds_stdout
[0]);
259 close(fds_stdout
[1]);
266 close(fds_stdout
[0]);
267 close(fds_stdout
[1]);
268 close(fds_stderr
[0]);
269 close(fds_stderr
[1]);
274 dup2(fds_stdout
[1], 1);
275 dup2(fds_stderr
[1], 2);
276 close(fds_stdout
[0]);
277 close(fds_stdout
[1]);
278 close(fds_stderr
[0]);
279 close(fds_stderr
[1]);
281 /* XXX close other fds? */
283 execv(plugin_path
, cycle
->argv
);
287 close(fds_stdout
[1]);
288 close(fds_stderr
[1]);
294 plugin_fd_add(cycle
, &cycle
->pipe_stdout
, fds_stdout
[0]);
295 plugin_fd_add(cycle
, &cycle
->pipe_stderr
, fds_stderr
[0]);
301 next_plugin(struct plugin_cycle
*cycle
)
303 const char *plugin_path
;
306 assert(cycle
->pid
< 0);
307 assert(cycle
->pipe_stdout
.fd
< 0);
308 assert(cycle
->pipe_stderr
.fd
< 0);
309 assert(cycle
->pipe_stdout
.data
== NULL
);
310 assert(cycle
->pipe_stderr
.data
== NULL
);
312 if (cycle
->next_plugin
>= cycle
->list
->plugins
->len
) {
313 /* no plugins left */
314 g_timeout_add(0, plugin_delayed_fail
, cycle
);
318 plugin_path
= g_ptr_array_index(cycle
->list
->plugins
,
319 cycle
->next_plugin
++);
320 ret
= start_plugin(cycle
, plugin_path
);
323 g_timeout_add(0, plugin_delayed_fail
, cycle
);
329 make_argv(const char*const* args
)
334 while (args
[num
] != NULL
)
338 ret
= g_new(char*, num
);
340 /* reserve space for the program name */
343 while (*args
!= NULL
)
344 *ret
++ = g_strdup(*args
++);
346 /* end of argument vector */
352 struct plugin_cycle
*
353 plugin_run(struct plugin_list
*list
, const char *const*args
,
354 plugin_callback_t callback
, void *callback_data
)
356 struct plugin_cycle
*cycle
= g_new(struct plugin_cycle
, 1);
358 assert(args
!= NULL
);
361 cycle
->argv
= make_argv(args
);
362 cycle
->callback
= callback
;
363 cycle
->callback_data
= callback_data
;
364 cycle
->next_plugin
= 0;
366 cycle
->pipe_stdout
.fd
= -1;
367 cycle
->pipe_stderr
.fd
= -1;
368 cycle
->pipe_stdout
.data
= NULL
;
369 cycle
->pipe_stderr
.data
= NULL
;
371 cycle
->all_errors
= g_string_new(NULL
);
378 plugin_fd_remove(struct plugin_pipe
*p
)
381 g_source_remove(p
->event_id
);
382 g_io_channel_unref(p
->channel
);
388 plugin_stop(struct plugin_cycle
*cycle
)
390 plugin_fd_remove(&cycle
->pipe_stdout
);
391 plugin_fd_remove(&cycle
->pipe_stderr
);
393 if (cycle
->pid
> 0) {
394 /* kill the plugin process */
397 kill(cycle
->pid
, SIGTERM
);
398 waitpid(cycle
->pid
, &status
, 0);
401 /* free data that has been received */
402 if (cycle
->pipe_stdout
.data
!= NULL
)
403 g_string_free(cycle
->pipe_stdout
.data
, TRUE
);
404 if (cycle
->pipe_stderr
.data
!= NULL
)
405 g_string_free(cycle
->pipe_stderr
.data
, TRUE
);
406 if (cycle
->all_errors
!= NULL
)
407 g_string_free(cycle
->all_errors
, TRUE
);
409 /* free argument list */
410 for (guint i
= 0; i
== 0 || cycle
->argv
[i
] != NULL
; ++i
)
411 g_free(cycle
->argv
[i
]);