chiark / gitweb /
Correct command FD!
[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
20 * @brief Support for commmand backend */
21#include "common.h"
22
23#include <errno.h>
24#include <unistd.h>
25
26#include "syscalls.h"
27#include "log.h"
28#include "mem.h"
29#include "wstat.h"
30#include "uaudio.h"
31
32/** @brief Pipe to subprocess */
33static int command_fd;
34
35/** @brief Child process ID */
36static pid_t command_pid;
37
38static const char *const command_options[] = {
39 "command",
40 NULL
41};
42
43/** @brief Close pipe and wait for subprocess to terminate */
44static void command_wait(void) {
45 int w;
46 pid_t rc;
47 char *ws;
48
49 close(command_fd);
50 while((rc = waitpid(command_pid, &w, 0) < 0 && errno == EINTR))
51 ;
52 if(rc < 0)
53 fatal(errno, "waitpid");
54 if(w) {
55 ws = wstat(w);
56 error(0, "command subprocess %s", ws);
57 xfree(ws);
58 }
59}
60
61/** @brief Create subprocess */
62static void command_open(void) {
63 int pfd[2];
64 const char *command;
65
66 if(!(command = uaudio_get("command")))
67 fatal(0, "'command' not set");
68 xpipe(pfd);
69 command_pid = xfork();
70 if(!command_pid) {
71 exitfn = _exit;
72 signal(SIGPIPE, SIG_DFL);
73 xdup2(pfd[0], 0);
74 close(pfd[0]);
75 close(pfd[1]);
76 /* TODO it would be nice to set some environment variables given the sample
77 * format. The original intended model is that you adapt DisOrder to the
78 * command you run but it'd be nice to support the opposite. */
79 execl("/bin/sh", "sh", "-c", command, (char *)0);
80 fatal(errno, "error executing /bin/sh");
81 }
82 close(pfd[0]);
e03eb69e 83 command_fd = pfd[1];
e8c185c3
RK
84}
85
86/** @brief Send audio data to subprocess */
87static size_t command_play(void *buffer, size_t nsamples) {
88 const size_t bytes = nsamples * uaudio_sample_size;
89 int written = write(command_fd, buffer, bytes);
90 if(written < 0) {
91 switch(errno) {
92 case EINTR:
93 return 0; /* will retry */
94 case EPIPE:
95 error(0, "audio command subprocess terminated");
96 command_wait();
97 command_open();
98 return 0; /* will retry */
99 default:
100 fatal(errno, "error writing to audio command subprocess");
101 }
102 }
103 return written / uaudio_sample_size;
104}
105
106static void command_start(uaudio_callback *callback,
107 void *userdata) {
108 command_open();
109 uaudio_thread_start(callback,
110 userdata,
111 command_play,
112 uaudio_channels,
113 4096 / uaudio_sample_size);
114}
115
116static void command_stop(void) {
117 uaudio_thread_stop();
118 command_wait();
119}
120
121static void command_activate(void) {
122 uaudio_thread_activate();
123}
124
125static void command_deactivate(void) {
126 uaudio_thread_deactivate();
127}
128
129const struct uaudio uaudio_command = {
130 .name = "command",
131 .options = command_options,
132 .start = command_start,
133 .stop = command_stop,
134 .activate = command_activate,
135 .deactivate = command_deactivate
136};
137
138/*
139Local Variables:
140c-basic-offset:2
141comment-column:40
142fill-column:79
143indent-tabs-mode:nil
144End:
145*/