chiark / gitweb /
Work on playlist right-click menu
[disorder] / server / background.c
CommitLineData
5bac5b78
RK
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2004-2009 Richard Kettlewell
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 3 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 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18/** @file server/background.c
19 * @brief Background process support for playing tracks
20 */
21
22#include "disorder-server.h"
23
c0f52b2c
RK
24/** @brief Fork the player or decoder for @p q
25 * @param ev Event loop
5bac5b78
RK
26 * @param player Pointer to player information
27 * @param q Track to play or decode
28 * @param child Function to run inside fork
29 * @param bgdata Passed to @c child()
30 *
31 * @c q->pl had better already be set.
32 */
33int play_background(ev_source *ev,
34 const struct stringlist *player,
35 struct queue_entry *q,
36 play_background_child_fn *child,
37 void *bgdata) {
38 int lfd;
39 struct pbgc_params params[1];
40
41 memset(params, 0, sizeof params);
42 /* Get the raw path. This needs to be done outside the fork. It's needed by
43 * the play-track callback which has to have the raw filename bytes we got
44 * from readdir() as well as the normalized unicode version of the track
45 * name. (Emphasize 'normalized'; even if you use UTF-8 for your filenames,
46 * they might not be normalized and if they are they might not be normalized
47 * to the same canonical form as DisOrder uses.) */
48 params->rawpath = trackdb_rawpath(q->track);
49 /* Call the prefork function in the player module. None of the built-in
50 * modules use this so it's not well tested, unfortunately. */
51 if(q->type & DISORDER_PLAYER_PREFORK)
52 if(!(q->data = play_prefork(q->pl, q->track))) {
2e9ba080 53 disorder_error(0, "prefork function for %s failed", q->track);
5bac5b78
RK
54 return START_HARDFAIL;
55 }
56 /* Capture the player/decoder's stderr and feed it into our logs.
57 *
58 * Use the second arg as the tag if available (it's probably a command name),
59 * otherwise the module name. */
60 if(!isatty(2))
61 lfd = logfd(ev, player->s[2] ? player->s[2] : player->s[1]);
62 else
63 lfd = -1;
64 /* Parse player arguments */
65 int optc = player->n - 2;
66 const char **optv = (const char **)&player->s[2];
67 while(optc > 0 && optv[0][0] == '-') {
68 if(!strcmp(optv[0], "--")) {
69 ++optv;
70 --optc;
71 break;
72 }
73 if(!strcmp(optv[0], "--wait-for-device")
74 || !strncmp(optv[0], "--wait-for-device=", 18)) {
75 const char *waitdevice;
76 if((waitdevice = strchr(optv[0], '='))) {
77 params->waitdevice = waitdevice + 1;
78 } else
79 params->waitdevice = ""; /* use default */
80 ++optv;
81 --optc;
82 } else {
2e9ba080 83 disorder_error(0, "unknown option %s", optv[0]);
5bac5b78
RK
84 return START_HARDFAIL;
85 }
86 }
87 params->argc = optc;
88 params->argv = optv;
89 /* Create the child process */
90 switch(q->pid = fork()) {
91 case 0:
92 /* Child of disorderd */
93 exitfn = _exit;
94 progname = "disorderd-fork";
95 ev_signal_atfork(ev);
96 signal(SIGPIPE, SIG_DFL);
97 /* Send our log output to DisOrder's logs */
98 if(lfd != -1) {
99 xdup2(lfd, 1);
100 xdup2(lfd, 2);
101 xclose(lfd); /* tidy up */
102 }
103 /* Create a new process group, ID = child PID */
104 setpgid(0, 0);
105 _exit(child(q, params, bgdata));
106 case -1:
107 /* Back in disorderd (child could not be created) */
2e9ba080 108 disorder_error(errno, "error calling fork");
5bac5b78
RK
109 if(q->type & DISORDER_PLAYER_PREFORK)
110 play_cleanup(q->pl, q->data); /* else would leak */
111 if(lfd != -1)
112 xclose(lfd);
113 return START_SOFTFAIL;
114 }
115 /* We don't need the child's end of the log pipe */
116 if(lfd != -1)
117 xclose(lfd);
118 /* Set the child's process group ID.
119 *
120 * But wait, didn't we already set it in the child? Yes, but it's possible
121 * that we'll need to address it by process group ID before it gets that far,
122 * so we set it here too. One or the other may fail but as long as one
123 * succeeds that's fine.
124 */
125 setpgid(q->pid, q->pid);
126 /* Ask the event loop to tell us when the child terminates */
127 D(("player subprocess ID %lu", (unsigned long)q->pid));
128 return START_OK;
129}
130
131/*
132Local Variables:
133c-basic-offset:2
134comment-column:40
135fill-column:79
136indent-tabs-mode:nil
137End:
138*/