Remove LocaleGuard from ARDOUR::IO class state methods
[ardour.git] / libs / ardouralsautil / request_device.c
index 9e929c3cd05dbf44f2bc0bc06e7bdc3b605dfcfd..3fff33bf137b64c7b6c29ccb2996ab296a74dca7 100644 (file)
@@ -1,4 +1,5 @@
-/*
+/* alsa/ardour dbus device request tool
+ *
  * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * 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 <stdlib.h>
 #include <unistd.h>
+#include <getopt.h>
 #include <signal.h>
 #include <string.h>
 #include <fcntl.h>
 
 #include "ardouralsautil/reserve.h"
 
+#ifndef ARD_PROG_NAME
+#define ARD_PROG_NAME "alsa_request_device"
+#endif
+#ifndef ARD_APPL_NAME
+#define ARD_APPL_NAME "ALSA User"
+#endif
+#ifndef VERSION
+#define VERSION "v0.3"
+#endif
+
 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,65 +57,158 @@ static int stdin_available(void) {
        return errno != EBADF;
 }
 
+static void print_version(int status) {
+       printf (ARD_PROG_NAME " " VERSION "\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) {
-       printf ("ardour-request-device - DBus Audio Reservation Utility.\n");
-       printf ("Usage: ardour-request-device [ OPTIONS ] <Audio-Device-ID>\n");
+       printf (ARD_PROG_NAME " - DBus Audio Reservation Utility.\n");
+       printf ("Usage: " ARD_PROG_NAME " [ OPTIONS ] <Audio-Device-ID>\n");
        printf ("Options:\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\
+      -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\
+" ARD_PROG_NAME " by default announces itself as \"" ARD_APPL_NAME "\"\n\
+and uses the maximum possible priority for requesting the device.\n\
+These settings can be overridden 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 " ARD_PROG_NAME " 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, " ARD_PROG_NAME " 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\
 Examples:\n\
-ardour-request-device Audio0\n\
+" ARD_PROG_NAME " Audio0\n\
 \n");
 
        printf ("Report bugs to Robin Gareus <robin@gareus.org>\n");
        exit (status);
 }
 
-static void print_version(void) {
-       printf ("ardour-request-device 0.1\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 (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) {
-               usage(EXIT_FAILURE);
-       }
-       if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
-               print_version();
+       int32_t priority = INT32_MAX;
+       char *name = strdup(ARD_APPL_NAME);
+
+       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 (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
-               usage(EXIT_SUCCESS);
+
+       if (optind + 1 != argc) {
+               free(name);
+               usage(EXIT_FAILURE);
        }
+       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");
+               free(name);
+               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 +216,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 +237,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;
 }