[pulseaudio-discuss] [PATCH] Run a user specified program on suspend/resume.
David Henningsson
david.henningsson at canonical.com
Mon Dec 7 01:36:20 PST 2015
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
More information about the pulseaudio-discuss
mailing list