Commit | Line | Data |
---|---|---|
1c3f1e73 | 1 | /* |
2 | * This file is part of DisOrder | |
3 | * Copyright (C) 2005, 2006, 2007 Richard Kettlewell | |
313acc77 | 4 | * Portions (C) 2007 Mark Wooding |
1c3f1e73 | 5 | * |
e7eb3a27 | 6 | * This program is free software: you can redistribute it and/or modify |
1c3f1e73 | 7 | * it under the terms of the GNU General Public License as published by |
e7eb3a27 | 8 | * the Free Software Foundation, either version 3 of the License, or |
1c3f1e73 | 9 | * (at your option) any later version. |
e7eb3a27 RK |
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 | * | |
1c3f1e73 | 16 | * You should have received a copy of the GNU General Public License |
e7eb3a27 | 17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1c3f1e73 | 18 | */ |
19 | /** @file server/speaker-command.c | |
20 | * @brief Support for @ref BACKEND_COMMAND */ | |
21 | ||
05b75f8d | 22 | #include "common.h" |
1c3f1e73 | 23 | |
24 | #include <unistd.h> | |
25 | #include <poll.h> | |
6d2d327c | 26 | #include <errno.h> |
1c3f1e73 | 27 | |
28 | #include "configuration.h" | |
29 | #include "syscalls.h" | |
30 | #include "log.h" | |
31 | #include "speaker-protocol.h" | |
32 | #include "speaker.h" | |
33 | ||
34 | /** @brief Pipe to subprocess | |
35 | * | |
36 | * This is the file descriptor to write to for @ref BACKEND_COMMAND. | |
37 | */ | |
38 | static int cmdfd = -1; | |
39 | ||
40 | /** @brief poll array slot for @ref cmdfd | |
41 | * | |
42 | * Set by command_beforepoll(). | |
43 | */ | |
44 | static int cmdfd_slot; | |
45 | ||
46 | /** @brief Start the subprocess for @ref BACKEND_COMMAND */ | |
47 | static void fork_cmd(void) { | |
48 | pid_t cmdpid; | |
49 | int pfd[2]; | |
50 | if(cmdfd != -1) close(cmdfd); | |
51 | xpipe(pfd); | |
52 | cmdpid = xfork(); | |
53 | if(!cmdpid) { | |
6d2d327c | 54 | exitfn = _exit; |
1c3f1e73 | 55 | signal(SIGPIPE, SIG_DFL); |
56 | xdup2(pfd[0], 0); | |
57 | close(pfd[0]); | |
58 | close(pfd[1]); | |
59 | execl("/bin/sh", "sh", "-c", config->speaker_command, (char *)0); | |
60 | fatal(errno, "error execing /bin/sh"); | |
61 | } | |
62 | close(pfd[0]); | |
63 | cmdfd = pfd[1]; | |
64 | D(("forked cmd %d, fd = %d", cmdpid, cmdfd)); | |
65 | } | |
66 | ||
67 | /** @brief Command backend initialization */ | |
68 | static void command_init(void) { | |
69 | info("selected command backend"); | |
70 | fork_cmd(); | |
71 | } | |
72 | ||
73 | /** @brief Play to a subprocess */ | |
74 | static size_t command_play(size_t frames) { | |
6d2d327c | 75 | size_t bytes = frames * bpf; |
1c3f1e73 | 76 | int written_bytes; |
77 | ||
78 | written_bytes = write(cmdfd, playing->buffer + playing->start, bytes); | |
79 | D(("actually play %zu bytes, wrote %d", | |
80 | bytes, written_bytes)); | |
81 | if(written_bytes < 0) { | |
82 | switch(errno) { | |
83 | case EPIPE: | |
84 | error(0, "hmm, command died; trying another"); | |
85 | fork_cmd(); | |
86 | return 0; | |
87 | case EAGAIN: | |
88 | return 0; | |
89 | default: | |
90 | fatal(errno, "error writing to subprocess"); | |
91 | } | |
92 | } else | |
6d2d327c | 93 | return written_bytes / bpf; |
1c3f1e73 | 94 | } |
95 | ||
96 | /** @brief Update poll array for writing to subprocess */ | |
e84fb5f0 | 97 | static void command_beforepoll(int attribute((unused)) *timeoutp) { |
1c3f1e73 | 98 | /* We send sample data to the subprocess as fast as it can accept it. |
99 | * This isn't ideal as pause latency can be very high as a result. */ | |
100 | if(cmdfd >= 0) | |
101 | cmdfd_slot = addfd(cmdfd, POLLOUT); | |
102 | } | |
103 | ||
104 | /** @brief Process poll() results for subprocess play */ | |
105 | static int command_ready(void) { | |
106 | if(fds[cmdfd_slot].revents & (POLLOUT | POLLERR)) | |
107 | return 1; | |
108 | else | |
109 | return 0; | |
110 | } | |
111 | ||
112 | const struct speaker_backend command_backend = { | |
113 | BACKEND_COMMAND, | |
6d2d327c | 114 | 0, |
1c3f1e73 | 115 | command_init, |
116 | 0, /* activate */ | |
117 | command_play, | |
118 | 0, /* deactivate */ | |
119 | command_beforepoll, | |
120 | command_ready | |
121 | }; | |
122 | ||
123 | /* | |
124 | Local Variables: | |
125 | c-basic-offset:2 | |
126 | comment-column:40 | |
127 | fill-column:79 | |
128 | indent-tabs-mode:nil | |
129 | End: | |
130 | */ |