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