init_parse.c (47297B)
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <alsa/asoundlib.h> 41 #include "aconfig.h" 42 #include "alsactl.h" 43 #include "list.h" 44 45 #define PATH_SIZE 512 46 #define NAME_SIZE 128 47 #define EJUSTRETURN 0x7fffffff 48 49 enum key_op { 50 KEY_OP_UNSET, 51 KEY_OP_MATCH, 52 KEY_OP_NOMATCH, 53 KEY_OP_ADD, 54 KEY_OP_ASSIGN, 55 KEY_OP_ASSIGN_FINAL 56 }; 57 58 struct pair { 59 char *key; 60 char *value; 61 struct pair *next; 62 }; 63 64 struct space { 65 struct pair *pairs; 66 char *rootdir; 67 char *go_to; 68 char *program_result; 69 const char *filename; 70 int linenum; 71 int log_run; 72 int exit_code; 73 int quit; 74 unsigned int ctl_id_changed; 75 snd_hctl_t *ctl_handle; 76 snd_ctl_card_info_t *ctl_card_info; 77 snd_ctl_elem_id_t *ctl_id; 78 snd_ctl_elem_info_t *ctl_info; 79 snd_ctl_elem_value_t *ctl_value; 80 }; 81 82 static void Perror(struct space *space, const char *fmt, ...) 83 { 84 va_list arg; 85 va_start(arg, fmt); 86 fprintf(stderr, "%s:%i: ", space->filename, space->linenum); 87 vfprintf(stderr, fmt, arg); 88 putc('\n', stderr); 89 va_end(arg); 90 } 91 92 #include "init_sysdeps.c" 93 #include "init_utils_string.c" 94 #include "init_utils_run.c" 95 #include "init_sysfs.c" 96 97 static void free_space(struct space *space) 98 { 99 struct pair *pair = space->pairs; 100 struct pair *next = pair; 101 102 while (next) { 103 pair = next; 104 next = pair->next; 105 free(pair->value); 106 free(pair->key); 107 free(pair); 108 } 109 space->pairs = NULL; 110 if (space->ctl_value) { 111 snd_ctl_elem_value_free(space->ctl_value); 112 space->ctl_value = NULL; 113 } 114 if (space->ctl_info) { 115 snd_ctl_elem_info_free(space->ctl_info); 116 space->ctl_info = NULL; 117 } 118 if (space->ctl_id) { 119 snd_ctl_elem_id_free(space->ctl_id); 120 space->ctl_id = NULL; 121 } 122 if (space->ctl_card_info) { 123 snd_ctl_card_info_free(space->ctl_card_info); 124 space->ctl_card_info = NULL; 125 } 126 if (space->ctl_handle) { 127 snd_hctl_close(space->ctl_handle); 128 space->ctl_handle = NULL; 129 } 130 if (space->rootdir) 131 free(space->rootdir); 132 if (space->program_result) 133 free(space->program_result); 134 if (space->go_to) 135 free(space->go_to); 136 free(space); 137 } 138 139 static struct pair *value_find(struct space *space, const char *key) 140 { 141 struct pair *pair = space->pairs; 142 143 while (pair && strcmp(pair->key, key) != 0) 144 pair = pair->next; 145 return pair; 146 } 147 148 static int value_set(struct space *space, const char *key, const char *value) 149 { 150 struct pair *pair; 151 152 pair = value_find(space, key); 153 if (pair) { 154 free(pair->value); 155 pair->value = strdup(value); 156 if (pair->value == NULL) 157 return -ENOMEM; 158 } else { 159 pair = malloc(sizeof(struct pair)); 160 if (pair == NULL) 161 return -ENOMEM; 162 pair->key = strdup(key); 163 if (pair->key == NULL) { 164 free(pair); 165 return -ENOMEM; 166 } 167 pair->value = strdup(value); 168 if (pair->value == NULL) { 169 free(pair->key); 170 free(pair); 171 return -ENOMEM; 172 } 173 pair->next = space->pairs; 174 space->pairs = pair; 175 } 176 return 0; 177 } 178 179 static int init_space(struct space **space, int card) 180 { 181 struct space *res; 182 char device[16]; 183 int err; 184 185 res = calloc(1, sizeof(struct space)); 186 if (res == NULL) 187 return -ENOMEM; 188 res->ctl_id_changed = ~0; 189 res->linenum = -1; 190 sprintf(device, "hw:%u", card); 191 err = snd_hctl_open(&res->ctl_handle, device, 0); 192 if (err < 0) 193 goto error; 194 err = snd_hctl_load(res->ctl_handle); 195 if (err < 0) 196 goto error; 197 err = snd_ctl_card_info_malloc(&res->ctl_card_info); 198 if (err < 0) 199 goto error; 200 err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info); 201 if (err < 0) 202 goto error; 203 err = snd_ctl_elem_id_malloc(&res->ctl_id); 204 if (err < 0) 205 goto error; 206 err = snd_ctl_elem_info_malloc(&res->ctl_info); 207 if (err < 0) 208 goto error; 209 err = snd_ctl_elem_value_malloc(&res->ctl_value); 210 if (err < 0) 211 goto error; 212 *space = res; 213 return 0; 214 error: 215 free_space(res); 216 return err; 217 } 218 219 static const char *cardinfo_get(struct space *space, const char *attr) 220 { 221 if (strncasecmp(attr, "CARD", 4) == 0) { 222 static char res[16]; 223 sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info)); 224 return res; 225 } 226 if (strncasecmp(attr, "ID", 2) == 0) 227 return snd_ctl_card_info_get_id(space->ctl_card_info); 228 if (strncasecmp(attr, "DRIVER", 6) == 0) 229 return snd_ctl_card_info_get_driver(space->ctl_card_info); 230 if (strncasecmp(attr, "NAME", 4) == 0) 231 return snd_ctl_card_info_get_name(space->ctl_card_info); 232 if (strncasecmp(attr, "LONGNAME", 8) == 0) 233 return snd_ctl_card_info_get_longname(space->ctl_card_info); 234 if (strncasecmp(attr, "MIXERNAME", 9) == 0) 235 return snd_ctl_card_info_get_mixername(space->ctl_card_info); 236 if (strncasecmp(attr, "COMPONENTS", 10) == 0) 237 return snd_ctl_card_info_get_components(space->ctl_card_info); 238 Perror(space, "unknown cardinfo{} attribute '%s'", attr); 239 return NULL; 240 } 241 242 static int check_id_changed(struct space *space, unsigned int what) 243 { 244 snd_hctl_elem_t *elem; 245 int err; 246 247 if ((space->ctl_id_changed & what & 1) != 0) { 248 snd_ctl_elem_id_set_numid(space->ctl_id, 0); 249 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 250 if (!elem) 251 return -ENOENT; 252 err = snd_hctl_elem_info(elem, space->ctl_info); 253 if (err == 0) 254 space->ctl_id_changed &= ~1; 255 return err; 256 } 257 if ((space->ctl_id_changed & what & 2) != 0) { 258 snd_ctl_elem_id_set_numid(space->ctl_id, 0); 259 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 260 if (!elem) 261 return -ENOENT; 262 err = snd_hctl_elem_read(elem, space->ctl_value); 263 if (err == 0) 264 space->ctl_id_changed &= ~2; 265 return err; 266 } 267 return 0; 268 } 269 270 static const char *get_ctl_value(struct space *space) 271 { 272 snd_ctl_elem_type_t type; 273 unsigned int idx, count; 274 static char res[1024], tmp[16]; 275 static const char hex[] = "0123456789abcdef"; 276 char *pos; 277 const char *pos1; 278 279 type = snd_ctl_elem_info_get_type(space->ctl_info); 280 count = snd_ctl_elem_info_get_count(space->ctl_info); 281 res[0] = '\0'; 282 switch (type) { 283 case SND_CTL_ELEM_TYPE_BOOLEAN: 284 for (idx = 0; idx < count; idx++) { 285 if (idx > 0) 286 strlcat(res, ",", sizeof(res)); 287 strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res)); 288 } 289 break; 290 case SND_CTL_ELEM_TYPE_INTEGER: 291 for (idx = 0; idx < count; idx++) { 292 if (idx > 0) 293 strlcat(res, ",", sizeof(res)); 294 snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx)); 295 strlcat(res, tmp, sizeof(res)); 296 } 297 break; 298 case SND_CTL_ELEM_TYPE_INTEGER64: 299 for (idx = 0; idx < count; idx++) { 300 if (idx > 0) 301 strlcat(res, ",", sizeof(res)); 302 snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx)); 303 strlcat(res, tmp, sizeof(res)); 304 } 305 break; 306 case SND_CTL_ELEM_TYPE_ENUMERATED: 307 for (idx = 0; idx < count; idx++) { 308 if (idx > 0) 309 strlcat(res, ",", sizeof(res)); 310 snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx)); 311 strlcat(res, tmp, sizeof(res)); 312 } 313 break; 314 case SND_CTL_ELEM_TYPE_BYTES: 315 case SND_CTL_ELEM_TYPE_IEC958: 316 if (type == SND_CTL_ELEM_TYPE_IEC958) 317 count = sizeof(snd_aes_iec958_t); 318 if (count > (sizeof(res)-1)/2) 319 count = (sizeof(res)-1/2); 320 pos = res; 321 pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value); 322 while (count > 0) { 323 idx = *pos1++; 324 *pos++ = hex[idx >> 4]; 325 *pos++ = hex[idx & 0x0f]; 326 count++; 327 } 328 *pos++ = '\0'; 329 break; 330 default: 331 Perror(space, "unknown element type '%i'", type); 332 return NULL; 333 } 334 return res; 335 } 336 337 /* Function to convert from percentage to volume. val = percentage */ 338 #define convert_prange1(val, min, max) \ 339 ceil((val) * ((max) - (min)) * 0.01 + (min)) 340 341 static int set_ctl_value(struct space *space, const char *value, int all) 342 { 343 snd_ctl_elem_type_t type; 344 unsigned int idx, idx2, count, items; 345 const char *pos, *pos2; 346 snd_hctl_elem_t *elem; 347 int val; 348 long lval; 349 350 type = snd_ctl_elem_info_get_type(space->ctl_info); 351 count = snd_ctl_elem_info_get_count(space->ctl_info); 352 switch (type) { 353 case SND_CTL_ELEM_TYPE_BOOLEAN: 354 for (idx = 0; idx < count; idx++) { 355 while (*value == ' ') 356 value++; 357 if (*value == '\0') 358 goto missing; 359 val = strncasecmp(value, "true", 4) == 0 || 360 strncasecmp(value, "yes", 3) == 0 || 361 strncasecmp(value, "on", 2) == 0 || 362 strncasecmp(value, "1", 1) == 0; 363 snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val); 364 if (all) 365 continue; 366 pos = strchr(value, ','); 367 value = pos ? pos + 1 : value + strlen(value) - 1; 368 } 369 break; 370 case SND_CTL_ELEM_TYPE_INTEGER: 371 for (idx = 0; idx < count; idx++) { 372 while (*value == ' ') 373 value++; 374 pos = strchr(value, ','); 375 if (pos) 376 *(char *)pos = '\0'; 377 remove_trailing_chars((char *)value, ' '); 378 items = pos ? pos - value : strlen(value); 379 if (items > 1 && value[items-1] == '%') { 380 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)); 381 snd_ctl_elem_value_set_integer(space->ctl_value, idx, val); 382 } else if (items > 2 && value[items-2] == 'd' && value[items-1] == 'B') { 383 val = strtol(value, NULL, 0) * 100; 384 if ((pos2 = strchr(value, '.')) != NULL) { 385 if (isdigit(*(pos2-1)) && isdigit(*(pos2-2))) { 386 if (val < 0) 387 val -= strtol(pos2 + 1, NULL, 0); 388 else 389 val += strtol(pos2 + 1, NULL, 0); 390 } else if (isdigit(*(pos2-1))) { 391 if (val < 0) 392 val -= strtol(pos2 + 1, NULL, 0) * 10; 393 else 394 val += strtol(pos2 + 1, NULL, 0) * 10; 395 } 396 } 397 val = snd_ctl_convert_from_dB(snd_hctl_ctl(space->ctl_handle), space->ctl_id, val, &lval, -1); 398 if (val < 0) { 399 dbg("unable to convert dB value '%s' to internal integer range", value); 400 return val; 401 } 402 snd_ctl_elem_value_set_integer(space->ctl_value, idx, lval); 403 } else { 404 snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0)); 405 } 406 if (all) 407 continue; 408 value = pos ? pos + 1 : value + strlen(value) - 1; 409 } 410 break; 411 case SND_CTL_ELEM_TYPE_INTEGER64: 412 for (idx = 0; idx < count; idx++) { 413 while (*value == ' ') 414 value++; 415 snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0)); 416 if (all) 417 continue; 418 pos = strchr(value, ','); 419 value = pos ? pos + 1 : value + strlen(value) - 1; 420 } 421 break; 422 case SND_CTL_ELEM_TYPE_ENUMERATED: 423 for (idx = 0; idx < count; idx++) { 424 while (*value == ' ') 425 value++; 426 pos = strchr(value, ','); 427 if (isdigit(value[0]) || value[0] == '-') { 428 snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0)); 429 } else { 430 if (pos) 431 *(char *)pos = '\0'; 432 remove_trailing_chars((char *)value, ' '); 433 items = snd_ctl_elem_info_get_items(space->ctl_info); 434 for (idx2 = 0; idx2 < items; idx2++) { 435 snd_ctl_elem_info_set_item(space->ctl_info, idx2); 436 elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id); 437 if (elem == NULL) 438 return -ENOENT; 439 val = snd_hctl_elem_info(elem, space->ctl_info); 440 if (val < 0) 441 return val; 442 if (strcasecmp(snd_ctl_elem_info_get_item_name(space->ctl_info), value) == 0) { 443 snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, idx2); 444 break; 445 } 446 } 447 if (idx2 >= items) { 448 Perror(space, "wrong enum identifier '%s'", value); 449 return -EINVAL; 450 } 451 } 452 if (all) 453 continue; 454 value = pos ? pos + 1 : value + strlen(value) - 1; 455 } 456 break; 457 case SND_CTL_ELEM_TYPE_BYTES: 458 case SND_CTL_ELEM_TYPE_IEC958: 459 if (type == SND_CTL_ELEM_TYPE_IEC958) 460 count = sizeof(snd_aes_iec958_t); 461 while (*value == ' ') 462 value++; 463 if (strlen(value) != count * 2) { 464 Perror(space, "bad ctl value hexa length (should be %u bytes)", count); 465 return -EINVAL; 466 } 467 for (idx = 0; idx < count; idx += 2) { 468 val = hextodigit(*(value++)) << 4; 469 val |= hextodigit(*(value++)); 470 if (val > 255) { 471 Perror(space, "bad ctl hexa value"); 472 return -EINVAL; 473 } 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 487 static 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 508 static int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id) 509 { 510 if (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 (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 (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 (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 527 static 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; 657 dbvalue: 658 sprintf(res, "%li.%02idB", (long)(val / 100), (int)abs(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, "%u", 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 748 static 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 838 static 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} */ 942 static char *get_key_attribute(struct space *space, char *str, char *res, size_t resize) 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, resize); 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 */ 966 static 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*/ 986 static 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 1004 static 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; 1088 found: 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, substitution 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 1242 static 1243 int run_program1(struct space *space, 1244 const char *command0, char *result, 1245 size_t resize, size_t *reslen, int log) 1246 { 1247 if (strncmp(command0, "__ctl_search", 12) == 0) { 1248 const char *res = elemid_get(space, "do_search"); 1249 if (res == NULL || strcmp(res, "1") != 0) 1250 return EXIT_FAILURE; 1251 return EXIT_SUCCESS; 1252 } 1253 if (strncmp(command0, "__ctl_count", 11) == 0) { 1254 const char *res = elemid_get(space, "do_count"); 1255 if (res == NULL || strcmp(res, "0") == 0) 1256 return EXIT_FAILURE; 1257 strlcpy(result, res, resize); 1258 return EXIT_SUCCESS; 1259 } 1260 Perror(space, "unknown buildin command '%s'", command0); 1261 return EXIT_FAILURE; 1262 } 1263 1264 static int parse(struct space *space, const char *filename); 1265 1266 static char *new_root_dir(const char *filename) 1267 { 1268 char *res, *tmp; 1269 1270 res = strdup(filename); 1271 if (res) { 1272 tmp = strrchr(res, '/'); 1273 if (tmp) 1274 *tmp = '\0'; 1275 } 1276 dbg("new_root_dir '%s' '%s'", filename, res); 1277 return res; 1278 } 1279 1280 /* return non-zero if the file name has the extension ".conf" */ 1281 static int conf_name_filter(const struct dirent *d) 1282 { 1283 char *ext = strrchr(d->d_name, '.'); 1284 return ext && !strcmp(ext, ".conf"); 1285 } 1286 1287 static int parse_line(struct space *space, char *line, size_t linesize) 1288 { 1289 char *linepos; 1290 char *key, *value, *attr, *temp; 1291 struct pair *pair; 1292 enum key_op op; 1293 int err = 0, count; 1294 char string[PATH_SIZE]; 1295 char result[PATH_SIZE]; 1296 1297 linepos = line; 1298 while (*linepos != '\0') { 1299 op = KEY_OP_UNSET; 1300 1301 err = get_key(&linepos, &key, &op, &value); 1302 if (err < 0) 1303 goto invalid; 1304 1305 if (strncasecmp(key, "LABEL", 5) == 0) { 1306 if (op != KEY_OP_ASSIGN) { 1307 Perror(space, "invalid LABEL operation"); 1308 goto invalid; 1309 } 1310 if (space->go_to && strcmp(space->go_to, value) == 0) { 1311 free(space->go_to); 1312 space->go_to = NULL; 1313 } 1314 continue; 1315 } 1316 1317 if (space->go_to) { 1318 dbg("skip (GOTO '%s')", space->go_to); 1319 break; /* not for us */ 1320 } 1321 1322 if (strncasecmp(key, "CTL{", 4) == 0) { 1323 attr = get_key_attribute(space, key + 3, string, sizeof(string)); 1324 if (attr == NULL) { 1325 Perror(space, "error parsing CTL attribute"); 1326 goto invalid; 1327 } 1328 if (op == KEY_OP_ASSIGN) { 1329 strlcpy(result, value, sizeof(result)); 1330 apply_format(space, result, sizeof(result)); 1331 dbg("ctl assign: '%s' '%s'", value, attr); 1332 err = elemid_set(space, attr, result); 1333 if (space->program_result) { 1334 free(space->program_result); 1335 space->program_result = NULL; 1336 } 1337 snprintf(string, sizeof(string), "%i", err); 1338 space->program_result = strdup(string); 1339 err = 0; 1340 if (space->program_result == NULL) 1341 break; 1342 } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1343 if (strncmp(attr, "write", 5) == 0) { 1344 strlcpy(result, value, sizeof(result)); 1345 apply_format(space, result, sizeof(result)); 1346 dbg("ctl write: '%s' '%s'", value, attr); 1347 err = elemid_set(space, "values", result); 1348 if (err == 0 && op == KEY_OP_NOMATCH) 1349 break; 1350 if (err != 0 && op == KEY_OP_MATCH) 1351 break; 1352 } else { 1353 temp = (char *)elemid_get(space, attr); 1354 dbg("ctl match: '%s' '%s' '%s'", attr, value, temp); 1355 if (!do_match(key, op, value, temp)) 1356 break; 1357 } 1358 } else { 1359 Perror(space, "invalid CTL{} operation"); 1360 goto invalid; 1361 } 1362 continue; 1363 } 1364 if (strcasecmp(key, "RESULT") == 0) { 1365 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1366 if (!do_match(key, op, value, space->program_result)) 1367 break; 1368 } else if (op == KEY_OP_ASSIGN) { 1369 if (space->program_result) { 1370 free(space->program_result); 1371 space->program_result = NULL; 1372 } 1373 strlcpy(string, value, sizeof(string)); 1374 apply_format(space, string, sizeof(string)); 1375 space->program_result = strdup(string); 1376 if (space->program_result == NULL) 1377 break; 1378 } else { 1379 Perror(space, "invalid RESULT operation"); 1380 goto invalid; 1381 } 1382 continue; 1383 } 1384 if (strcasecmp(key, "PROGRAM") == 0) { 1385 if (op == KEY_OP_UNSET) 1386 continue; 1387 strlcpy(string, value, sizeof(string)); 1388 apply_format(space, string, sizeof(string)); 1389 if (space->program_result) { 1390 free(space->program_result); 1391 space->program_result = NULL; 1392 } 1393 if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) { 1394 dbg("PROGRAM '%s' is false", string); 1395 if (op != KEY_OP_NOMATCH) 1396 break; 1397 } else { 1398 remove_trailing_chars(result, '\n'); 1399 count = replace_untrusted_chars(result); 1400 if (count) 1401 info("%i untrusted character(s) replaced", count); 1402 dbg("PROGRAM '%s' result is '%s'", string, result); 1403 space->program_result = strdup(result); 1404 if (space->program_result == NULL) 1405 break; 1406 dbg("PROGRAM returned successful"); 1407 if (op == KEY_OP_NOMATCH) 1408 break; 1409 } 1410 dbg("PROGRAM key is true"); 1411 continue; 1412 } 1413 if (strncasecmp(key, "CARDINFO{", 9) == 0) { 1414 attr = get_key_attribute(space, key + 8, string, sizeof(string)); 1415 if (attr == NULL) { 1416 Perror(space, "error parsing CARDINFO attribute"); 1417 goto invalid; 1418 } 1419 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1420 dbg("cardinfo: '%s' '%s'", value, attr); 1421 temp = (char *)cardinfo_get(space, attr); 1422 if (!do_match(key, op, value, temp)) 1423 break; 1424 } else { 1425 Perror(space, "invalid CARDINFO{} operation"); 1426 goto invalid; 1427 } 1428 continue; 1429 } 1430 if (strncasecmp(key, "ATTR{", 5) == 0) { 1431 attr = get_key_attribute(space, key + 4, string, sizeof(string)); 1432 if (attr == NULL) { 1433 Perror(space, "error parsing ATTR attribute"); 1434 goto invalid; 1435 } 1436 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1437 pair = value_find(space, "sysfs_device"); 1438 if (pair == NULL) 1439 break; 1440 dbg("sysfs_attr: '%s' '%s'", pair->value, attr); 1441 temp = sysfs_attr_get_value(pair->value, attr); 1442 if (!do_match(key, op, value, temp)) 1443 break; 1444 } else { 1445 Perror(space, "invalid ATTR{} operation"); 1446 goto invalid; 1447 } 1448 continue; 1449 } 1450 if (strncasecmp(key, "ENV{", 4) == 0) { 1451 attr = get_key_attribute(space, key + 3, string, sizeof(string)); 1452 if (attr == NULL) { 1453 Perror(space, "error parsing ENV attribute"); 1454 goto invalid; 1455 } 1456 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1457 temp = getenv(attr); 1458 dbg("env: '%s' '%s'", attr, temp); 1459 if (!do_match(key, op, value, temp)) 1460 break; 1461 } else if (op == KEY_OP_ASSIGN || 1462 op == KEY_OP_ASSIGN_FINAL) { 1463 strlcpy(result, value, sizeof(result)); 1464 apply_format(space, result, sizeof(result)); 1465 dbg("env set: '%s' '%s'", attr, result); 1466 if (setenv(attr, result, op == KEY_OP_ASSIGN_FINAL)) 1467 break; 1468 } else { 1469 Perror(space, "invalid ENV{} operation"); 1470 goto invalid; 1471 } 1472 continue; 1473 } 1474 if (strcasecmp(key, "GOTO") == 0) { 1475 if (op != KEY_OP_ASSIGN) { 1476 Perror(space, "invalid GOTO operation"); 1477 goto invalid; 1478 } 1479 space->go_to = strdup(value); 1480 if (space->go_to == NULL) { 1481 err = -ENOMEM; 1482 break; 1483 } 1484 continue; 1485 } 1486 if (strcasecmp(key, "INCLUDE") == 0) { 1487 char *rootdir, *go_to; 1488 const char *filename; 1489 struct stat st; 1490 int linenum; 1491 if (op != KEY_OP_ASSIGN) { 1492 Perror(space, "invalid INCLUDE operation"); 1493 goto invalid; 1494 } 1495 if (value[0] == '/') 1496 strlcpy(string, value, sizeof(string)); 1497 else { 1498 strlcpy(string, space->rootdir, sizeof(string)); 1499 strlcat(string, "/", sizeof(string)); 1500 strlcat(string, value, sizeof(string)); 1501 } 1502 rootdir = space->rootdir; 1503 go_to = space->go_to; 1504 filename = space->filename; 1505 linenum = space->linenum; 1506 if (stat(string, &st)) { 1507 Perror(space, "invalid filename '%s'", string); 1508 continue; 1509 } 1510 if (S_ISDIR(st.st_mode)) { 1511 struct dirent **list; 1512 int i, num; 1513 num = scandir(string, &list, conf_name_filter, 1514 alphasort); 1515 if (num < 0) { 1516 Perror(space, "invalid directory '%s'", string); 1517 continue; 1518 } 1519 count = strlen(string); 1520 for (i = 0; i < num; i++) { 1521 string[count] = '\0'; 1522 strlcat(string, "/", sizeof(string)); 1523 strlcat(string, list[i]->d_name, sizeof(string)); 1524 space->go_to = NULL; 1525 space->rootdir = new_root_dir(string); 1526 free(list[i]); 1527 if (space->rootdir) { 1528 err = parse(space, string); 1529 free(space->rootdir); 1530 } else 1531 err = -ENOMEM; 1532 if (space->go_to) { 1533 Perror(space, "unterminated GOTO '%s'", space->go_to); 1534 free(space->go_to); 1535 } 1536 if (err) 1537 break; 1538 } 1539 free(list); 1540 } else { 1541 space->go_to = NULL; 1542 space->rootdir = new_root_dir(string); 1543 if (space->rootdir) { 1544 err = parse(space, string); 1545 free(space->rootdir); 1546 } else 1547 err = -ENOMEM; 1548 if (space->go_to) { 1549 Perror(space, "unterminated GOTO '%s'", space->go_to); 1550 free(space->go_to); 1551 } 1552 } 1553 space->go_to = go_to; 1554 space->rootdir = rootdir; 1555 space->filename = filename; 1556 space->linenum = linenum; 1557 if (space->quit) 1558 break; 1559 if (err) 1560 break; 1561 continue; 1562 } 1563 if (strncasecmp(key, "ACCESS", 6) == 0) { 1564 if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1565 if (value[0] == '$') { 1566 strlcpy(string, value, sizeof(string)); 1567 apply_format(space, string, sizeof(string)); 1568 if (string[0] == '/') 1569 goto __access1; 1570 } 1571 if (value[0] != '/') { 1572 strlcpy(string, space->rootdir, sizeof(string)); 1573 strlcat(string, "/", sizeof(string)); 1574 strlcat(string, value, sizeof(string)); 1575 } else { 1576 strlcpy(string, value, sizeof(string)); 1577 } 1578 apply_format(space, string, sizeof(string)); 1579 __access1: 1580 count = access(string, F_OK); 1581 dbg("access(%s) = %i (%s)", string, count, value); 1582 if (op == KEY_OP_MATCH && count != 0) 1583 break; 1584 if (op == KEY_OP_NOMATCH && count == 0) 1585 break; 1586 } else { 1587 Perror(space, "invalid ACCESS operation"); 1588 goto invalid; 1589 } 1590 continue; 1591 } 1592 if (strncasecmp(key, "PRINT", 5) == 0) { 1593 if (op != KEY_OP_ASSIGN) { 1594 Perror(space, "invalid PRINT operation"); 1595 goto invalid; 1596 } 1597 strlcpy(string, value, sizeof(string)); 1598 apply_format(space, string, sizeof(string)); 1599 fwrite(string, strlen(string), 1, stdout); 1600 continue; 1601 } 1602 if (strncasecmp(key, "ERROR", 5) == 0) { 1603 if (op != KEY_OP_ASSIGN) { 1604 Perror(space, "invalid ERROR operation"); 1605 goto invalid; 1606 } 1607 strlcpy(string, value, sizeof(string)); 1608 apply_format(space, string, sizeof(string)); 1609 fwrite(string, strlen(string), 1, stderr); 1610 continue; 1611 } 1612 if (strncasecmp(key, "EXIT", 4) == 0) { 1613 if (op != KEY_OP_ASSIGN) { 1614 Perror(space, "invalid EXIT operation"); 1615 goto invalid; 1616 } 1617 strlcpy(string, value, sizeof(string)); 1618 apply_format(space, string, sizeof(string)); 1619 if (strcmp(string, "return") == 0) 1620 return -EJUSTRETURN; 1621 space->exit_code = strtol(string, NULL, 0); 1622 space->quit = 1; 1623 break; 1624 } 1625 if (strncasecmp(key, "CONFIG{", 7) == 0) { 1626 attr = get_key_attribute(space, key + 6, string, sizeof(string)); 1627 if (attr == NULL) { 1628 Perror(space, "error parsing CONFIG attribute"); 1629 goto invalid; 1630 } 1631 strlcpy(result, value, sizeof(result)); 1632 apply_format(space, result, sizeof(result)); 1633 if (op == KEY_OP_ASSIGN) { 1634 err = value_set(space, attr, result); 1635 dbg("CONFIG{%s}='%s'", attr, result); 1636 break; 1637 } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) { 1638 pair = value_find(space, attr); 1639 if (pair == NULL) 1640 break; 1641 if (!do_match(key, op, result, pair->value)) 1642 break; 1643 } else { 1644 Perror(space, "invalid CONFIG{} operation"); 1645 goto invalid; 1646 } 1647 } 1648 1649 Perror(space, "unknown key '%s'", key); 1650 } 1651 return err; 1652 1653 invalid: 1654 Perror(space, "invalid rule"); 1655 return -EINVAL; 1656 } 1657 1658 static int parse(struct space *space, const char *filename) 1659 { 1660 char *buf, *bufline, *line; 1661 size_t bufsize, pos, count, linesize; 1662 unsigned int linenum, i, j, linenum_adj; 1663 int err; 1664 1665 dbg("start of file '%s'", filename); 1666 1667 if (file_map(filename, &buf, &bufsize) != 0) { 1668 err = errno; 1669 error("Unable to open file '%s': %s", filename, strerror(err)); 1670 return -err; 1671 } 1672 1673 err = 0; 1674 pos = 0; 1675 linenum = 0; 1676 linesize = 128; 1677 line = malloc(linesize); 1678 if (line == NULL) 1679 return -ENOMEM; 1680 space->filename = filename; 1681 while (!err && pos < bufsize && !space->quit) { 1682 count = line_width(buf, bufsize, pos); 1683 bufline = buf + pos; 1684 pos += count + 1; 1685 linenum++; 1686 1687 /* skip whitespaces */ 1688 while (count > 0 && isspace(bufline[0])) { 1689 bufline++; 1690 count--; 1691 } 1692 if (count == 0) 1693 continue; 1694 1695 /* comment check */ 1696 if (bufline[0] == '#') 1697 continue; 1698 1699 if (count > linesize - 1) { 1700 free(line); 1701 linesize = (count + 127 + 1) & ~127; 1702 if (linesize > 2048) { 1703 error("file %s, line %i too long", filename, linenum); 1704 err = -EINVAL; 1705 break; 1706 } 1707 line = malloc(linesize); 1708 if (line == NULL) { 1709 err = -EINVAL; 1710 break; 1711 } 1712 } 1713 1714 /* skip backslash and newline from multiline rules */ 1715 linenum_adj = 0; 1716 for (i = j = 0; i < count; i++) { 1717 if (bufline[i] == '\\' && bufline[i+1] == '\n') { 1718 linenum_adj++; 1719 continue; 1720 } 1721 line[j++] = bufline[i]; 1722 } 1723 line[j] = '\0'; 1724 1725 dbg("read (%i) '%s'", linenum, line); 1726 space->linenum = linenum; 1727 err = parse_line(space, line, linesize); 1728 if (err == -EJUSTRETURN) { 1729 err = 0; 1730 break; 1731 } 1732 linenum += linenum_adj; 1733 } 1734 1735 free(line); 1736 space->filename = NULL; 1737 space->linenum = -1; 1738 file_unmap(buf, bufsize); 1739 dbg("end of file '%s'", filename); 1740 return err ? err : -abs(space->exit_code); 1741 } 1742 1743 int init(const char *filename, const char *cardname) 1744 { 1745 struct space *space; 1746 int err = 0, card, first; 1747 1748 sysfs_init(); 1749 if (!cardname) { 1750 first = 1; 1751 card = -1; 1752 while (1) { 1753 if (snd_card_next(&card) < 0) 1754 break; 1755 if (card < 0) { 1756 if (first) { 1757 error("No soundcards found..."); 1758 return -ENODEV; 1759 } 1760 break; 1761 } 1762 first = 0; 1763 err = init_space(&space, card); 1764 if (err == 0) { 1765 space->rootdir = new_root_dir(filename); 1766 if (space->rootdir != NULL) 1767 err = parse(space, filename); 1768 free_space(space); 1769 } 1770 if (err < 0) 1771 break; 1772 } 1773 } else { 1774 card = snd_card_get_index(cardname); 1775 if (card < 0) { 1776 error("Cannot find soundcard '%s'...", cardname); 1777 goto error; 1778 } 1779 memset(&space, 0, sizeof(space)); 1780 err = init_space(&space, card); 1781 if (err == 0) { 1782 space->rootdir = new_root_dir(filename); 1783 if (space->rootdir != NULL) 1784 err = parse(space, filename); 1785 free_space(space); 1786 } 1787 } 1788 error: 1789 sysfs_cleanup(); 1790 return err; 1791 }