make alsa/ardour dbus-device request tool generally useful.

This commit is contained in:
Robin Gareus 2014-06-05 18:02:43 +02:00
parent 8d6183bff5
commit de6a1ab701

View file

@ -1,4 +1,5 @@
/* /* alsa/ardour dbus device request tool
*
* Copyright (C) 2014 Robin Gareus <robin@gareus.org> * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
* *
* This program is free software; you can redistribute it and/or modify * 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. * 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 <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <getopt.h>
#include <signal.h> #include <signal.h>
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
@ -27,6 +32,8 @@
#include "ardouralsautil/reserve.h" #include "ardouralsautil/reserve.h"
static int run = 1; static int run = 1;
static int release_wait_for_signal = 0;
static pid_t parent_pid = 0;
static void wearedone(int sig) { static void wearedone(int sig) {
(void) sig; // skip 'unused variable' compiler warning; (void) sig; // skip 'unused variable' compiler warning;
@ -40,21 +47,45 @@ static int stdin_available(void) {
return errno != EBADF; return errno != EBADF;
} }
static void print_version(int status) {
printf ("ardour-request-device 0.2\n\n");
printf (
"Copyright (C) 2014 Robin Gareus <robin@gareus.org>\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) { static void usage(int status) {
printf ("ardour-request-device - DBus Audio Reservation Utility.\n"); printf ("ardour-request-device - DBus Audio Reservation Utility.\n");
printf ("Usage: ardour-request-device [ OPTIONS ] <Audio-Device-ID>\n"); printf ("Usage: ardour-request-device [ OPTIONS ] <Audio-Device-ID>\n");
printf ("Options:\n\ printf ("Options:\n\
-h, --help display this help and exit\n\ -h, --help display this help and exit\n\
-p, --priority <int> reservation priority (default: int32_max)\n\
-P, --pid <int> process-id to watch (default 0: none)\n\
-n, --name <string> application name to use for registration\n\
-V, --version print version information and exit\n\ -V, --version print version information and exit\n\
-w, --releasewait wait for signal on yield-release\n\
"); ");
printf ("\n\ printf ("\n\
This tool issues a dbus request to reserve an ALSA Audio-device.\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\ 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\ \n\
ardour-request-device announces itself as \"Ardour ALSA Backend\" and uses the\n\ ardour-request-device by default announces itself as \"Ardour ALSA Backend\"\n\
maximum possible priority for requesting the device.\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\ \n\
The audio-device-id is a string e.g. 'Audio1'\n\ The audio-device-id is a string e.g. 'Audio1'\n\
\n\ \n\
@ -66,39 +97,107 @@ ardour-request-device Audio0\n\
exit (status); exit (status);
} }
static void print_version(void) { static struct option const long_options[] =
printf ("ardour-request-device 0.1\n\n"); {
printf ( {"help", no_argument, 0, 'h'},
"Copyright (C) 2014 Robin Gareus <robin@gareus.org>\n" {"name", required_argument, 0, 'n'},
"This is free software; see the source for copying conditions. There is NO\n" {"pid", required_argument, 0, 'P'},
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n" {"priority", required_argument, 0, 'p'},
); {"version", no_argument, 0, 'V'},
exit (0); {"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) { int main(int argc, char **argv) {
DBusConnection* dbus_connection = NULL; DBusConnection* dbus_connection = NULL;
rd_device * reserved_device = NULL; rd_device * reserved_device = NULL;
DBusError error; 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); usage(EXIT_FAILURE);
} }
if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { const char *device_name = argv[optind];
print_version();
}
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
usage(EXIT_SUCCESS);
}
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); dbus_error_init(&error);
if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &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."); fprintf(stderr, "Failed to connect to session bus for device reservation: %s\n", error.message ? error.message : "unknown error.");
dbus_error_free(&error); dbus_error_free(&error);
free(name);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -106,14 +205,15 @@ int main(int argc, char **argv) {
&reserved_device, &reserved_device,
dbus_connection, dbus_connection,
device_name, device_name,
"Ardour ALSA Backend", name,
INT32_MAX, priority,
NULL, request_cb,
&error)) < 0) &error)) < 0)
{ {
fprintf(stderr, "Failed to acquire device: '%s'\n%s\n", device_name, (error.message ? error.message : strerror(-ret))); fprintf(stderr, "Failed to acquire device: '%s'\n%s\n", device_name, (error.message ? error.message : strerror(-ret)));
dbus_error_free(&error); dbus_error_free(&error);
dbus_connection_unref(dbus_connection); dbus_connection_unref(dbus_connection);
free(name);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -126,14 +226,20 @@ int main(int argc, char **argv) {
while (run && dbus_connection_read_write_dispatch (dbus_connection, 200)) { while (run && dbus_connection_read_write_dispatch (dbus_connection, 200)) {
if (!stdin_available()) { 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; break;
} }
} }
rd_release (reserved_device); rd_release (reserved_device);
fprintf(stdout, "Released audio-card '%s'\n", device_name); fprintf(stdout, "Released audio-card '%s'\n", device_name);
dbus_error_free(&error);
dbus_connection_unref(dbus_connection); dbus_connection_unref(dbus_connection);
dbus_error_free(&error);
free(name);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }