From de6a1ab701c6249ea04ea98465c09070fd8cc7a2 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Thu, 5 Jun 2014 18:02:43 +0200 Subject: [PATCH] make alsa/ardour dbus-device request tool generally useful. --- libs/ardouralsautil/request_device.c | 158 ++++++++++++++++++++++----- 1 file changed, 132 insertions(+), 26 deletions(-) diff --git a/libs/ardouralsautil/request_device.c b/libs/ardouralsautil/request_device.c index 9e929c3cd0..de3fde44a1 100644 --- a/libs/ardouralsautil/request_device.c +++ b/libs/ardouralsautil/request_device.c @@ -1,4 +1,5 @@ -/* +/* alsa/ardour dbus device request tool + * * Copyright (C) 2014 Robin Gareus * * This program is free software; you can redistribute it and/or modify @@ -16,9 +17,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +// NB generate man-page with +// help2man -N -n "alsa/ardour dbus device request tool" -o ardour-request-device.1 ./build/libs/ardouralsautil/ardour-request-device + #include #include #include +#include #include #include #include @@ -27,6 +32,8 @@ #include "ardouralsautil/reserve.h" static int run = 1; +static int release_wait_for_signal = 0; +static pid_t parent_pid = 0; static void wearedone(int sig) { (void) sig; // skip 'unused variable' compiler warning; @@ -40,21 +47,45 @@ static int stdin_available(void) { return errno != EBADF; } +static void print_version(int status) { + printf ("ardour-request-device 0.2\n\n"); + printf ( + "Copyright (C) 2014 Robin Gareus \n" + "This is free software; see the source for copying conditions. There is NO\n" + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n" + ); + exit (status); +} + static void usage(int status) { printf ("ardour-request-device - DBus Audio Reservation Utility.\n"); printf ("Usage: ardour-request-device [ OPTIONS ] \n"); printf ("Options:\n\ -h, --help display this help and exit\n\ + -p, --priority reservation priority (default: int32_max)\n\ + -P, --pid process-id to watch (default 0: none)\n\ + -n, --name application name to use for registration\n\ -V, --version print version information and exit\n\ + -w, --releasewait wait for signal on yield-release\n\ "); printf ("\n\ This tool issues a dbus request to reserve an ALSA Audio-device.\n\ If successful other users of the device (e.g. pulseaudio) will\n\ -release the device so that ardour can access it.\n\ +release the device.\n\ \n\ -ardour-request-device announces itself as \"Ardour ALSA Backend\" and uses the\n\ -maximum possible priority for requesting the device.\n\ +ardour-request-device by default announces itself as \"Ardour ALSA Backend\"\n\ +and uses the maximum possible priority for requesting the device.\n\ +These settings can be overriden using the -n and -p options respectively.\n\ +\n\ +If a PID is given the tool will watch the process and if that is not running\n\ +release the device and exit. Otherwise ardour-request-device runs until\n\ +either stdin is closed, a SIGINT or SIGTERM is received or some other\n\ +application requests the device with a higher priority.\n\ +\n\ +Without the -w option, ardour-request-device yields the device after 500ms to\n\ +any higher-priority request. With the -w option this tool waits until it\n\ +for SIGINT or SIGTERM - but at most 4 sec to acknowledge before releasing.\n\ \n\ The audio-device-id is a string e.g. 'Audio1'\n\ \n\ @@ -66,39 +97,107 @@ ardour-request-device Audio0\n\ exit (status); } -static void print_version(void) { - printf ("ardour-request-device 0.1\n\n"); - printf ( - "Copyright (C) 2014 Robin Gareus \n" - "This is free software; see the source for copying conditions. There is NO\n" - "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n" - ); - exit (0); +static struct option const long_options[] = +{ + {"help", no_argument, 0, 'h'}, + {"name", required_argument, 0, 'n'}, + {"pid", required_argument, 0, 'P'}, + {"priority", required_argument, 0, 'p'}, + {"version", no_argument, 0, 'V'}, + {"releasewait", no_argument, 0, 'w'}, + {NULL, 0, NULL, 0} +}; + +static int request_cb(rd_device *d, int forced) { + (void) d; // skip 'unused variable' compiler warning; + (void) forced; // skip 'unused variable' compiler warning; + fprintf(stdout, "Received higher priority request - releasing device.\n"); + fflush(stdout); + if(!release_wait_for_signal) { + usleep (500000); + run = 0; + } else if (run) { + int timeout = 4000; + fprintf(stdout, "Waiting for acknowledge signal to release.\n"); + while (release_wait_for_signal && run && --timeout) { + if (!stdin_available()) { + break; + } + if (parent_pid > 0 && kill (parent_pid, 0)) { + break; + } + usleep (1000); + } + run = 0; + } + return 1; // OK } int main(int argc, char **argv) { DBusConnection* dbus_connection = NULL; rd_device * reserved_device = NULL; DBusError error; - int ret; + int ret, c; - if (argc < 2 || argc > 2) { + int32_t priority = INT32_MAX; + char *name = strdup("Ardour ALSA Backend"); + + while ((c = getopt_long (argc, argv, + "h" /* help */ + "n:" /* name */ + "P:" /* pid */ + "p:" /* priority */ + "V" /* version */ + "w", /* release wait for signal */ + long_options, (int *) 0)) != EOF) + { + switch (c) { + case 'h': + free(name); + usage(EXIT_SUCCESS); + break; + case 'n': + free(name); + name = strdup(optarg); + break; + case 'p': + priority = atoi (optarg); + if (priority < 0) priority = 0; + break; + case 'P': + parent_pid = atoi (optarg); + break; + case 'V': + free(name); + print_version(EXIT_SUCCESS); + break; + case 'w': + release_wait_for_signal = 1; + break; + default: + free(name); + usage(EXIT_FAILURE); + break; + } + } + + if (optind + 1 != argc) { + free(name); usage(EXIT_FAILURE); } - if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { - print_version(); - } - if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { - usage(EXIT_SUCCESS); - } + const char *device_name = argv[optind]; - const char *device_name = argv[1]; + if (parent_pid > 0 && kill (parent_pid, 0)) { + fprintf(stderr, "Given PID to watch is not running.\n"); + return EXIT_FAILURE; + } dbus_error_init(&error); if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &error))) { fprintf(stderr, "Failed to connect to session bus for device reservation: %s\n", error.message ? error.message : "unknown error."); dbus_error_free(&error); + free(name); return EXIT_FAILURE; } @@ -106,14 +205,15 @@ int main(int argc, char **argv) { &reserved_device, dbus_connection, device_name, - "Ardour ALSA Backend", - INT32_MAX, - NULL, + name, + priority, + request_cb, &error)) < 0) { fprintf(stderr, "Failed to acquire device: '%s'\n%s\n", device_name, (error.message ? error.message : strerror(-ret))); dbus_error_free(&error); dbus_connection_unref(dbus_connection); + free(name); return EXIT_FAILURE; } @@ -126,14 +226,20 @@ int main(int argc, char **argv) { while (run && dbus_connection_read_write_dispatch (dbus_connection, 200)) { if (!stdin_available()) { - fprintf(stderr, "stdin closed - shutting down.\n"); + fprintf(stderr, "stdin closed - releasing device.\n"); + break; + } + if (parent_pid > 0 && kill (parent_pid, 0)) { + fprintf(stderr, "watched PID no longer exists - releasing device.\n"); break; } } rd_release (reserved_device); fprintf(stdout, "Released audio-card '%s'\n", device_name); - dbus_error_free(&error); + dbus_connection_unref(dbus_connection); + dbus_error_free(&error); + free(name); return EXIT_SUCCESS; }