Hi! Sorry for the late reply. When starting child processes, there is a helper function in src/pulsecore/start-child.c that you can use. It should cover the platform and security questions you're having. (Or at least put the ball back into our ballpark. :-) ) That said, about the general concept of launching an executable on suspend/resume...it seems useful to me, but should such an executable be blocking or non-blocking (i e, resume/suspend continuing in parallel, or waiting for the executable to finish)? What do others think? On 2015-08-21 22:29, Joonas Govenius wrote: > This allows calling a user-specified external program whenever a > source or sink is suspended or resumed (by specifying > external_suspend_handler="<path_to_executable>" as an option to > module-suspend-on-idle). The external executable is called > with "--suspended" or "--resumed" followed by "--source > <source_name>" or "--sink <sink_name>". > > My use case for this feature is to cut the power to my active > speakers in order to eliminate hissing. I've been using the patch > (applied to pulseaudio 4.0) on Ubuntu 14.04 since February. I > have not tested it with later versions, except to check that it > compiles. I could do some further testing if the patch is > otherwise acceptable/useful enough. > > Some things I'm not sure about: > > * What happens on Windows? Does fork() work and if not, what does > it return? Maybe some of the code should be wrapped > with "#ifndef OS_IS_WIN32". > > * Security considerations? This might provide a sneaky way to run > malicious code repeatedly, but only if you have write access to > the config file. In that case you are probably screwed in a > multitude of ways already... > > * What would be the correct place to document the new > option. Maybe > http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Modules/#index62h3 > ? That seems to be the right place IMO. > > --- > src/modules/module-suspend-on-idle.c | 35 ++++++++++++++++++++++++++++++++++- > 1 file changed, 34 insertions(+), 1 deletion(-) > > diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c > index f7620db..f21c7cc 100644 > --- a/src/modules/module-suspend-on-idle.c > +++ b/src/modules/module-suspend-on-idle.c > @@ -21,6 +21,8 @@ > #include <config.h> > #endif > > +#include <unistd.h> > + > #include <pulse/xmalloc.h> > #include <pulse/timeval.h> > #include <pulse/rtclock.h> > @@ -38,10 +40,11 @@ PA_MODULE_AUTHOR("Lennart Poettering"); > PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it"); > PA_MODULE_VERSION(PACKAGE_VERSION); > PA_MODULE_LOAD_ONCE(true); > -PA_MODULE_USAGE("timeout=<timeout>"); > +PA_MODULE_USAGE("timeout=<timeout>, external_suspend_handler=<executable called whenever a sink/source is suspended/resumed>"); > > static const char* const valid_modargs[] = { > "timeout", > + "external_suspend_handler", > NULL, > }; > > @@ -49,6 +52,7 @@ struct userdata { > pa_core *core; > pa_usec_t timeout; > pa_hashmap *device_infos; > + const char *external_suspend_handler; > }; > > struct device_info { > @@ -60,6 +64,24 @@ struct device_info { > pa_usec_t timeout; > }; > > +static void call_external_suspend_handler(bool is_suspended, struct device_info *d) { > + /* is_suspended --> whether suspending or resuming */ > + if (d->userdata->external_suspend_handler) { > + const char* c = d->userdata->external_suspend_handler; > + > + pa_log_debug("Calling external %s handler: %s", is_suspended ? "suspend" : "resume", c); > + if (fork() == 0) { > + execl(c, c, is_suspended ? "--suspended" : "--resumed", (d->sink) ? "--sink" : "--source", > + (d->sink) ? d->sink->name : d->source->name, NULL); > + > + pa_log_debug("Failed to execute external suspend/resume handler " > + "(Is the following a valid path to an executable file?): %s", c); > + /* This is normal if the user-specified path is invalid, so terminate the child process quietly. */ > + _exit(3); > + } > + } > +} > + > static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) { > struct device_info *d = userdata; > > @@ -69,12 +91,14 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval > > if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) { > pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name); > + call_external_suspend_handler(true, d); > pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE); > pa_core_maybe_vacuum(d->userdata->core); > } > > if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) { > pa_log_info("Source %s idle for too long, suspending ...", d->source->name); > + call_external_suspend_handler(true, d); > pa_source_suspend(d->source, true, PA_SUSPEND_IDLE); > pa_core_maybe_vacuum(d->userdata->core); > } > @@ -102,11 +126,13 @@ static void resume(struct device_info *d) { > > if (d->sink) { > pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name); > + call_external_suspend_handler(false, d); > pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE); > } > > if (d->source) { > pa_log_debug("Source %s becomes busy, resuming.", d->source->name); > + call_external_suspend_handler(false, d); > pa_source_suspend(d->source, false, PA_SUSPEND_IDLE); > } > } > @@ -439,6 +465,10 @@ int pa__init(pa_module*m) { > u->timeout = timeout * PA_USEC_PER_SEC; > u->device_infos = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_info_free); > > + u->external_suspend_handler = pa_modargs_get_value(ma, "external_suspend_handler", NULL); > + if (u->external_suspend_handler) > + u->external_suspend_handler = pa_xstrdup(u->external_suspend_handler); > + > PA_IDXSET_FOREACH(sink, m->core->sinks, idx) > device_new_hook_cb(m->core, PA_OBJECT(sink), u); > > @@ -486,5 +516,8 @@ void pa__done(pa_module*m) { > > pa_hashmap_free(u->device_infos); > > + if (u->external_suspend_handler) > + pa_xfree((char *) u->external_suspend_handler); > + > pa_xfree(u); > } > -- David Henningsson, Canonical Ltd. https://launchpad.net/~diwic