• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 
27 #include <signal.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <getopt.h>
35 #include <locale.h>
36 
37 #ifdef __linux__
38 #include <sys/prctl.h>
39 #endif
40 
41 #include <pulse/pulseaudio.h>
42 
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/macro.h>
45 
46 static pa_context *context = NULL;
47 static pa_mainloop_api *mainloop_api = NULL;
48 static char **child_argv = NULL;
49 static int child_argc = 0;
50 static pid_t child_pid = (pid_t) -1;
51 static int child_ret = 0;
52 static int dead = 1;
53 static int fork_failed = 0;
54 
quit(int ret)55 static void quit(int ret) {
56     pa_assert(mainloop_api);
57     mainloop_api->quit(mainloop_api, ret);
58 }
59 
context_drain_complete(pa_context * c,void * userdata)60 static void context_drain_complete(pa_context *c, void *userdata) {
61     pa_context_disconnect(c);
62 }
63 
drain(void)64 static void drain(void) {
65     pa_operation *o;
66 
67     if (context) {
68         if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
69             pa_context_disconnect(context);
70         else
71             pa_operation_unref(o);
72     } else
73         quit(0);
74 }
75 
start_child(void)76 static int start_child(void) {
77 
78     if ((child_pid = fork()) < 0) {
79         fprintf(stderr, _("fork(): %s\n"), strerror(errno));
80         fork_failed = 1;
81 
82         return -1;
83 
84     } else if (child_pid == 0) {
85         /* Child */
86 
87 #ifdef __linux__
88         prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
89 #endif
90 
91         if (execvp(child_argv[0], child_argv) < 0)
92             fprintf(stderr, _("execvp(): %s\n"), strerror(errno));
93 
94         _exit(1);
95 
96     } else {
97 
98         /* parent */
99         dead = 0;
100     }
101 
102     return 0;
103 }
104 
resume_complete(pa_context * c,int success,void * userdata)105 static void resume_complete(pa_context *c, int success, void *userdata) {
106     static int n = 0;
107 
108     n++;
109 
110     if (!success) {
111         fprintf(stderr, _("Failure to resume: %s\n"), pa_strerror(pa_context_errno(c)));
112         quit(1);
113         return;
114     }
115 
116     if (n >= 2)
117         drain(); /* drain and quit */
118 }
119 
resume(void)120 static void resume(void) {
121     static int n = 0;
122 
123     n++;
124 
125     if (n > 1)
126         return;
127 
128     if (context) {
129         if (pa_context_is_local(context)) {
130             pa_operation_unref(pa_context_suspend_sink_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
131             pa_operation_unref(pa_context_suspend_source_by_index(context, PA_INVALID_INDEX, 0, resume_complete, NULL));
132         } else
133             drain();
134     } else {
135         quit(0);
136     }
137 }
138 
suspend_complete(pa_context * c,int success,void * userdata)139 static void suspend_complete(pa_context *c, int success, void *userdata) {
140     static int n = 0;
141 
142     n++;
143 
144     if (!success) {
145         fprintf(stderr, _("Failure to suspend: %s\n"), pa_strerror(pa_context_errno(c)));
146         quit(1);
147         return;
148     }
149 
150     if (n >= 2) {
151         if (start_child() < 0)
152             resume();
153     }
154 }
155 
context_state_callback(pa_context * c,void * userdata)156 static void context_state_callback(pa_context *c, void *userdata) {
157     pa_assert(c);
158 
159     switch (pa_context_get_state(c)) {
160         case PA_CONTEXT_CONNECTING:
161         case PA_CONTEXT_AUTHORIZING:
162         case PA_CONTEXT_SETTING_NAME:
163             break;
164 
165         case PA_CONTEXT_READY:
166             if (pa_context_is_local(c)) {
167                 pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
168                 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, 1, suspend_complete, NULL));
169             } else {
170                 fprintf(stderr, _("WARNING: Sound server is not local, not suspending.\n"));
171                 if (start_child() < 0)
172                     drain();
173             }
174 
175             break;
176 
177         case PA_CONTEXT_TERMINATED:
178             quit(0);
179             break;
180 
181         case PA_CONTEXT_FAILED:
182         default:
183             fprintf(stderr, _("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
184 
185             pa_context_unref(context);
186             context = NULL;
187 
188             if (child_pid == (pid_t) -1) {
189                 /* not started yet, then we do it now */
190                 if (start_child() < 0)
191                     quit(1);
192             } else if (dead)
193                 /* already started, and dead, so let's quit */
194                 quit(1);
195 
196             break;
197     }
198 }
199 
sigint_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)200 static void sigint_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
201     fprintf(stderr, _("Got SIGINT, exiting.\n"));
202     resume();
203 }
204 
sigchld_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)205 static void sigchld_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
206     int status = 0;
207     pid_t p;
208 
209     p = waitpid(-1, &status, WNOHANG);
210 
211     if (p != child_pid)
212         return;
213 
214     dead = 1;
215 
216     if (WIFEXITED(status))
217         child_ret = WEXITSTATUS(status);
218     else if (WIFSIGNALED(status)) {
219         fprintf(stderr, _("WARNING: Child process terminated by signal %u\n"), WTERMSIG(status));
220         child_ret = 1;
221     }
222 
223     resume();
224 }
225 
help(const char * argv0)226 static void help(const char *argv0) {
227 
228     printf(_("%s [options] -- PROGRAM [ARGUMENTS ...]\n\n"
229            "Temporarily suspend PulseAudio while PROGRAM runs.\n\n"
230            "  -h, --help                            Show this help\n"
231            "      --version                         Show version\n"
232            "  -s, --server=SERVER                   The name of the server to connect to\n\n"),
233            argv0);
234 }
235 
236 enum {
237     ARG_VERSION = 256
238 };
239 
main(int argc,char * argv[])240 int main(int argc, char *argv[]) {
241     pa_mainloop* m = NULL;
242     int c, ret = 1;
243     char *server = NULL, *bn;
244 
245     static const struct option long_options[] = {
246         {"server",      1, NULL, 's'},
247         {"version",     0, NULL, ARG_VERSION},
248         {"help",        0, NULL, 'h'},
249         {NULL,          0, NULL, 0}
250     };
251 
252     setlocale(LC_ALL, "");
253 #ifdef ENABLE_NLS
254     bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
255 #endif
256 
257     bn = pa_path_get_filename(argv[0]);
258 
259     while ((c = getopt_long(argc, argv, "s:h", long_options, NULL)) != -1) {
260         switch (c) {
261             case 'h' :
262                 help(bn);
263                 ret = 0;
264                 goto quit;
265 
266             case ARG_VERSION:
267                 printf(_("pasuspender %s\n"
268                          "Compiled with libpulse %s\n"
269                          "Linked with libpulse %s\n"),
270                        PACKAGE_VERSION,
271                        pa_get_headers_version(),
272                        pa_get_library_version());
273                 ret = 0;
274                 goto quit;
275 
276             case 's':
277                 pa_xfree(server);
278                 server = pa_xstrdup(optarg);
279                 break;
280 
281             default:
282                 goto quit;
283         }
284     }
285 
286     child_argv = argv + optind;
287     child_argc = argc - optind;
288 
289     if (child_argc <= 0) {
290         help(bn);
291         ret = 0;
292         goto quit;
293     }
294 
295     if (!(m = pa_mainloop_new())) {
296         fprintf(stderr, _("pa_mainloop_new() failed.\n"));
297         goto quit;
298     }
299 
300     pa_assert_se(mainloop_api = pa_mainloop_get_api(m));
301     pa_assert_se(pa_signal_init(mainloop_api) == 0);
302     pa_signal_new(SIGINT, sigint_callback, NULL);
303     pa_signal_new(SIGCHLD, sigchld_callback, NULL);
304 #ifdef SIGPIPE
305     signal(SIGPIPE, SIG_IGN);
306 #endif
307 
308     if (!(context = pa_context_new(mainloop_api, bn))) {
309         fprintf(stderr, _("pa_context_new() failed.\n"));
310         goto quit;
311     }
312 
313     pa_context_set_state_callback(context, context_state_callback, NULL);
314 
315     if (pa_context_connect(context, server, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) {
316         fprintf(stderr, "pa_context_connect() failed: %s\n", pa_strerror(pa_context_errno(context)));
317         goto quit;
318     }
319 
320     if (pa_mainloop_run(m, &ret) < 0) {
321         fprintf(stderr, _("pa_mainloop_run() failed.\n"));
322         goto quit;
323     }
324 
325     if (ret == 0 && fork_failed)
326         ret = 1;
327 
328 quit:
329     if (context)
330         pa_context_unref(context);
331 
332     if (m) {
333         pa_signal_done();
334         pa_mainloop_free(m);
335     }
336 
337     pa_xfree(server);
338 
339     if (!dead)
340         kill(child_pid, SIGTERM);
341 
342     return ret == 0 ? child_ret : ret;
343 }
344