switch-on-connect: Add blacklisting

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.
This commit is contained in:
Ryszard Knop 2019-11-17 15:24:03 +01:00
parent c90894b4d5
commit ad16d77dfe
3 changed files with 48 additions and 0 deletions

View file

@ -40,17 +40,20 @@ PA_MODULE_LOAD_ONCE(true);
PA_MODULE_USAGE( PA_MODULE_USAGE(
"only_from_unavailable=<boolean, only switch from unavailable ports> " "only_from_unavailable=<boolean, only switch from unavailable ports> "
"ignore_virtual=<boolean, ignore new virtual sinks and sources, defaults to true> " "ignore_virtual=<boolean, ignore new virtual sinks and sources, defaults to true> "
"blacklist=<regex, ignore matching devices> "
); );
static const char* const valid_modargs[] = { static const char* const valid_modargs[] = {
"only_from_unavailable", "only_from_unavailable",
"ignore_virtual", "ignore_virtual",
"blacklist",
NULL, NULL,
}; };
struct userdata { struct userdata {
bool only_from_unavailable; bool only_from_unavailable;
bool ignore_virtual; bool ignore_virtual;
char *blacklist;
}; };
static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, void* userdata) { 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 */ /* Ignore virtual sinks if not configured otherwise on the command line */
if (u->ignore_virtual && !(sink->flags & PA_SINK_HARDWARE)) { if (u->ignore_virtual && !(sink->flags & PA_SINK_HARDWARE)) {
pa_log_debug("Refusing to switch to virtual sink"); 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; 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 */ /* Ignore virtual sources if not configured otherwise on the command line */
if (u->ignore_virtual && !(source->flags & PA_SOURCE_HARDWARE)) { if (u->ignore_virtual && !(source->flags & PA_SOURCE_HARDWARE)) {
pa_log_debug("Refusing to switch to virtual source"); pa_log_debug("Refusing to switch to virtual source");
@ -234,6 +249,15 @@ int pa__init(pa_module*m) {
goto fail; 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); pa_modargs_free(ma);
return 0; return 0;
@ -254,5 +278,8 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata)) if (!(u = m->userdata))
return; return;
if (u->blacklist)
pa_xfree(u->blacklist);
pa_xfree(u); pa_xfree(u);
} }

View file

@ -784,12 +784,16 @@ void pa_reset_priority(void) {
#endif #endif
} }
/* Check whenever any substring in v matches the provided regex. */
int pa_match(const char *expr, const char *v) { int pa_match(const char *expr, const char *v) {
#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
int k; int k;
regex_t re; regex_t re;
int r; int r;
pa_assert(expr);
pa_assert(v);
if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) { if (regcomp(&re, expr, REG_NOSUB|REG_EXTENDED) != 0) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
@ -814,6 +818,22 @@ int pa_match(const char *expr, const char *v) {
#endif #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.*/ /* Try to parse a boolean string value.*/
int pa_parse_boolean(const char *v) { int pa_parse_boolean(const char *v) {
pa_assert(v); pa_assert(v);

View 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); char *pa_truncate_utf8(char *c, size_t l);
int pa_match(const char *expr, const char *v); int pa_match(const char *expr, const char *v);
bool pa_is_regex_valid(const char *expr);
char *pa_getcwd(void); char *pa_getcwd(void);
char *pa_make_path_absolute(const char *p); char *pa_make_path_absolute(const char *p);