</varlistentry>
<varlistentry>
- <term><option>SYSFS_DEVICE</option></term>
+ <term><option>CONFIG{sysfs_device}</option></term>
<listitem>
<para>The relative path to sysfs subsystem specifying
the root directory of a soundcard device. Usually,
<listitem>
<para>Match sysfs attribute values of the soundcard device.
The relative path to sysfs tree must be defined by
- SYSFS_DEVICE key. Trailing whitespace in the attribute
+ CONFIG{sysfs_device} key. Trailing whitespace in the attribute
values is ignored, if the specified match value does
not contain trailing whitespace itself. Depending on
the type of operator, this key is also used to set
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>RESULT</option></term>
+ <listitem>
+ <para>Set RESULT variable. Note that PROGRAM also sets
+ this variable, but setting this variable manually
+ might be useful to change code execution order (included
+ files).</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>LABEL</option></term>
<listitem>
</varlistentry>
<varlistentry>
- <term><option>SYSFS_DEVICE</option></term>
+ <term><option>INCLUDE</option></term>
+ <listitem>
+ <para>Include specified filename or all files in specified directory</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>ACCESS</option></term>
+ <listitem>
+ <para>Check if specified file or directory exists</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>CONFIG{sysfs_device}</option></term>
<listitem>
<para>The relative path to sysfs subsystem specifying
the root directory of a soundcard device. Usually,
<varlistentry>
<term><option>EXIT</option></term>
<listitem>
- <para>Exit immediately and set program exit code to value (should be integer).</para>
+ <para>Exit immediately and set program exit code to value
+ (should be integer). If value is "return" string,
+ parser leaves current included file and returns to parent
+ configuration file.</para>
</listitem>
</varlistentry>
</variablelist>
- <para>The <option>PROGRAM</option>, <option>CTL{value}</option>,
+ <para>The <option>PROGRAM</option>, <option>RESULT</option>,
+ <option>CTL{value}</option>,
<option>PRINT</option>, <option>ERROR</option>,
- <option>EXIT</option>, <option>SYSFS_DEVICE</option>
+ <option>EXIT</option>, <option>CONFIG{}</option>
fields support simple printf-like string substitutions.
It allows the use of the complete environment set by earlier matching
rules. For all other fields, substitutions are applied while the individual rule is
# See 'man alsactl_init' for syntax.
# set root device directory in sysfs for soundcard for ATTR{} command
-SYSFS_DEVICE="/class/sound/controlC$cardinfo{card}/device"
+CONFIG{sysfs_device}="/class/sound/controlC$cardinfo{card}/device"
# test for extra commands
ENV{CMD}=="help", INCLUDE="help", GOTO="00main_end"
ENV{CMD}=="*", ERROR="Unknown command '$env{CMD}'\n", GOTO="00main_end"
# include files with real configuration
-CARDINFO{driver}=="HDA-Intel", INCLUDE="hda", GOTO="00main_end"
-CARDINFO{driver}=="Test", INCLUDE="test", GOTO="00main_end"
+#
+# steps are:
+# 1) look for preinit subdirectory and parse all files in it
+# 2) if RESULT=="skip", skip ALSA standard configuration files
+# 3) do ALSA standard configuration
+# 4) look for postinit subdirectory and parse all files in it
+# 5) if RESULT!="true", print an error message and return with exit code 99
+# 6) return with exit code 0 (success)
+#
+
+RESULT="unknown"
+ACCESS=="preinit", INCLUDE="preinit"
+RESULT=="skip", GOTO="init_end"
+
+# real ALSA configuration database
+CARDINFO{driver}=="HDA-Intel", INCLUDE="hda", GOTO="init_end"
+CARDINFO{driver}=="Test", INCLUDE="test", GOTO="init_end"
+
+LABEL="init_end"
+ACCESS=="postinit", INCLUDE="postinit"
+RESULT=="true", GOTO="00_mainend"
+ERROR="Unknown hardware: \"$cardinfo{driver}\" \"$cardinfo{mixername}\" \"$cardinfo{components}\" \"$attr{subsystem_vendor}\" \"$attr{subsystem_device}\"\n"
+ERROR="Hardware is left uninitialized\n"
+EXIT="99"
+
+#
+# label identifying end of main file
+#
+
LABEL="00main_end"
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/select.h>
+#include <sys/types.h>
+#include <dirent.h>
#include <alsa/asoundlib.h>
#include "aconfig.h"
#include "alsactl.h"
#define PATH_SIZE 512
#define NAME_SIZE 128
+#define EJUSTRETURN 0x7fffffff
enum key_op {
KEY_OP_UNSET,
const char *value = NULL;
size_t size;
- pair = value_find(space, "SYSFS_DEVICE");
+ pair = value_find(space, "sysfs_device");
if (pair == NULL)
break;
value = sysfs_attr_get_value(pair->value, attr);
if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
if (!do_match(key, op, value, space->program_result))
break;
- } else {
+ } else if (op == KEY_OP_ASSIGN) {
+ if (space->program_result) {
+ free(space->program_result);
+ space->program_result = NULL;
+ }
+ strlcpy(string, value, sizeof(string));
+ apply_format(space, string, sizeof(string));
+ space->program_result = strdup(string);
+ if (space->program_result == NULL)
+ break;
+ } else {
Perror(space, "invalid RESULT operation");
goto invalid;
}
goto invalid;
}
if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
- pair = value_find(space, "SYSFS_DEVICE");
+ pair = value_find(space, "sysfs_device");
if (pair == NULL)
break;
dbg("sysfs_attr: '%s' '%s'", pair->value, attr);
if (strcasecmp(key, "INCLUDE") == 0) {
char *rootdir, *go_to;
const char *filename;
+ struct dirent *dirent;
+ DIR *dir;
int linenum;
if (op != KEY_OP_ASSIGN) {
Perror(space, "invalid INCLUDE operation");
go_to = space->go_to;
filename = space->filename;
linenum = space->linenum;
- space->go_to = NULL;
- space->rootdir = new_root_dir(string);
- if (space->rootdir) {
- err = parse(space, string);
- free(space->rootdir);
- } else
- err = -ENOMEM;
- if (space->go_to) {
- Perror(space, "unterminated GOTO '%s'", space->go_to);
- free(space->go_to);
+ dir = opendir(string);
+ if (dir) {
+ count = strlen(string);
+ while ((dirent = readdir(dir)) != NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0)
+ continue;
+ string[count] = '\0';
+ strlcat(string, "/", sizeof(string));
+ strlcat(string, dirent->d_name, sizeof(string));
+ space->go_to = NULL;
+ space->rootdir = new_root_dir(string);
+ if (space->rootdir) {
+ err = parse(space, string);
+ free(space->rootdir);
+ } else
+ err = -ENOMEM;
+ if (space->go_to) {
+ Perror(space, "unterminated GOTO '%s'", space->go_to);
+ free(space->go_to);
+ }
+ }
+ closedir(dir);
+ } else {
+ space->go_to = NULL;
+ space->rootdir = new_root_dir(string);
+ if (space->rootdir) {
+ err = parse(space, string);
+ free(space->rootdir);
+ } else
+ err = -ENOMEM;
+ if (space->go_to) {
+ Perror(space, "unterminated GOTO '%s'", space->go_to);
+ free(space->go_to);
+ }
}
space->go_to = go_to;
space->rootdir = rootdir;
break;
continue;
}
+ if (strncasecmp(key, "ACCESS", 6) == 0) {
+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ if (value[0] != '/') {
+ strlcpy(string, space->rootdir, sizeof(string));
+ strlcat(string, "/", sizeof(string));
+ strlcat(string, value, sizeof(string));
+ } else {
+ strlcat(string, value, sizeof(string));
+ }
+ count = access(string, F_OK);
+ dbg("access(%s) = %i", value, count);
+ if (op == KEY_OP_MATCH && count != 0)
+ break;
+ if (op == KEY_OP_NOMATCH && count == 0)
+ break;
+ } else {
+ Perror(space, "invalid ACCESS operation");
+ goto invalid;
+ }
+ continue;
+ }
if (strncasecmp(key, "PRINT", 5) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid PRINT operation");
+ goto invalid;
+ }
strlcpy(string, value, sizeof(string));
apply_format(space, string, sizeof(string));
fwrite(string, strlen(string), 1, stdout);
continue;
}
if (strncasecmp(key, "ERROR", 5) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid ERROR operation");
+ goto invalid;
+ }
strlcpy(string, value, sizeof(string));
apply_format(space, string, sizeof(string));
fwrite(string, strlen(string), 1, stderr);
continue;
}
if (strncasecmp(key, "EXIT", 4) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid EXIT operation");
+ goto invalid;
+ }
strlcpy(string, value, sizeof(string));
apply_format(space, string, sizeof(string));
+ if (strcmp(string, "return") == 0)
+ return -EJUSTRETURN;
space->exit_code = strtol(string, NULL, 0);
space->quit = 1;
break;
}
- if (strncasecmp(key, "SYSFS_DEVICE", 12) == 0) {
- strlcpy(string, value, sizeof(string));
- apply_format(space, string, sizeof(string));
- err = value_set(space, key, string);
- dbg("SYSFS_DEVICE='%s'", string);
- break;
+ if (strncasecmp(key, "CONFIG{", 7) == 0) {
+ attr = get_key_attribute(space, key + 6, string, sizeof(string));
+ if (attr == NULL) {
+ Perror(space, "error parsing CONFIG attribute");
+ goto invalid;
+ }
+ strlcpy(result, value, sizeof(result));
+ apply_format(space, result, sizeof(result));
+ if (op == KEY_OP_ASSIGN) {
+ err = value_set(space, attr, result);
+ dbg("CONFIG{%s}='%s'", attr, result);
+ break;
+ } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ pair = value_find(space, attr);
+ if (pair == NULL)
+ break;
+ if (!do_match(key, op, result, pair->value))
+ break;
+ } else {
+ Perror(space, "invalid CONFIG{} operation");
+ goto invalid;
+ }
}
Perror(space, "unknown key '%s'", key);
dbg("read (%i) '%s'", linenum, line);
space->linenum = linenum;
err = parse_line(space, line, linesize);
+ if (err == -EJUSTRETURN) {
+ err = 0;
+ break;
+ }
linenum += linenum_adj;
}