chiark / gitweb /
Zero out configuration strings when they are freed.
[disorder] / lib / uaudio-command.c
CommitLineData
e8c185c3
RK
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2005, 2006, 2007, 2009 Richard Kettlewell
4 * Portions (C) 2007 Mark Wooding
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19/** @file lib/uaudio-command.c
ec57f6c9
RK
20 * @brief Support for commmand backend
21 *
22 * We use the code in @ref lib/uaudio-schedule.c to ensure that we write at
23 * approximately the 'real' rate. For disorder-playrtp this isn't very useful
24 * (thought it might reduce the size of various buffers downstream of us) but
25 * when run from the speaker it means that pausing stands a chance of working.
26 */
e8c185c3
RK
27#include "common.h"
28
29#include <errno.h>
30#include <unistd.h>
31
32#include "syscalls.h"
33#include "log.h"
34#include "mem.h"
35#include "wstat.h"
36#include "uaudio.h"
37
38/** @brief Pipe to subprocess */
39static int command_fd;
40
41/** @brief Child process ID */
42static pid_t command_pid;
43
44static const char *const command_options[] = {
45 "command",
46 NULL
47};
48
49/** @brief Close pipe and wait for subprocess to terminate */
50static void command_wait(void) {
51 int w;
52 pid_t rc;
53 char *ws;
54
55 close(command_fd);
56 while((rc = waitpid(command_pid, &w, 0) < 0 && errno == EINTR))
57 ;
58 if(rc < 0)
59 fatal(errno, "waitpid");
60 if(w) {
61 ws = wstat(w);
62 error(0, "command subprocess %s", ws);
63 xfree(ws);
64 }
65}
66
67/** @brief Create subprocess */
68static void command_open(void) {
69 int pfd[2];
70 const char *command;
71
b50cfb8a 72 if(!(command = uaudio_get("command", NULL)))
e8c185c3
RK
73 fatal(0, "'command' not set");
74 xpipe(pfd);
75 command_pid = xfork();
76 if(!command_pid) {
77 exitfn = _exit;
78 signal(SIGPIPE, SIG_DFL);
79 xdup2(pfd[0], 0);
80 close(pfd[0]);
81 close(pfd[1]);
82 /* TODO it would be nice to set some environment variables given the sample
83 * format. The original intended model is that you adapt DisOrder to the
84 * command you run but it'd be nice to support the opposite. */
85 execl("/bin/sh", "sh", "-c", command, (char *)0);
86 fatal(errno, "error executing /bin/sh");
87 }
88 close(pfd[0]);
e03eb69e 89 command_fd = pfd[1];
e8c185c3
RK
90}
91
92/** @brief Send audio data to subprocess */
93static size_t command_play(void *buffer, size_t nsamples) {
ec57f6c9 94 uaudio_schedule_synchronize();
e8c185c3
RK
95 const size_t bytes = nsamples * uaudio_sample_size;
96 int written = write(command_fd, buffer, bytes);
97 if(written < 0) {
98 switch(errno) {
99 case EINTR:
100 return 0; /* will retry */
101 case EPIPE:
102 error(0, "audio command subprocess terminated");
103 command_wait();
104 command_open();
105 return 0; /* will retry */
106 default:
107 fatal(errno, "error writing to audio command subprocess");
108 }
109 }
ec57f6c9
RK
110 const size_t written_samples = written / uaudio_sample_size;
111 uaudio_schedule_update(written_samples);
112 return written_samples;
e8c185c3
RK
113}
114
115static void command_start(uaudio_callback *callback,
116 void *userdata) {
117 command_open();
ec57f6c9 118 uaudio_schedule_init();
e8c185c3
RK
119 uaudio_thread_start(callback,
120 userdata,
121 command_play,
122 uaudio_channels,
123 4096 / uaudio_sample_size);
124}
125
126static void command_stop(void) {
127 uaudio_thread_stop();
128 command_wait();
129}
130
131static void command_activate(void) {
ec57f6c9 132 uaudio_schedule_reactivated = 1;
e8c185c3
RK
133 uaudio_thread_activate();
134}
135
136static void command_deactivate(void) {
137 uaudio_thread_deactivate();
138}
139
140const struct uaudio uaudio_command = {
141 .name = "command",
142 .options = command_options,
143 .start = command_start,
144 .stop = command_stop,
145 .activate = command_activate,
146 .deactivate = command_deactivate
147};
148
149/*
150Local Variables:
151c-basic-offset:2
152comment-column:40
153fill-column:79
154indent-tabs-mode:nil
155End:
156*/