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