switch-on-connect: Add blacklisting
authorRyszard Knop <dragoon@dragonic.eu>
Sun, 17 Nov 2019 14:24:03 +0000 (15:24 +0100)
committerRyszard Knop <dragoon@dragonic.eu>
Thu, 21 Nov 2019 21:19:44 +0000 (22:19 +0100)
Add a new module argument, blacklist, which is a regular expression.
If the sink/source name matches the provided blacklist regex, don't
automatically switch to it. By default, no devices are blacklisted.

Add a new function to check whenever a regex pattern is valid, plus
extra NULL asserts in pa_match.

src/modules/module-switch-on-connect.c
src/pulsecore/core-util.c
src/pulsecore/core-util.h

index 9077eae..5ebe0f4 100644 (file)
@@ -40,17 +40,20 @@ PA_MODULE_LOAD_ONCE(true);
 PA_MODULE_USAGE(
         "only_from_unavailable=<boolean, only switch from unavailable ports> "
         "ignore_virtual=<boolean, ignore new virtual sinks and sources, defaults to true> "
+        "blacklist=<regex, ignore matching devices> "
 );
 
 static const char* const valid_modargs[] = {
     "only_from_unavailable",
     "ignore_virtual",
+    "blacklist",
     NULL,
 };
 
 struct userdata {
     bool only_from_unavailable;
     bool ignore_virtual;
+    char *blacklist;
 };
 
 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
@@ -80,6 +83,12 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void*
         }
     }
 
+    /* Ignore sinks matching the blacklist regex */
+    if (u->blacklist && (pa_match(u->blacklist, sink->name) > 0)) {
+        pa_log_info("Refusing to switch to blacklisted sink %s", sink->name);
+        return PA_HOOK_OK;
+    }
+
     /* Ignore virtual sinks if not configured otherwise on the command line */
     if (u->ignore_virtual && !(sink->flags & PA_SINK_HARDWARE)) {
         pa_log_debug("Refusing to switch to virtual sink");
@@ -157,6 +166,12 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
         return PA_HOOK_OK;
     }
 
+    /* Ignore sources matching the blacklist regex */
+    if (u->blacklist && (pa_match(u->blacklist, source->name) > 0)) {
+        pa_log_info("Refusing to switch to blacklisted source %s", source->name);
+        return PA_HOOK_OK;
+    }
+
     /* Ignore virtual sources if not configured otherwise on the command line */
     if (u->ignore_virtual && !(source->flags & PA_SOURCE_HARDWARE)) {
         pa_log_debug("Refusing to switch to virtual source");
@@ -234,6 +249,15 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    u->blacklist = pa_modargs_get_value(ma, "blacklist", NULL);
+    if (u->blacklist != NULL && pa_is_regex_valid(u->blacklist)) {
+        /* String returned above will be freed with modargs, duplicate it */
+        u->blacklist = pa_xstrdup(u->blacklist);
+    } else if (u->blacklist != NULL) {
+        pa_log_error("A blacklist pattern was provided but is not a valid regex.");
+        goto fail;
+    }
+
     pa_modargs_free(ma);
     return 0;
 
@@ -254,5 +278,8 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         return;
 
+    if (u->blacklist)
+        pa_xfree(u->blacklist);
+
     pa_xfree(u);
 }
index 270acd8..f5ec67b 100644 (file)
@@ -784,12 +784,16 @@ void pa_reset_priority(void) {
 #endif
 }
 
+/* Check whenever any substring in v matches the provided regex. */
 int pa_match(const char *expr, const char *v) {
 #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
     int k;
     regex_t re;
     int r;
 
+    pa_assert(expr);
+    pa_assert(v);
+
     if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) {
         errno = EINVAL;
         return -1;
@@ -814,6 +818,22 @@ int pa_match(const char *expr, const char *v) {
 #endif
 }
 
+/* Check whenever the provided regex pattern is valid. */
+bool pa_is_regex_valid(const char *expr) {
+#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
+    regex_t re;
+
+    if (expr == NULL || regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) {
+        return false;
+    }
+
+    regfree(&re);
+    return true;
+#else
+    return false;
+#endif
+}
+
 /* Try to parse a boolean string value.*/
 int pa_parse_boolean(const char *v) {
     pa_assert(v);
index cfb9eb2..d1c4ae1 100644 (file)
@@ -158,6 +158,7 @@ size_t pa_vsnprintf(char *str, size_t size, const char *format, va_list ap);
 char *pa_truncate_utf8(char *c, size_t l);
 
 int pa_match(const char *expr, const char *v);
+bool pa_is_regex_valid(const char *expr);
 
 char *pa_getcwd(void);
 char *pa_make_path_absolute(const char *p);