1/* 2 * Advanced Linux Sound Architecture Control Program - Parse initialization files 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>, 4 * Greg Kroah-Hartman <greg@kroah.com>, 5 * Kay Sievers <kay.sievers@vrfy.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24#include <stdlib.h> 25#include <stdio.h> 26#include <stddef.h> 27#include <unistd.h> 28#include <string.h> 29#include <fcntl.h> 30#include <ctype.h> 31#include <errno.h> 32#include <fnmatch.h> 33#include <sys/stat.h> 34#include <sys/un.h> 35#include <sys/wait.h> 36#include <sys/select.h> 37#include <sys/types.h> 38#include <dirent.h> 39#include <math.h> 40#include "aconfig.h" 41#include "alsactl.h" 42#include "list.h" 43 44#define PATH_SIZE 512 45#define NAME_SIZE 128 46#define EJUSTRETURN 0x7fffffff 47 48enum key_op { 49 KEY_OP_UNSET, 50 KEY_OP_MATCH, 51 KEY_OP_NOMATCH, 52 KEY_OP_ADD, 53 KEY_OP_ASSIGN, 54 KEY_OP_ASSIGN_FINAL 55}; 56 57struct pair { 58 char *key; 59 char *value; 60 struct pair *next; 61}; 62 63struct space { 64 struct pair *pairs; 65 char *rootdir; 66 char *go_to; 67 char *program_result; 68 const char *filename; 69 int linenum; 70 int log_run; 71 int exit_code; 72 int quit; 73 unsigned int ctl_id_changed; 74 snd_hctl_t *ctl_handle; 75 snd_ctl_card_info_t *ctl_card_info; 76 snd_ctl_elem_id_t *ctl_id; 77 snd_ctl_elem_info_t *ctl_info; 78 snd_ctl_elem_value_t *ctl_value; 79}; 80 81static void Perror(struct space *space, const char *fmt, ...) 82{ 83 va_list arg; 84 va_start(arg, fmt); 85 fprintf(stderr, "%s:%i: ", space->filename, space->linenum); 86 vfprintf(stderr, fmt, arg); 87 putc('\n', stderr); 88 va_end(arg); 89} 90 91#include "init_sysdeps.c" 92#include "init_utils_string.c" 93#include "init_utils_run.c" 94#include "init_sysfs.c" 95 96static void free_space(struct space *space) 97{ 98 struct pair *pair = space->pairs; 99 struct pair *next = pair; 100 101 while (next) { 102 pair = next; 103 next = pair->next; 104 free(pair->value); 105 free(pair->key); 106 free(pair); 107 } 108 space->pairs = NULL; 109 if (space->ctl_value) { 110 snd_ctl_elem_value_free(space->ctl_value); 111 space->ctl_value = NULL; 112 } 113 if (space->ctl_info) { 114 snd_ctl_elem_info_free(space->ctl_info); 115 space->ctl_info = NULL; 116 } 117 if (space->ctl_id) { 118 snd_ctl_elem_id_free(space->ctl_id); 119 space->ctl_id = NULL; 120 } 121 if (space->ctl_card_info) { 122 snd_ctl_card_info_free(space->ctl_card_info); 123 space->ctl_card_info = NULL; 124 } 125 if (space->ctl_handle) { 126 snd_hctl_close(space->ctl_handle); 127 space->ctl_handle = NULL; 128 } 129 if (space->rootdir) 130 free(space->rootdir); 131 if (space->program_result) 132 free(space->program_result); 133 if (space->go_to) 134 free(space->go_to); 135 free(space); 136} 137 138static struct pair *value_find(struct space *space, const char *key) 139{ 140 struct pair *pair = space->pairs; 141 142 while (pair && strcmp(pair->key, key) != 0) 143 pair = pair->next; 144 return pair; 145} 146 147static int value_set(struct space *space, const char *key, const char *value) 148{ 149 struct pair *pair; 150 151 pair = value_find(space, key); 152 if (pair) { 153 free(pair->value); 154 pair->value = strdup(value); 155 if (pair->value == NULL) 156 return -ENOMEM; 157 } else { 158 pair = malloc(sizeof(struct pair)); 159 if (pair == NULL) 160 return -ENOMEM; 161 pair->key = strdup(key); 162 if (pair->key == NULL) { 163 free(pair); 164 return -ENOMEM; 165 } 166 pair->value = strdup(value); 167 if (pair->value == NULL) { 168 free(pair->key); 169 free(pair); 170 return -ENOMEM; 171 } 172 pair->next = space->pairs; 173 space->pairs = pair; 174 } 175 return 0; 176} 177 178static int init_space(struct space **space, int card) 179{ 180 struct space *res; 181 char device[16]; 182 int err; 183 184 res = calloc(1, sizeof(struct space)); 185 if (res == NULL) 186 return -ENOMEM; 187 res->ctl_id_changed = ~0; 188 res->linenum = -1; 189 sprintf(device, "hw:%d", card); 190 err = snd_hctl_open(&res->ctl_handle, device, 0); 191 if (err < 0) 192 goto error; 193 err = snd_hctl_load(res->ctl_handle); 194 if (err < 0) 195 goto error; 196 err = snd_ctl_card_info_malloc(&res->ctl_card_info); 197 if (err < 0) 198 goto error; 199 err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info); 200 if (err < 0) 201 goto error; 202 err = snd_ctl_elem_id_malloc(&res->ctl_id); 203 if (err < 0) 204 goto error; 205 err = snd_ctl_elem_info_malloc(&res->ctl_info); 206 if (err < 0) 207 goto error; 208 err = snd_ctl_elem_value_malloc(&res->ctl_value); 209 if (err < 0) 210 goto error; 211 *space = res; 212 return 0; 213 error: 214 free_space(res); 215 return err; 216} 217 218static const char *cardinfo_get(struct space *space, const char *attr) 219{ 220 if (strncasecmp(attr, "CARD", 4) == 0) { 221 static char res[16]; 222 sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info)); 223 return res; 224 } 225 if (strncasecmp(attr, "ID", 2) == 0) 226 return snd_ctl_card_info_get_id(space->ctl_card_info); 227 if (strncasecmp(attr, "DRIVER", 6) == 0) 228 return snd_ctl_card_info_get_driver(space->ctl_card_info); 229 if (strncasecmp(attr, "NAME", 4) == 0) 230 return snd_ctl_card_info_get_name(space->ctl_card_info); 231 if (strncasecmp(attr, "LONGNAME", 8) == 0) 232 return snd_ctl_card_info_get_longname(space->ctl_card_info); 233 if (strncasecmp(attr, "MIXERNAME", 9) == 0) 234 return snd_ctl_card_info_get_mixername(space->ctl_card_info); 235 if (strncasecmp(attr, "COMPONENTS", 10) == 0) 236 return snd_ctl_card_info_get_components(space->ctl_card_info); 237 Perror(space, "unknown cardinfo{} attribute '%s'", attr); 238 return NULL; 239} 240 241static int check_id_changed(struct space *space, unsigned int what) 242{ 243 snd_hctl_elem_t *elem; 244 int err; 245 246 if ((space->ctl_id_changed & what & 1) != 0) { 247 snd_ctl_elem_id_set_numid(space->ctl_id, 0); 248 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 249 if (!elem) 250 return -ENOENT; 251 err = snd_hctl_elem_info(elem, space->ctl_info); 252 if (err == 0) 253 space->ctl_id_changed &= ~1; 254 return err; 255 } 256 if ((space->ctl_id_changed & what & 2) != 0) { 257 snd_ctl_elem_id_set_numid(space->ctl_id, 0); 258 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 259 if (!elem) 260 return -ENOENT; 261 err = snd_hctl_elem_read(elem, space->ctl_value); 262 if (err == 0) 263 space->ctl_id_changed &= ~2; 264 return err; 265 } 266 return 0; 267} 268 269static const char *get_ctl_value(struct space *space) 270{ 271 snd_ctl_elem_type_t type; 272 unsigned int idx, count; 273 static char res[1024], tmp[16]; 274 static const char hex[] = "0123456789abcdef"; 275 char *pos; 276 const char *pos1; 277 278 type = snd_ctl_elem_info_get_type(space->ctl_info); 279 count = snd_ctl_elem_info_get_count(space->ctl_info); 280 res[0] = '\0'; 281 switch (type) { 282 case SND_CTL_ELEM_TYPE_BOOLEAN: 283 for (idx = 0; idx < count; idx++) { 284 if (idx > 0) 285 strlcat(res, ",", sizeof(res)); 286 strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res)); 287 } 288 break; 289 case SND_CTL_ELEM_TYPE_INTEGER: 290 for (idx = 0; idx < count; idx++) { 291 if (idx > 0) 292 strlcat(res, ",", sizeof(res)); 293 snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx)); 294 strlcat(res, tmp, sizeof(res)); 295 } 296 break; 297 case SND_CTL_ELEM_TYPE_INTEGER64: 298 for (idx = 0; idx < count; idx++) { 299 if (idx > 0) 300 strlcat(res, ",", sizeof(res)); 301 snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx)); 302 strlcat(res, tmp, sizeof(res)); 303 } 304 break; 305 case SND_CTL_ELEM_TYPE_ENUMERATED: 306 for (idx = 0; idx < count; idx++) { 307 if (idx > 0) 308 strlcat(res, ",", sizeof(res)); 309 snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx)); 310 strlcat(res, tmp, sizeof(res)); 311 } 312 break; 313 case SND_CTL_ELEM_TYPE_BYTES: 314 case SND_CTL_ELEM_TYPE_IEC958: 315 if (type == SND_CTL_ELEM_TYPE_IEC958) 316 count = sizeof(snd_aes_iec958_t); 317 if (count > (sizeof(res)-1)/2) 318 count = (sizeof(res)-1/2); 319 pos = res; 320 pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value); 321 while (count > 0) { 322 idx = *pos1++; 323 *pos++ = hex[idx >> 4]; 324 *pos++ = hex[idx & 0x0f]; 325 count++; 326 } 327 *pos++ = '\0'; 328 break; 329 default: 330 Perror(space, "unknown element type '%i'", type); 331 return NULL; 332 } 333 return res; 334} 335 336/* Function to convert from percentage to volume. val = percentage */ 337#define convert_prange1(val, min, max) \ 338 ceil((val) * ((max) - (min)) * 0.01 + (min)) 339 340static int set_ctl_value(struct space *space, const char *value, int all) 341{ 342 snd_ctl_elem_type_t type; 343 unsigned int idx, idx2, count, items; 344 const char *pos, *pos2; 345 snd_hctl_elem_t *elem; 346 int val; 347 long lval; 348 349 type = snd_ctl_elem_info_get_type(space->ctl_info); 350 count = snd_ctl_elem_info_get_count(space->ctl_info); 351 switch (type) { 352 case SND_CTL_ELEM_TYPE_BOOLEAN: 353 for (idx = 0; idx < count; idx++) { 354 while (*value == ' ') 355 value++; 356 if (*value == '\0') 357 goto missing; 358 val = strncasecmp(value, "true", 4) == 0 || 359 strncasecmp(value, "yes", 3) == 0 || 360 strncasecmp(value, "on", 2) == 0 || 361 strncasecmp(value, "1", 1) == 0; 362 snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val); 363 if (all) 364 continue; 365 pos = strchr(value, ','); 366 value = pos ? pos + 1 : value + strlen(value) - 1; 367 } 368 break; 369 case SND_CTL_ELEM_TYPE_INTEGER: 370 for (idx = 0; idx < count; idx++) { 371 while (*value == ' ') 372 value++; 373 pos = strchr(value, ','); 374 if (pos) 375 *(char *)pos = '\0'; 376 remove_trailing_chars((char *)value, ' '); 377 items = pos ? (unsigned)(pos - value) : (unsigned)strlen(value); 378 if (items > 1 && value[items-1] == '%') { 379 val = convert_prange1(strtol(value, NULL, 0), snd_ctl_elem_info_get_min(space->ctl_info), snd_ctl_elem_info_get_max(space->ctl_info)); 380 snd_ctl_elem_value_set_integer(space->ctl_value, idx, val); 381 } else if (items > 2 && value[items-2] == 'd' && value[items-1] == 'B') { 382 val = strtol(value, NULL, 0) * 100; 383 if ((pos2 = strchr(value, '.')) != NULL) { 384 if (isdigit(*(pos2-1)) && isdigit(*(pos2-2))) { 385 if (val < 0) 386 val -= strtol(pos2 + 1, NULL, 0); 387 else 388 val += strtol(pos2 + 1, NULL, 0); 389 } else if (isdigit(*(pos2-1))) { 390 if (val < 0) 391 val -= strtol(pos2 + 1, NULL, 0) * 10; 392 else 393 val += strtol(pos2 + 1, NULL, 0) * 10; 394 } 395 } 396 val = snd_ctl_convert_from_dB(snd_hctl_ctl(space->ctl_handle), space->ctl_id, val, &lval, -1); 397 if (val < 0) { 398 dbg("unable to convert dB value '%s' to internal integer range", value); 399 return val; 400 } 401 snd_ctl_elem_value_set_integer(space->ctl_value, idx, lval); 402 } else { 403 snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0)); 404 } 405 if (all) 406 continue; 407 value = pos ? pos + 1 : value + strlen(value) - 1; 408 } 409 break; 410 case SND_CTL_ELEM_TYPE_INTEGER64: 411 for (idx = 0; idx < count; idx++) { 412 while (*value == ' ') 413 value++; 414 snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0)); 415 if (all) 416 continue; 417 pos = strchr(value, ','); 418 value = pos ? pos + 1 : value + strlen(value) - 1; 419 } 420 break; 421 case SND_CTL_ELEM_TYPE_ENUMERATED: 422 for (idx = 0; idx < count; idx++) { 423 while (*value == ' ') 424 value++; 425 pos = strchr(value, ','); 426 if (isdigit(value[0]) || value[0] == '-') { 427 snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0)); 428 } else { 429 if (pos) 430 *(char *)pos = '\0'; 431 remove_trailing_chars((char *)value, ' '); 432 items = snd_ctl_elem_info_get_items(space->ctl_info); 433 for (idx2 = 0; idx2 < items; idx2++) { 434 snd_ctl_elem_info_set_item(space->ctl_info, idx2); 435 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 436 if (elem == NULL) 437 return -ENOENT; 438 val = snd_hctl_elem_info(elem, space->ctl_info); 439 if (val < 0) 440 return val; 441 if (strcasecmp(snd_ctl_elem_info_get_item_name(space->ctl_info), value) == 0) { 442 snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, idx2); 443 break; 444 } 445 } 446 if (idx2 >= items) { 447 Perror(space, "wrong enum identifier '%s'", value); 448 return -EINVAL; 449 } 450 } 451 if (all) 452 continue; 453 value = pos ? pos + 1 : value + strlen(value) - 1; 454 } 455 break; 456 case SND_CTL_ELEM_TYPE_BYTES: 457 case SND_CTL_ELEM_TYPE_IEC958: 458 if (type == SND_CTL_ELEM_TYPE_IEC958) 459 count = sizeof(snd_aes_iec958_t); 460 while (*value == ' ') 461 value++; 462 if (strlen(value) != count * 2) { 463 Perror(space, "bad ctl value hexa length (should be %u bytes)", count); 464 return -EINVAL; 465 } 466 for (idx = 0; idx < count; idx += 2) { 467 int nibble1 = hextodigit(*(value++)); 468 int nibble2 = hextodigit(*(value++)); 469 if (nibble1 < 0 || nibble2 < 0) { 470 Perror(space, "bad ctl hexa value"); 471 return -EINVAL; 472 } 473 val = (nibble1 << 4) | nibble2; 474 snd_ctl_elem_value_set_byte(space->ctl_value, idx, val); 475 } 476 break; 477 default: 478 Perror(space, "unknown element type '%i'", type); 479 return -EINVAL; 480 } 481 return 0; 482 missing: 483 Perror(space, "missing some ctl values (line %i)", space->linenum); 484 return -EINVAL; 485} 486 487static int do_match(const char *key, enum key_op op, 488 const char *key_value, const char *value) 489{ 490 int match; 491 492 if (value == NULL) 493 return 0; 494 dbg("match %s '%s' <-> '%s'", key, key_value, value); 495 match = fnmatch(key_value, value, 0) == 0; 496 if (match && op == KEY_OP_MATCH) { 497 dbg("%s is true (matching value)", key); 498 return 1; 499 } 500 if (!match && op == KEY_OP_NOMATCH) { 501 dbg("%s is true (non-matching value)", key); 502 return 1; 503 } 504 dbg("%s is false", key); 505 return 0; 506} 507 508static int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id) 509{ 510 if ((int)snd_ctl_elem_id_get_interface(pattern) != -1 && 511 snd_ctl_elem_id_get_interface(pattern) != snd_ctl_elem_id_get_interface(id)) 512 return 0; 513 if ((int)snd_ctl_elem_id_get_device(pattern) != -1 && 514 snd_ctl_elem_id_get_device(pattern) != snd_ctl_elem_id_get_device(id)) 515 return 0; 516 if ((int)snd_ctl_elem_id_get_subdevice(pattern) != -1 && 517 snd_ctl_elem_id_get_subdevice(pattern) != snd_ctl_elem_id_get_subdevice(id)) 518 return 0; 519 if ((int)snd_ctl_elem_id_get_index(pattern) != -1 && 520 snd_ctl_elem_id_get_index(pattern) != snd_ctl_elem_id_get_index(id)) 521 return 0; 522 if (fnmatch(snd_ctl_elem_id_get_name(pattern), snd_ctl_elem_id_get_name(id), 0) != 0) 523 return 0; 524 return 1; 525} 526 527static const char *elemid_get(struct space *space, const char *attr) 528{ 529 long long val; 530 snd_ctl_elem_type_t type; 531 static char res[256]; 532 533 if (strncasecmp(attr, "numid", 5) == 0) { 534 val = snd_ctl_elem_id_get_numid(space->ctl_id); 535 goto value; 536 } 537 if (strncasecmp(attr, "iface", 5) == 0 || 538 strncasecmp(attr, "interface", 9) == 0) 539 return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(space->ctl_id)); 540 if (strncasecmp(attr, "device", 6) == 0) { 541 val = snd_ctl_elem_id_get_device(space->ctl_id); 542 goto value; 543 } 544 if (strncasecmp(attr, "subdev", 6) == 0) { 545 val = snd_ctl_elem_id_get_subdevice(space->ctl_id); 546 goto value; 547 } 548 if (strncasecmp(attr, "name", 4) == 0) 549 return snd_ctl_elem_id_get_name(space->ctl_id); 550 if (strncasecmp(attr, "index", 5) == 0) { 551 val = snd_ctl_elem_id_get_index(space->ctl_id); 552 goto value; 553 } 554 if (strncasecmp(attr, "type", 4) == 0) { 555 if (check_id_changed(space, 1)) 556 return NULL; 557 return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(space->ctl_info)); 558 } 559 if (strncasecmp(attr, "attr", 4) == 0) { 560 if (check_id_changed(space, 1)) 561 return NULL; 562 res[0] = '\0'; 563 if (snd_ctl_elem_info_is_readable(space->ctl_info)) 564 strcat(res, "r"); 565 if (snd_ctl_elem_info_is_writable(space->ctl_info)) 566 strcat(res, "w"); 567 if (snd_ctl_elem_info_is_volatile(space->ctl_info)) 568 strcat(res, "v"); 569 if (snd_ctl_elem_info_is_inactive(space->ctl_info)) 570 strcat(res, "i"); 571 if (snd_ctl_elem_info_is_locked(space->ctl_info)) 572 strcat(res, "l"); 573 if (snd_ctl_elem_info_is_tlv_readable(space->ctl_info)) 574 strcat(res, "R"); 575 if (snd_ctl_elem_info_is_tlv_writable(space->ctl_info)) 576 strcat(res, "W"); 577 if (snd_ctl_elem_info_is_tlv_commandable(space->ctl_info)) 578 strcat(res, "C"); 579 if (snd_ctl_elem_info_is_owner(space->ctl_info)) 580 strcat(res, "o"); 581 if (snd_ctl_elem_info_is_user(space->ctl_info)) 582 strcat(res, "u"); 583 return res; 584 } 585 if (strncasecmp(attr, "owner", 5) == 0) { 586 if (check_id_changed(space, 1)) 587 return NULL; 588 val = snd_ctl_elem_info_get_owner(space->ctl_info); 589 goto value; 590 } 591 if (strncasecmp(attr, "count", 5) == 0) { 592 if (check_id_changed(space, 1)) 593 return NULL; 594 val = snd_ctl_elem_info_get_count(space->ctl_info); 595 goto value; 596 } 597 if (strncasecmp(attr, "min", 3) == 0) { 598 if (check_id_changed(space, 1)) 599 return NULL; 600 type = snd_ctl_elem_info_get_type(space->ctl_info); 601 if (type == SND_CTL_ELEM_TYPE_INTEGER64) 602 val = snd_ctl_elem_info_get_min64(space->ctl_info); 603 else if (type == SND_CTL_ELEM_TYPE_INTEGER) 604 val = snd_ctl_elem_info_get_min(space->ctl_info); 605 else 606 goto empty; 607 goto value; 608 } 609 if (strncasecmp(attr, "max", 3) == 0) { 610 if (check_id_changed(space, 1)) 611 return NULL; 612 type = snd_ctl_elem_info_get_type(space->ctl_info); 613 if (type == SND_CTL_ELEM_TYPE_INTEGER64) 614 val = snd_ctl_elem_info_get_max64(space->ctl_info); 615 else if (type == SND_CTL_ELEM_TYPE_INTEGER) 616 val = snd_ctl_elem_info_get_max(space->ctl_info); 617 else 618 goto empty; 619 goto value; 620 } 621 if (strncasecmp(attr, "step", 3) == 0) { 622 if (check_id_changed(space, 1)) 623 return NULL; 624 type = snd_ctl_elem_info_get_type(space->ctl_info); 625 if (type == SND_CTL_ELEM_TYPE_INTEGER64) 626 val = snd_ctl_elem_info_get_step64(space->ctl_info); 627 else if (type == SND_CTL_ELEM_TYPE_INTEGER) 628 val = snd_ctl_elem_info_get_step(space->ctl_info); 629 else 630 goto empty; 631 goto value; 632 } 633 if (strncasecmp(attr, "items", 5) == 0) { 634 if (check_id_changed(space, 1)) 635 return NULL; 636 if (snd_ctl_elem_info_get_type(space->ctl_info) == SND_CTL_ELEM_TYPE_ENUMERATED) 637 val = snd_ctl_elem_info_get_items(space->ctl_info); 638 else { 639 empty: 640 res[0] = '\0'; 641 return res; 642 } 643 goto value; 644 } 645 if (strncasecmp(attr, "value", 5) == 0) { 646 if (check_id_changed(space, 3)) 647 return NULL; 648 return get_ctl_value(space); 649 } 650 if (strncasecmp(attr, "dBmin", 5) == 0) { 651 long min, max; 652 if (check_id_changed(space, 1)) 653 return NULL; 654 if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0) 655 goto empty; 656 val = min; 657dbvalue: 658 sprintf(res, "%li.%02lidB", (long)(val / 100), labs(val % 100)); 659 return res; 660 } 661 if (strncasecmp(attr, "dBmax", 5) == 0) { 662 long min, max; 663 if (check_id_changed(space, 1)) 664 return NULL; 665 if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0) 666 goto empty; 667 val = max; 668 goto dbvalue; 669 } 670 if (strncasecmp(attr, "enums", 5) == 0) { 671 unsigned int idx, items; 672 snd_hctl_elem_t *elem; 673 if (check_id_changed(space, 1)) 674 return NULL; 675 if (snd_ctl_elem_info_get_type(space->ctl_info) != SND_CTL_ELEM_TYPE_ENUMERATED) 676 goto empty; 677 items = snd_ctl_elem_info_get_items(space->ctl_info); 678 strcpy(res, "|"); 679 for (idx = 0; idx < items; idx++) { 680 snd_ctl_elem_info_set_item(space->ctl_info, idx); 681 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 682 if (elem == NULL) 683 break; 684 if (snd_hctl_elem_info(elem, space->ctl_info) < 0) 685 break; 686 strlcat(res, snd_ctl_elem_info_get_item_name(space->ctl_info), sizeof(res)); 687 strlcat(res, "|", sizeof(res)); 688 } 689 return res; 690 } 691 if (strncasecmp(attr, "do_search", 9) == 0) { 692 int err, index = 0; 693 snd_hctl_elem_t *elem; 694 snd_ctl_elem_id_t *id; 695 char *pos = strchr(attr, ' '); 696 if (pos) 697 index = strtol(pos, NULL, 0); 698 err = snd_ctl_elem_id_malloc(&id); 699 if (err < 0) 700 return NULL; 701 elem = snd_hctl_first_elem(space->ctl_handle); 702 while (elem) { 703 snd_hctl_elem_get_id(elem, id); 704 if (!ctl_match(space->ctl_id, id)) 705 goto next_search; 706 if (index > 0) { 707 index--; 708 goto next_search; 709 } 710 strcpy(res, "1"); 711 snd_ctl_elem_id_copy(space->ctl_id, id); 712 snd_ctl_elem_id_free(id); 713 dbg("do_ctl_search found a control"); 714 return res; 715 next_search: 716 elem = snd_hctl_elem_next(elem); 717 } 718 snd_ctl_elem_id_free(id); 719 strcpy(res, "0"); 720 return res; 721 } 722 if (strncasecmp(attr, "do_count", 8) == 0) { 723 int err, index = 0; 724 snd_hctl_elem_t *elem; 725 snd_ctl_elem_id_t *id; 726 err = snd_ctl_elem_id_malloc(&id); 727 if (err < 0) 728 return NULL; 729 elem = snd_hctl_first_elem(space->ctl_handle); 730 while (elem) { 731 snd_hctl_elem_get_id(elem, id); 732 if (ctl_match(space->ctl_id, id)) 733 index++; 734 elem = snd_hctl_elem_next(elem); 735 } 736 snd_ctl_elem_id_free(id); 737 sprintf(res, "%d", index); 738 dbg("do_ctl_count found %s controls", res); 739 return res; 740 } 741 Perror(space, "unknown ctl{} attribute '%s'", attr); 742 return NULL; 743 value: 744 sprintf(res, "%lli", val); 745 return res; 746} 747 748static int elemid_set(struct space *space, const char *attr, const char *value) 749{ 750 unsigned int val; 751 void (*fcn)(snd_ctl_elem_id_t *, unsigned int); 752 snd_ctl_elem_iface_t iface; 753 int err; 754 755 if (strncasecmp(attr, "numid", 5) == 0) { 756 fcn = snd_ctl_elem_id_set_numid; 757 goto value; 758 } 759 if (strncasecmp(attr, "iface", 5) == 0 || 760 strncasecmp(attr, "interface", 9) == 0 || 761 strncasecmp(attr, "reset", 5) == 0 || 762 strncasecmp(attr, "search", 6) == 0) { 763 if (strlen(value) == 0 && strncasecmp(attr, "search", 6) == 0) { 764 iface = 0; 765 goto search; 766 } 767 for (iface = 0; iface <= SND_CTL_ELEM_IFACE_LAST; iface++) { 768 if (strcasecmp(value, snd_ctl_elem_iface_name(iface)) == 0) { 769 if (strncasecmp(attr, "reset", 5) == 0) 770 snd_ctl_elem_id_clear(space->ctl_id); 771 if (strncasecmp(attr, "search", 5) == 0) { 772 search: 773 snd_ctl_elem_id_clear(space->ctl_id); 774 /* -1 means all */ 775 snd_ctl_elem_id_set_interface(space->ctl_id, -1); 776 snd_ctl_elem_id_set_device(space->ctl_id, -1); 777 snd_ctl_elem_id_set_subdevice(space->ctl_id, -1); 778 snd_ctl_elem_id_set_name(space->ctl_id, "*"); 779 snd_ctl_elem_id_set_index(space->ctl_id, -1); 780 if (strlen(value) == 0) 781 return 0; 782 } 783 snd_ctl_elem_id_set_interface(space->ctl_id, iface); 784 space->ctl_id_changed = ~0; 785 return 0; 786 } 787 } 788 Perror(space, "unknown control interface name '%s'", value); 789 return -EINVAL; 790 } 791 if (strncasecmp(attr, "device", 6) == 0) { 792 fcn = snd_ctl_elem_id_set_device; 793 goto value; 794 } 795 if (strncasecmp(attr, "subdev", 6) == 0) { 796 fcn = snd_ctl_elem_id_set_subdevice; 797 goto value; 798 } 799 if (strncasecmp(attr, "name", 4) == 0) { 800 snd_ctl_elem_id_set_name(space->ctl_id, value); 801 space->ctl_id_changed = ~0; 802 return 0; 803 } 804 if (strncasecmp(attr, "index", 5) == 0) { 805 fcn = snd_ctl_elem_id_set_index; 806 goto value; 807 } 808 if (strncasecmp(attr, "values", 6) == 0 || 809 strncasecmp(attr, "value", 5) == 0) { 810 err = check_id_changed(space, 1); 811 if (err < 0) { 812 Perror(space, "control element not found"); 813 return err; 814 } 815 err = set_ctl_value(space, value, strncasecmp(attr, "values", 6) == 0); 816 if (err < 0) { 817 space->ctl_id_changed |= 2; 818 } else { 819 space->ctl_id_changed &= ~2; 820 snd_ctl_elem_value_set_id(space->ctl_value, space->ctl_id); 821 err = snd_ctl_elem_write(snd_hctl_ctl(space->ctl_handle), space->ctl_value); 822 if (err < 0) { 823 Perror(space, "value write error: %s", snd_strerror(err)); 824 return err; 825 } 826 } 827 return err; 828 } 829 Perror(space, "unknown CTL{} attribute '%s'", attr); 830 return -EINVAL; 831 value: 832 val = (unsigned int)strtol(value, NULL, 0); 833 fcn(space->ctl_id, val); 834 space->ctl_id_changed = ~0; 835 return 0; 836} 837 838static int get_key(char **line, char **key, enum key_op *op, char **value) 839{ 840 char *linepos; 841 char *temp; 842 843 linepos = *line; 844 if (linepos == NULL && linepos[0] == '\0') 845 return -EINVAL; 846 847 /* skip whitespace */ 848 while (isspace(linepos[0]) || linepos[0] == ',') 849 linepos++; 850 851 /* get the key */ 852 if (linepos[0] == '\0') 853 return -EINVAL; 854 *key = linepos; 855 856 while (1) { 857 linepos++; 858 if (linepos[0] == '\0') 859 return -1; 860 if (isspace(linepos[0])) 861 break; 862 if (linepos[0] == '=') 863 break; 864 if (linepos[0] == '+') 865 break; 866 if (linepos[0] == '!') 867 break; 868 if (linepos[0] == ':') 869 break; 870 } 871 872 /* remember end of key */ 873 temp = linepos; 874 875 /* skip whitespace after key */ 876 while (isspace(linepos[0])) 877 linepos++; 878 if (linepos[0] == '\0') 879 return -EINVAL; 880 881 /* get operation type */ 882 if (linepos[0] == '=' && linepos[1] == '=') { 883 *op = KEY_OP_MATCH; 884 linepos += 2; 885 dbg("operator=match"); 886 } else if (linepos[0] == '!' && linepos[1] == '=') { 887 *op = KEY_OP_NOMATCH; 888 linepos += 2; 889 dbg("operator=nomatch"); 890 } else if (linepos[0] == '+' && linepos[1] == '=') { 891 *op = KEY_OP_ADD; 892 linepos += 2; 893 dbg("operator=add"); 894 } else if (linepos[0] == '=') { 895 *op = KEY_OP_ASSIGN; 896 linepos++; 897 dbg("operator=assign"); 898 } else if (linepos[0] == ':' && linepos[1] == '=') { 899 *op = KEY_OP_ASSIGN_FINAL; 900 linepos += 2; 901 dbg("operator=assign_final"); 902 } else 903 return -EINVAL; 904 905 /* terminate key */ 906 temp[0] = '\0'; 907 dbg("key='%s'", *key); 908 909 /* skip whitespace after operator */ 910 while (isspace(linepos[0])) 911 linepos++; 912 if (linepos[0] == '\0') 913 return -EINVAL; 914 915 /* get the value*/ 916 if (linepos[0] != '"') 917 return -EINVAL; 918 linepos++; 919 *value = linepos; 920 921 while (1) { 922 temp = strchr(linepos, '"'); 923 if (temp && temp[-1] == '\\') { 924 linepos = temp + 1; 925 continue; 926 } 927 break; 928 } 929 if (!temp) 930 return -EINVAL; 931 temp[0] = '\0'; 932 temp++; 933 dbg("value='%s'", *value); 934 935 /* move line to next key */ 936 *line = temp; 937 938 return 0; 939} 940 941/* extract possible KEY{attr} */ 942static char *get_key_attribute(struct space *space, char *str, char *res, size_t ressize) 943{ 944 char *pos; 945 char *attr; 946 947 attr = strchr(str, '{'); 948 if (attr != NULL) { 949 attr++; 950 pos = strchr(attr, '}'); 951 if (pos == NULL) { 952 Perror(space, "missing closing brace for format"); 953 return NULL; 954 } 955 pos[0] = '\0'; 956 strlcpy(res, attr, ressize); 957 pos[0] = '}'; 958 dbg("attribute='%s'", res); 959 return res; 960 } 961 962 return NULL; 963} 964 965/* extract possible {attr} and move str behind it */ 966static char *get_format_attribute(struct space *space, char **str) 967{ 968 char *pos; 969 char *attr = NULL; 970 971 if (*str[0] == '{') { 972 pos = strchr(*str, '}'); 973 if (pos == NULL) { 974 Perror(space, "missing closing brace for format"); 975 return NULL; 976 } 977 pos[0] = '\0'; 978 attr = *str+1; 979 *str = pos+1; 980 dbg("attribute='%s', str='%s'", attr, *str); 981 } 982 return attr; 983} 984 985/* extract possible format length and move str behind it*/ 986static int get_format_len(struct space *space, char **str) 987{ 988 int num; 989 char *tail; 990 991 if (isdigit(*str[0])) { 992 num = (int) strtoul(*str, &tail, 10); 993 if (num > 0) { 994 *str = tail; 995 dbg("format length=%i", num); 996 return num; 997 } else { 998 Perror(space, "format parsing error '%s'", *str); 999 } 1000 } 1001 return -1; 1002} 1003 1004static void apply_format(struct space *space, char *string, size_t maxsize) 1005{ 1006 char temp[PATH_SIZE]; 1007 char temp2[PATH_SIZE]; 1008 char *head, *tail, *pos, *cpos, *attr, *rest; 1009 struct pair *pair; 1010 int len; 1011 int i; 1012 int count; 1013 enum subst_type { 1014 SUBST_UNKNOWN, 1015 SUBST_CARDINFO, 1016 SUBST_CTL, 1017 SUBST_RESULT, 1018 SUBST_ATTR, 1019 SUBST_SYSFSROOT, 1020 SUBST_ENV, 1021 SUBST_CONFIG, 1022 }; 1023 static const struct subst_map { 1024 char *name; 1025 char fmt; 1026 enum subst_type type; 1027 } map[] = { 1028 { .name = "cardinfo", .fmt = 'i', .type = SUBST_CARDINFO }, 1029 { .name = "ctl", .fmt = 'C', .type = SUBST_CTL }, 1030 { .name = "result", .fmt = 'c', .type = SUBST_RESULT }, 1031 { .name = "attr", .fmt = 's', .type = SUBST_ATTR }, 1032 { .name = "sysfsroot", .fmt = 'r', .type = SUBST_SYSFSROOT }, 1033 { .name = "env", .fmt = 'E', .type = SUBST_ENV }, 1034 { .name = "config", .fmt = 'g', .type = SUBST_CONFIG }, 1035 { NULL, '\0', 0 } 1036 }; 1037 enum subst_type type; 1038 const struct subst_map *subst; 1039 1040 head = string; 1041 while (1) { 1042 len = -1; 1043 while (head[0] != '\0') { 1044 if (head[0] == '$') { 1045 /* substitute named variable */ 1046 if (head[1] == '\0') 1047 break; 1048 if (head[1] == '$') { 1049 strlcpy(temp, head+2, sizeof(temp)); 1050 strlcpy(head+1, temp, maxsize); 1051 head++; 1052 continue; 1053 } 1054 head[0] = '\0'; 1055 for (subst = map; subst->name; subst++) { 1056 if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) { 1057 type = subst->type; 1058 tail = head + strlen(subst->name)+1; 1059 dbg("will substitute format name '%s'", subst->name); 1060 goto found; 1061 } 1062 } 1063 } else if (head[0] == '%') { 1064 /* substitute format char */ 1065 if (head[1] == '\0') 1066 break; 1067 if (head[1] == '%') { 1068 strlcpy(temp, head+2, sizeof(temp)); 1069 strlcpy(head+1, temp, maxsize); 1070 head++; 1071 continue; 1072 } 1073 head[0] = '\0'; 1074 tail = head+1; 1075 len = get_format_len(space, &tail); 1076 for (subst = map; subst->name; subst++) { 1077 if (tail[0] == subst->fmt) { 1078 type = subst->type; 1079 tail++; 1080 dbg("will substitute format char '%c'", subst->fmt); 1081 goto found; 1082 } 1083 } 1084 } 1085 head++; 1086 } 1087 break; 1088found: 1089 attr = get_format_attribute(space, &tail); 1090 strlcpy(temp, tail, sizeof(temp)); 1091 dbg("format=%i, string='%s', tail='%s'", type ,string, tail); 1092 1093 switch (type) { 1094 case SUBST_CARDINFO: 1095 if (attr == NULL) 1096 Perror(space, "missing identification parametr for cardinfo"); 1097 else { 1098 const char *value = cardinfo_get(space, attr); 1099 if (value == NULL) 1100 break; 1101 strlcat(string, value, maxsize); 1102 dbg("substitute cardinfo{%s} '%s'", attr, value); 1103 } 1104 break; 1105 case SUBST_CTL: 1106 if (attr == NULL) 1107 Perror(space, "missing identification parametr for ctl"); 1108 else { 1109 const char *value = elemid_get(space, attr); 1110 if (value == NULL) 1111 break; 1112 strlcat(string, value, maxsize); 1113 dbg("substitute ctl{%s} '%s'", attr, value); 1114 } 1115 break; 1116 case SUBST_RESULT: 1117 if (space->program_result == NULL) 1118 break; 1119 /* get part part of the result string */ 1120 i = 0; 1121 if (attr != NULL) 1122 i = strtoul(attr, &rest, 10); 1123 if (i > 0) { 1124 dbg("request part #%d of result string", i); 1125 cpos = space->program_result; 1126 while (--i) { 1127 while (cpos[0] != '\0' && !isspace(cpos[0])) 1128 cpos++; 1129 while (isspace(cpos[0])) 1130 cpos++; 1131 } 1132 if (i > 0) { 1133 Perror(space, "requested part of result string not found"); 1134 break; 1135 } 1136 strlcpy(temp2, cpos, sizeof(temp2)); 1137 /* %{2+}c copies the whole string from the second part on */ 1138 if (rest[0] != '+') { 1139 cpos = strchr(temp2, ' '); 1140 if (cpos) 1141 cpos[0] = '\0'; 1142 } 1143 strlcat(string, temp2, maxsize); 1144 dbg("substitute part of result string '%s'", temp2); 1145 } else { 1146 strlcat(string, space->program_result, maxsize); 1147 dbg("substitute result string '%s'", space->program_result); 1148 } 1149 break; 1150 case SUBST_ATTR: 1151 if (attr == NULL) 1152 Perror(space, "missing file parameter for attr"); 1153 else { 1154 const char *value = NULL; 1155 size_t size; 1156 1157 pair = value_find(space, "sysfs_device"); 1158 if (pair == NULL) 1159 break; 1160 value = sysfs_attr_get_value(pair->value, attr); 1161 1162 if (value == NULL) 1163 break; 1164 1165 /* strip trailing whitespace and replace untrusted characters of sysfs value */ 1166 size = strlcpy(temp2, value, sizeof(temp2)); 1167 if (size >= sizeof(temp2)) 1168 size = sizeof(temp2)-1; 1169 while (size > 0 && isspace(temp2[size-1])) 1170 temp2[--size] = '\0'; 1171 count = replace_untrusted_chars(temp2); 1172 if (count > 0) 1173 Perror(space, "%i untrusted character(s) replaced" , count); 1174 strlcat(string, temp2, maxsize); 1175 dbg("substitute sysfs value '%s'", temp2); 1176 } 1177 break; 1178 case SUBST_SYSFSROOT: 1179 strlcat(string, sysfs_path, maxsize); 1180 dbg("substitute sysfs_path '%s'", sysfs_path); 1181 break; 1182 case SUBST_ENV: 1183 if (attr == NULL) { 1184 dbg("missing attribute"); 1185 break; 1186 } 1187 pos = getenv(attr); 1188 if (pos == NULL) { 1189 dbg("env '%s' not available", attr); 1190 break; 1191 } 1192 dbg("substitute env '%s=%s'", attr, pos); 1193 strlcat(string, pos, maxsize); 1194 break; 1195 case SUBST_CONFIG: 1196 if (attr == NULL) { 1197 dbg("missing attribute"); 1198 break; 1199 } 1200 pair = value_find(space, attr); 1201 if (pair == NULL) 1202 break; 1203 strlcat(string, pair->value, maxsize); 1204 break; 1205 default: 1206 Perror(space, "unknown substitution type=%i", type); 1207 break; 1208 } 1209 /* possibly truncate to format-char specified length */ 1210 if (len != -1) { 1211 head[len] = '\0'; 1212 dbg("truncate to %i chars, subtitution string becomes '%s'", len, head); 1213 } 1214 strlcat(string, temp, maxsize); 1215 } 1216 /* unescape strings */ 1217 head = tail = string; 1218 while (*head != '\0') { 1219 if (*head == '\\') { 1220 head++; 1221 if (*head == '\0') 1222 break; 1223 switch (*head) { 1224 case 'a': *tail++ = '\a'; break; 1225 case 'b': *tail++ = '\b'; break; 1226 case 'n': *tail++ = '\n'; break; 1227 case 'r': *tail++ = '\r'; break; 1228 case 't': *tail++ = '\t'; break; 1229 case 'v': *tail++ = '\v'; break; 1230 case '\\': *tail++ = '\\'; break; 1231 default: *tail++ = *head; break; 1232 } 1233 head++; 1234 continue; 1235 } 1236 if (*head) 1237 *tail++ = *head++; 1238 } 1239 *tail = 0; 1240} 1241 1242static 1243int run_program1(struct space *space, 1244 const char *command0, char *result, 1245 size_t ressize, size_t *reslen ATTRIBUTE_UNUSED, 1246 int log ATTRIBUTE_UNUSED) 1247{ 1248 if (strncmp(command0, "__ctl_search", 12) == 0) { 1249 const char *res = elemid_get(space, "do_search"); 1250 if (res == NULL || strcmp(res, "1") != 0) 1251 return EXIT_FAILURE; 1252 return EXIT_SUCCESS; 1253 } 1254 if (strncmp(command0, "__ctl_count", 11) == 0) { 1255 const char *res = elemid_get(space, "do_count"); 1256 if (res == NULL || strcmp(res, "0") == 0) 1257 return EXIT_FAILURE; 1258 strlcpy(result, res, ressize); 1259 return EXIT_SUCCESS; 1260 } 1261 Perror(space, "unknown buildin command '%s'", command0); 1262 return EXIT_FAILURE; 1263} 1264 1265static int parse(struct space *space, const char *filename); 1266 1267static char *new_root_dir(const char *filename) 1268{ 1269 char *res, *tmp; 1270 1271 res = strdup(filename); 1272 if (res) { 1273 tmp = strrchr(res, '/'); 1274 if (tmp) 1275 *tmp = '\0'; 1276 } 1277 dbg("new_root_dir '%s' '%s'", filename, res); 1278 return res; 1279} 1280 1281/* return non-zero if the file name has the extension ".conf" */ 1282static int conf_name_filter(const struct dirent *d) 1283{ 1284 char *ext = strrchr(d->d_name, '.'); 1285 return ext && !strcmp(ext, ".conf"); 1286} 1287 1288static int parse_line(struct space *space, char *line, size_t linesize ATTRIBUTE_UNUSED) 1289{ 1290 char *linepos; 1291 char *key, *value, *attr, *temp; 1292 struct pair *pair; 1293 enum key_op op; 1294 int err = 0, count; 1295 char string[PATH_SIZE]; 1296 char result[PATH_SIZE]; 1297 1298 linepos = line; 1299 while (*linepos != '\0') { 1300 op = KEY_OP_UNSET; 1301 1302 err = get_key(&linepos, &key, &op, &value); 1303 if (err < 0) 1304 goto invalid; 1305 1306 if (strncasecmp(key, "LABEL", 5) == 0) { 1307 if (op != KEY_OP_ASSIGN) { 1308 Perror(space, "invalid LABEL operation"); 1309 goto invalid; 1310 } 1311 if (space->go_to && strcmp(space->go_to, value) == 0) { 1312 free(space->go_to); 1313 space->go_to = NULL; 1314 } 1315 continue; 1316 } 1317 1318 if (space->go_to) { 1319 dbg("skip (GOTO '%s')", space->go_to); 1320 break; /* not for us */ 1321 } 1322 1323 if (strncasecmp(key, "CTL{", 4) == 0) { 1324 attr = get_key_attribute(space, key + 3, string, sizeof(string)); 1325 if (attr == NULL) { 1326 Perror(space, "error parsing CTL attribute"); 1327 goto invalid; 1328 } 1329 if (op == KEY_OP_ASSIGN) { 1330 strlcpy(result, value, sizeof(result)); 1331 apply_format(space, result, sizeof(result)); 1332 dbg("ctl assign: '%s' '%s'", value, attr); 1333 err = elemid_set(space, attr, result); 1334 if (space->program_result) { 1335 free(space->program_result); 1336 space->program_result = NULL; 1337 } 1338 snprintf(string, sizeof(string), "%i", err); 1339 space->program_result = strdup(string); 1340 err = 0; 1341 if (space->program_result == NULL) 1342 break; 1343 } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1344 if (strncmp(attr, "write", 5) == 0) { 1345 strlcpy(result, value, sizeof(result)); 1346 apply_format(space, result, sizeof(result)); 1347 dbg("ctl write: '%s' '%s'", value, attr); 1348 err = elemid_set(space, "values", result); 1349 if (err == 0 && op == KEY_OP_NOMATCH) 1350 break; 1351 if (err != 0 && op == KEY_OP_MATCH) 1352 break; 1353 } else { 1354 temp = (char *)elemid_get(space, attr); 1355 dbg("ctl match: '%s' '%s' '%s'", attr, value, temp); 1356 if (!do_match(key, op, value, temp)) 1357 break; 1358 } 1359 } else { 1360 Perror(space, "invalid CTL{} operation"); 1361 goto invalid; 1362 } 1363 continue; 1364 } 1365 if (strcasecmp(key, "RESULT") == 0) { 1366 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1367 if (!do_match(key, op, value, space->program_result)) 1368 break; 1369 } else if (op == KEY_OP_ASSIGN) { 1370 if (space->program_result) { 1371 free(space->program_result); 1372 space->program_result = NULL; 1373 } 1374 strlcpy(string, value, sizeof(string)); 1375 apply_format(space, string, sizeof(string)); 1376 space->program_result = strdup(string); 1377 if (space->program_result == NULL) 1378 break; 1379 } else { 1380 Perror(space, "invalid RESULT operation"); 1381 goto invalid; 1382 } 1383 continue; 1384 } 1385 if (strcasecmp(key, "PROGRAM") == 0) { 1386 if (op == KEY_OP_UNSET) 1387 continue; 1388 strlcpy(string, value, sizeof(string)); 1389 apply_format(space, string, sizeof(string)); 1390 if (space->program_result) { 1391 free(space->program_result); 1392 space->program_result = NULL; 1393 } 1394 if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) { 1395 dbg("PROGRAM '%s' is false", string); 1396 if (op != KEY_OP_NOMATCH) 1397 break; 1398 } else { 1399 remove_trailing_chars(result, '\n'); 1400 count = replace_untrusted_chars(result); 1401 if (count) 1402 info("%i untrusted character(s) replaced", count); 1403 dbg("PROGRAM '%s' result is '%s'", string, result); 1404 space->program_result = strdup(result); 1405 if (space->program_result == NULL) 1406 break; 1407 dbg("PROGRAM returned successful"); 1408 if (op == KEY_OP_NOMATCH) 1409 break; 1410 } 1411 dbg("PROGRAM key is true"); 1412 continue; 1413 } 1414 if (strncasecmp(key, "CARDINFO{", 9) == 0) { 1415 attr = get_key_attribute(space, key + 8, string, sizeof(string)); 1416 if (attr == NULL) { 1417 Perror(space, "error parsing CARDINFO attribute"); 1418 goto invalid; 1419 } 1420 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1421 dbg("cardinfo: '%s' '%s'", value, attr); 1422 temp = (char *)cardinfo_get(space, attr); 1423 if (!do_match(key, op, value, temp)) 1424 break; 1425 } else { 1426 Perror(space, "invalid CARDINFO{} operation"); 1427 goto invalid; 1428 } 1429 continue; 1430 } 1431 if (strncasecmp(key, "ATTR{", 5) == 0) { 1432 attr = get_key_attribute(space, key + 4, string, sizeof(string)); 1433 if (attr == NULL) { 1434 Perror(space, "error parsing ATTR attribute"); 1435 goto invalid; 1436 } 1437 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1438 pair = value_find(space, "sysfs_device"); 1439 if (pair == NULL) 1440 break; 1441 dbg("sysfs_attr: '%s' '%s'", pair->value, attr); 1442 temp = sysfs_attr_get_value(pair->value, attr); 1443 if (!do_match(key, op, value, temp)) 1444 break; 1445 } else { 1446 Perror(space, "invalid ATTR{} operation"); 1447 goto invalid; 1448 } 1449 continue; 1450 } 1451 if (strncasecmp(key, "ENV{", 4) == 0) { 1452 attr = get_key_attribute(space, key + 3, string, sizeof(string)); 1453 if (attr == NULL) { 1454 Perror(space, "error parsing ENV attribute"); 1455 goto invalid; 1456 } 1457 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1458 temp = getenv(attr); 1459 dbg("env: '%s' '%s'", attr, temp); 1460 if (!do_match(key, op, value, temp)) 1461 break; 1462 } else if (op == KEY_OP_ASSIGN || 1463 op == KEY_OP_ASSIGN_FINAL) { 1464 strlcpy(result, value, sizeof(result)); 1465 apply_format(space, result, sizeof(result)); 1466 dbg("env set: '%s' '%s'", attr, result); 1467 if (setenv(attr, result, op == KEY_OP_ASSIGN_FINAL)) 1468 break; 1469 } else { 1470 Perror(space, "invalid ENV{} operation"); 1471 goto invalid; 1472 } 1473 continue; 1474 } 1475 if (strcasecmp(key, "GOTO") == 0) { 1476 if (op != KEY_OP_ASSIGN) { 1477 Perror(space, "invalid GOTO operation"); 1478 goto invalid; 1479 } 1480 space->go_to = strdup(value); 1481 if (space->go_to == NULL) { 1482 err = -ENOMEM; 1483 break; 1484 } 1485 continue; 1486 } 1487 if (strcasecmp(key, "INCLUDE") == 0) { 1488 char *rootdir, *go_to; 1489 const char *filename; 1490 struct stat st; 1491 int linenum; 1492 if (op != KEY_OP_ASSIGN) { 1493 Perror(space, "invalid INCLUDE operation"); 1494 goto invalid; 1495 } 1496 if (value[0] == '/') 1497 strlcpy(string, value, sizeof(string)); 1498 else { 1499 strlcpy(string, space->rootdir, sizeof(string)); 1500 strlcat(string, "/", sizeof(string)); 1501 strlcat(string, value, sizeof(string)); 1502 } 1503 rootdir = space->rootdir; 1504 go_to = space->go_to; 1505 filename = space->filename; 1506 linenum = space->linenum; 1507 if (stat(string, &st)) { 1508 Perror(space, "invalid filename '%s'", string); 1509 continue; 1510 } 1511 if (S_ISDIR(st.st_mode)) { 1512 struct dirent **list; 1513 int i, num; 1514 num = scandir(string, &list, conf_name_filter, 1515 alphasort); 1516 if (num < 0) { 1517 Perror(space, "invalid directory '%s'", string); 1518 continue; 1519 } 1520 count = strlen(string); 1521 for (i = 0; i < num; i++) { 1522 string[count] = '\0'; 1523 strlcat(string, "/", sizeof(string)); 1524 strlcat(string, list[i]->d_name, sizeof(string)); 1525 space->go_to = NULL; 1526 space->rootdir = new_root_dir(string); 1527 free(list[i]); 1528 if (space->rootdir) { 1529 err = parse(space, string); 1530 free(space->rootdir); 1531 } else 1532 err = -ENOMEM; 1533 if (space->go_to) { 1534 Perror(space, "unterminated GOTO '%s'", space->go_to); 1535 free(space->go_to); 1536 } 1537 if (err) 1538 break; 1539 } 1540 free(list); 1541 } else { 1542 space->go_to = NULL; 1543 space->rootdir = new_root_dir(string); 1544 if (space->rootdir) { 1545 err = parse(space, string); 1546 free(space->rootdir); 1547 } else 1548 err = -ENOMEM; 1549 if (space->go_to) { 1550 Perror(space, "unterminated GOTO '%s'", space->go_to); 1551 free(space->go_to); 1552 } 1553 } 1554 space->go_to = go_to; 1555 space->rootdir = rootdir; 1556 space->filename = filename; 1557 space->linenum = linenum; 1558 if (space->quit) 1559 break; 1560 if (err) 1561 break; 1562 continue; 1563 } 1564 if (strncasecmp(key, "ACCESS", 6) == 0) { 1565 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1566 if (value[0] == '$') { 1567 strlcpy(string, value, sizeof(string)); 1568 apply_format(space, string, sizeof(string)); 1569 if (string[0] == '/') 1570 goto __access1; 1571 } 1572 if (value[0] != '/') { 1573 strlcpy(string, space->rootdir, sizeof(string)); 1574 strlcat(string, "/", sizeof(string)); 1575 strlcat(string, value, sizeof(string)); 1576 } else { 1577 strlcpy(string, value, sizeof(string)); 1578 } 1579 apply_format(space, string, sizeof(string)); 1580 __access1: 1581 count = access(string, F_OK); 1582 dbg("access(%s) = %i (%s)", string, count, value); 1583 if (op == KEY_OP_MATCH && count != 0) 1584 break; 1585 if (op == KEY_OP_NOMATCH && count == 0) 1586 break; 1587 } else { 1588 Perror(space, "invalid ACCESS operation"); 1589 goto invalid; 1590 } 1591 continue; 1592 } 1593 if (strncasecmp(key, "PRINT", 5) == 0) { 1594 if (op != KEY_OP_ASSIGN) { 1595 Perror(space, "invalid PRINT operation"); 1596 goto invalid; 1597 } 1598 strlcpy(string, value, sizeof(string)); 1599 apply_format(space, string, sizeof(string)); 1600 fwrite(string, strlen(string), 1, stdout); 1601 continue; 1602 } 1603 if (strncasecmp(key, "ERROR", 5) == 0) { 1604 if (op != KEY_OP_ASSIGN) { 1605 Perror(space, "invalid ERROR operation"); 1606 goto invalid; 1607 } 1608 strlcpy(string, value, sizeof(string)); 1609 apply_format(space, string, sizeof(string)); 1610 fwrite(string, strlen(string), 1, stderr); 1611 continue; 1612 } 1613 if (strncasecmp(key, "EXIT", 4) == 0) { 1614 if (op != KEY_OP_ASSIGN) { 1615 Perror(space, "invalid EXIT operation"); 1616 goto invalid; 1617 } 1618 strlcpy(string, value, sizeof(string)); 1619 apply_format(space, string, sizeof(string)); 1620 if (strcmp(string, "return") == 0) 1621 return -EJUSTRETURN; 1622 space->exit_code = strtol(string, NULL, 0); 1623 space->quit = 1; 1624 break; 1625 } 1626 if (strncasecmp(key, "CONFIG{", 7) == 0) { 1627 attr = get_key_attribute(space, key + 6, string, sizeof(string)); 1628 if (attr == NULL) { 1629 Perror(space, "error parsing CONFIG attribute"); 1630 goto invalid; 1631 } 1632 strlcpy(result, value, sizeof(result)); 1633 apply_format(space, result, sizeof(result)); 1634 if (op == KEY_OP_ASSIGN) { 1635 err = value_set(space, attr, result); 1636 dbg("CONFIG{%s}='%s'", attr, result); 1637 break; 1638 } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1639 pair = value_find(space, attr); 1640 if (pair == NULL) 1641 break; 1642 if (!do_match(key, op, result, pair->value)) 1643 break; 1644 } else { 1645 Perror(space, "invalid CONFIG{} operation"); 1646 goto invalid; 1647 } 1648 } 1649 1650 Perror(space, "unknown key '%s'", key); 1651 } 1652 return err; 1653 1654invalid: 1655 Perror(space, "invalid rule"); 1656 return -EINVAL; 1657} 1658 1659static int parse(struct space *space, const char *filename) 1660{ 1661 char *buf, *bufline, *line; 1662 size_t bufsize, pos, count, linesize; 1663 unsigned int linenum, i, j, linenum_adj; 1664 int err; 1665 1666 dbg("start of file '%s'", filename); 1667 1668 if (file_map(filename, &buf, &bufsize) != 0) { 1669 err = errno; 1670 error("Unable to open file '%s': %s", filename, strerror(err)); 1671 return -err; 1672 } 1673 1674 err = 0; 1675 pos = 0; 1676 linenum = 0; 1677 linesize = 128; 1678 line = malloc(linesize); 1679 if (line == NULL) { 1680 file_unmap(buf, bufsize); 1681 return -ENOMEM; 1682 } 1683 space->filename = filename; 1684 while (!err && pos < bufsize && !space->quit) { 1685 count = line_width(buf, bufsize, pos); 1686 bufline = buf + pos; 1687 pos += count + 1; 1688 linenum++; 1689 1690 /* skip whitespaces */ 1691 while (count > 0 && isspace(bufline[0])) { 1692 bufline++; 1693 count--; 1694 } 1695 if (count == 0) 1696 continue; 1697 1698 /* comment check */ 1699 if (bufline[0] == '#') 1700 continue; 1701 1702 if (count > linesize - 1) { 1703 free(line); 1704 line = NULL; 1705 linesize = (count + 127 + 1) & ~127; 1706 if (linesize > 2048) { 1707 error("file %s, line %i too long", filename, linenum); 1708 err = -EINVAL; 1709 break; 1710 } 1711 line = malloc(linesize); 1712 if (line == NULL) { 1713 err = -EINVAL; 1714 break; 1715 } 1716 } 1717 1718 /* skip backslash and newline from multiline rules */ 1719 linenum_adj = 0; 1720 for (i = j = 0; i < count; i++) { 1721 if (bufline[i] == '\\' && bufline[i+1] == '\n') { 1722 linenum_adj++; 1723 continue; 1724 } 1725 line[j++] = bufline[i]; 1726 } 1727 line[j] = '\0'; 1728 1729 dbg("read (%i) '%s'", linenum, line); 1730 space->linenum = linenum; 1731 err = parse_line(space, line, linesize); 1732 if (err == -EJUSTRETURN) { 1733 err = 0; 1734 break; 1735 } 1736 linenum += linenum_adj; 1737 } 1738 1739 free(line); 1740 space->filename = NULL; 1741 space->linenum = -1; 1742 file_unmap(buf, bufsize); 1743 dbg("end of file '%s'", filename); 1744 return err ? err : -abs(space->exit_code); 1745} 1746 1747int init(const char *cfgdir, const char *filename, int flags, const char *cardname) 1748{ 1749 struct space *space; 1750 struct snd_card_iterator iter; 1751 int err = 0, lasterr = 0; 1752 1753 sysfs_init(); 1754 err = snd_card_iterator_sinit(&iter, cardname); 1755 if (err < 0) 1756 goto out; 1757 while (snd_card_iterator_next(&iter)) { 1758 err = snd_card_clean_cfgdir(cfgdir, iter.card); 1759 if (err < 0) { 1760 if (lasterr == 0) 1761 lasterr = err; 1762 continue; 1763 } 1764 err = init_ucm(flags, iter.card); 1765 if (err == 0) 1766 continue; 1767 err = init_space(&space, iter.card); 1768 if (err != 0) 1769 continue; 1770 space->rootdir = new_root_dir(filename); 1771 if (space->rootdir != NULL) { 1772 err = parse(space, filename); 1773 if (!cardname && err <= -99) { /* non-fatal errors */ 1774 if (lasterr == 0) 1775 lasterr = err; 1776 err = 0; 1777 } 1778 } 1779 free_space(space); 1780 if (err < 0) 1781 goto out; 1782 } 1783 err = lasterr ? lasterr : snd_card_iterator_error(&iter); 1784out: 1785 sysfs_cleanup(); 1786 return err; 1787} 1788