- /* Create the subprocess */
- switch(pid = fork()) {
- case 0:
- /* Child of disorderd */
- exitfn = _exit;
- progname = "disorderd-fork";
- ev_signal_atfork(ev);
- signal(SIGPIPE, SIG_DFL);
- /* Send our log output to DisOrder's logs */
- if(lfd != -1) {
- xdup2(lfd, 1);
- xdup2(lfd, 2);
- xclose(lfd); /* tidy up */
- }
- /* Create a new process group, ID = child PID */
- setpgid(0, 0);
- if((q->type & DISORDER_PLAYER_TYPEMASK) == DISORDER_PLAYER_RAW) {
- /* "Raw" format players always have their output send down a pipe
- * to the disorder-normalize process. This will connect to the
- * speaker process to actually play the audio data.
- */
- /* np will be the pipe to disorder-normalize */
- if(socketpair(PF_UNIX, SOCK_STREAM, 0, np) < 0)
- fatal(errno, "error calling socketpair");
- /* Beware of the Leopard! On OS X 10.5.x, the order of the shutdown
- * calls here DOES MATTER. If you do the SHUT_WR first then the SHUT_RD
- * fails with "Socket is not connected". I think this is a bug but
- * provided implementors either don't care about the order or all agree
- * about the order, choosing the reliable order is an adequate
- * workaround. */
- xshutdown(np[1], SHUT_RD); /* decoder writes to np[1] */
- xshutdown(np[0], SHUT_WR); /* normalize reads from np[0] */
- blocking(np[0]);
- blocking(np[1]);
- /* Start disorder-normalize. We double-fork so that nothing has to wait
- * for disorder-normalize. */
- if(!(npid = xfork())) {
- /* Grandchild of disorderd */
- if(!xfork()) {
- /* Great-grandchild of disorderd */
- /* Connect to the speaker process */
- memset(&addr, 0, sizeof addr);
- addr.sun_family = AF_UNIX;
- snprintf(addr.sun_path, sizeof addr.sun_path,
- "%s/speaker/socket", config->home);
- sfd = xsocket(PF_UNIX, SOCK_STREAM, 0);
- if(connect(sfd, (const struct sockaddr *)&addr, sizeof addr) < 0)
- fatal(errno, "connecting to %s", addr.sun_path);
- /* Send the ID, with a NATIVE-ENDIAN 32 bit length */
- l = strlen(q->id);
- if(write(sfd, &l, sizeof l) < 0
- || write(sfd, q->id, l) < 0)
- fatal(errno, "writing to %s", addr.sun_path);
- /* Await the ack */
- if (read(sfd, &l, 1) < 0)
- fatal(errno, "reading ack from %s", addr.sun_path);
- /* Plumbing */
- xdup2(np[0], 0);
- xdup2(sfd, 1);
- xclose(np[0]);
- xclose(np[1]);
- xclose(sfd);
- /* Ask the speaker to actually start playing the track; we do it here
- * so it's definitely after ack.
- *
- * This is actually insufficient. If the track is prepared and then
- * very shortly afterwards played, then the race we're trying to
- * avoid here will still exist. So either the speaker must cope with
- * SM_PLAY before connection has arrived (in which case we might as
- * well move the SM_PLAY somewhere saner) or we must do more work
- * here to avoid the race.
- *
- * In fact the current speaker can indeed cope with SM_PLAY before
- * the connection arrives. So this code can probably be moved
- * somewhere saner in due course. TODO!
- */
- if(!prepare_only) {
- strcpy(sm.id, q->id);
- sm.type = SM_PLAY;
- speaker_send(speaker_fd, &sm);
- D(("sent SM_PLAY for %s", sm.id));
- }
- /* TODO stderr shouldn't be redirected for disorder-normalize
- * (but it should be for play_track() */
- execlp("disorder-normalize", "disorder-normalize",
- log_default == &log_syslog ? "--syslog" : "--no-syslog",
- "--config", configfile,
- (char *)0);
- fatal(errno, "executing disorder-normalize");
- /* End of the great-grandchild of disorderd */
- }
- /* Back in the grandchild of disorderd */
- _exit(0);
- /* End of the grandchild of disorderd */
- }
- /* Back in the child of disorderd */
- /* Wait for the grandchild of disordered to finish */
- while(waitpid(npid, &n, 0) < 0 && errno == EINTR)
- ;
- /* Pass the file descriptor to the driver in an environment
- * variable. */
- snprintf(buffer, sizeof buffer, "DISORDER_RAW_FD=%d", np[1]);
- if(putenv(buffer) < 0)
- fatal(errno, "error calling putenv");
- /* Close all the FDs we don't need */
- xclose(np[0]);
- }
- /* Wait for a device to clear. This ugliness is now deprecated and will
- * eventually be removed. */
- if(waitdevice) {
- ao_initialize();
- if(*waitdevice) {
- n = ao_driver_id(waitdevice);
- if(n == -1)
- fatal(0, "invalid libao driver: %s", optv[0]);
- } else
- n = ao_default_driver_id();
- /* Make up a format. */
- memset(&format, 0, sizeof format);
- format.bits = 8;
- format.rate = 44100;
- format.channels = 1;
- format.byte_format = AO_FMT_NATIVE;
- retries = 20;
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000; /* 0.1s */
- while((device = ao_open_live(n, &format, 0)) == 0 && retries-- > 0)
- nanosleep(&ts, 0);
- if(device)
- ao_close(device);
- }
- play_track(q->pl,
- optv, optc,
- p,
- q->track);
- /* End of child of disorderd */
- _exit(0);
- case -1:
- /* Back in disorderd (child could not be created) */
- error(errno, "error calling fork");
- if(q->type & DISORDER_PLAYER_PREFORK)
- play_cleanup(q->pl, q->data); /* else would leak */
- if(lfd != -1)
- xclose(lfd);
- return START_SOFTFAIL;
+}
+
+/** @brief Child-process half of start()
+ * @return Process exit code
+ *
+ * Called in subprocess to execute non-raw-format players (via plugin).
+ */
+static int start_child(struct queue_entry *q,
+ const struct pbgc_params *params,
+ void attribute((unused)) *bgdata) {
+ int n;
+
+ /* Wait for a device to clear. This ugliness is now deprecated and will
+ * eventually be removed. */
+ if(params->waitdevice) {
+ ao_initialize();
+ if(*params->waitdevice) {
+ n = ao_driver_id(params->waitdevice);
+ if(n == -1)
+ disorder_fatal(0, "invalid libao driver: %s", params->waitdevice);
+ } else
+ n = ao_default_driver_id();
+ /* Make up a format. */
+ ao_sample_format format;
+ memset(&format, 0, sizeof format);
+ format.bits = 8;
+ format.rate = 44100;
+ format.channels = 1;
+ format.byte_format = AO_FMT_NATIVE;
+ int retries = 20;
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100000000; /* 0.1s */
+ ao_device *device;
+ while((device = ao_open_live(n, &format, 0)) == 0 && retries-- > 0)
+ nanosleep(&ts, 0);
+ if(device)
+ ao_close(device);