1 /* PipeWire
2 *
3 * Copyright © 2021 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "config.h"
26
27 #include <signal.h>
28 #include <getopt.h>
29 #include <limits.h>
30 #include <sys/stat.h>
31 #include <sys/mman.h>
32 #include <sys/types.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <sys/wait.h>
36 #if HAVE_PWD_H
37 #include <pwd.h>
38 #endif
39 #if defined(__FreeBSD__) || defined(__DragonFly__)
40 #define O_PATH 0
41 #endif
42
43 #include <spa/utils/result.h>
44 #include <spa/utils/string.h>
45 #include <spa/utils/json.h>
46
47 #include <pipewire/impl.h>
48
49 PW_LOG_TOPIC_EXTERN(log_conf);
50 #define PW_LOG_TOPIC_DEFAULT log_conf
51
make_path(char * path,int size,const char * paths[])52 static int make_path(char *path, int size, const char *paths[])
53 {
54 int i, len;
55 char *p = path;
56 for (i = 0; paths[i] != NULL; i++) {
57 len = snprintf(p, size, "%s%s", i == 0 ? "" : "/", paths[i]);
58 if (len < 0)
59 return -errno;
60 if (len >= size)
61 return -ENOSPC;
62 p += len;
63 size -= len;
64 }
65 return 0;
66 }
67
get_config_path(char * path,size_t size,const char * prefix,const char * name)68 static int get_config_path(char *path, size_t size, const char *prefix, const char *name)
69 {
70 const char *dir;
71 char buffer[4096];
72
73 if (name[0] == '/') {
74 const char *paths[] = { name, NULL };
75 if (make_path(path, size, paths) == 0 &&
76 access(path, R_OK) == 0)
77 return 1;
78 return -ENOENT;
79 }
80
81 if (prefix && prefix[0] == '/') {
82 const char *paths[] = { prefix, name, NULL };
83 if (make_path(path, size, paths) == 0 &&
84 access(path, R_OK) == 0)
85 return 1;
86 return -ENOENT;
87 }
88
89 if (prefix == NULL) {
90 prefix = name;
91 name = NULL;
92 }
93
94 if (pw_check_option("no-config", "true"))
95 goto no_config;
96
97 dir = getenv("PIPEWIRE_CONFIG_DIR");
98 if (dir != NULL) {
99 const char *paths[] = { dir, prefix, name, NULL };
100 if (make_path(path, size, paths) == 0 &&
101 access(path, R_OK) == 0)
102 return 1;
103 }
104
105 dir = getenv("XDG_CONFIG_HOME");
106 if (dir != NULL) {
107 const char *paths[] = { dir, "pipewire", prefix, name, NULL };
108 if (make_path(path, size, paths) == 0 &&
109 access(path, R_OK) == 0)
110 return 1;
111 }
112 dir = getenv("HOME");
113 if (dir == NULL) {
114 struct passwd pwd, *result = NULL;
115 if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0)
116 dir = result ? result->pw_dir : NULL;
117 }
118 if (dir != NULL) {
119 const char *paths[] = { dir, ".config", "pipewire", prefix, name, NULL };
120 if (make_path(path, size, paths) == 0 &&
121 access(path, R_OK) == 0)
122 return 1;
123 }
124
125 dir = PIPEWIRE_CONFIG_DIR;
126 if (dir != NULL) {
127 const char *paths[] = { dir, prefix, name, NULL };
128 if (make_path(path, size, paths) == 0 &&
129 access(path, R_OK) == 0)
130 return 1;
131 }
132 no_config:
133 dir = PIPEWIRE_CONFDATADIR;
134 if (dir != NULL) {
135 const char *paths[] = { dir, prefix, name, NULL };
136 if (make_path(path, size, paths) == 0 &&
137 access(path, R_OK) == 0)
138 return 1;
139 }
140 return 0;
141 }
142
get_state_path(char * path,size_t size,const char * prefix,const char * name)143 static int get_state_path(char *path, size_t size, const char *prefix, const char *name)
144 {
145 const char *dir;
146 char buffer[4096];
147
148 if (name[0] == '/') {
149 const char *paths[] = { name, NULL };
150 if (make_path(path, size, paths) == 0 &&
151 access(path, R_OK) == 0)
152 return 1;
153 return -ENOENT;
154 }
155
156 if (prefix && prefix[0] == '/') {
157 const char *paths[] = { prefix, name, NULL };
158 if (make_path(path, size, paths) == 0 &&
159 access(path, R_OK) == 0)
160 return 1;
161 return -ENOENT;
162 }
163
164 if (prefix == NULL) {
165 prefix = name;
166 name = NULL;
167 }
168
169 dir = getenv("PIPEWIRE_STATE_DIR");
170 if (dir != NULL) {
171 const char *paths[] = { dir, prefix, name, NULL };
172 if (make_path(path, size, paths) == 0 &&
173 access(path, R_OK) == 0)
174 return 1;
175 }
176
177 dir = getenv("XDG_STATE_HOME");
178 if (dir != NULL) {
179 const char *paths[] = { dir, "pipewire", prefix, name, NULL };
180 if (make_path(path, size, paths) == 0 &&
181 access(path, R_OK) == 0)
182 return 1;
183 }
184 dir = getenv("HOME");
185 if (dir == NULL) {
186 struct passwd pwd, *result = NULL;
187 if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0)
188 dir = result ? result->pw_dir : NULL;
189 }
190 if (dir != NULL) {
191 const char *paths[] = { dir, ".local", "state", "pipewire", prefix, name, NULL };
192 if (make_path(path, size, paths) == 0 &&
193 access(path, R_OK) == 0)
194 return 1;
195 }
196 if (dir != NULL) {
197 /* fallback for old XDG_CONFIG_HOME */
198 const char *paths[] = { dir, ".config", "pipewire", prefix, name, NULL };
199 if (make_path(path, size, paths) == 0 &&
200 access(path, R_OK) == 0)
201 return 1;
202 }
203
204 return 0;
205 }
206
ensure_path(char * path,int size,const char * paths[])207 static int ensure_path(char *path, int size, const char *paths[])
208 {
209 int i, len, mode;
210 char *p = path;
211
212 for (i = 0; paths[i] != NULL; i++) {
213 len = snprintf(p, size, "%s/", paths[i]);
214 if (len < 0)
215 return -errno;
216 if (len >= size)
217 return -ENOSPC;
218
219 p += len;
220 size -= len;
221
222 mode = X_OK;
223 if (paths[i+1] == NULL)
224 mode |= R_OK | W_OK;
225
226 if (access(path, mode) < 0) {
227 if (errno != ENOENT)
228 return -errno;
229 if (mkdir(path, 0700) < 0) {
230 pw_log_info("Can't create directory %s: %m", path);
231 return -errno;
232 }
233 if (access(path, mode) < 0)
234 return -errno;
235
236 pw_log_info("created directory %s", path);
237 }
238 }
239 return 0;
240 }
241
open_write_dir(char * path,int size,const char * prefix)242 static int open_write_dir(char *path, int size, const char *prefix)
243 {
244 const char *dir;
245 char buffer[4096];
246 int res;
247
248 if (prefix != NULL && prefix[0] == '/') {
249 const char *paths[] = { prefix, NULL };
250 if (ensure_path(path, size, paths) == 0)
251 goto found;
252 }
253 dir = getenv("XDG_STATE_HOME");
254 if (dir != NULL) {
255 const char *paths[] = { dir, "pipewire", prefix, NULL };
256 if (ensure_path(path, size, paths) == 0)
257 goto found;
258 }
259 dir = getenv("HOME");
260 if (dir == NULL) {
261 struct passwd pwd, *result = NULL;
262 if (getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &result) == 0)
263 dir = result ? result->pw_dir : NULL;
264 }
265 if (dir != NULL) {
266 const char *paths[] = { dir, ".local", "state", "pipewire", prefix, NULL };
267 if (ensure_path(path, size, paths) == 0)
268 goto found;
269 }
270 return -ENOENT;
271 found:
272 if ((res = open(path, O_CLOEXEC | O_DIRECTORY | O_PATH)) < 0) {
273 pw_log_error("Can't open state directory %s: %m", path);
274 return -errno;
275 }
276 return res;
277 }
278
279 SPA_EXPORT
pw_conf_save_state(const char * prefix,const char * name,struct pw_properties * conf)280 int pw_conf_save_state(const char *prefix, const char *name, struct pw_properties *conf)
281 {
282 char path[PATH_MAX];
283 char *tmp_name;
284 int res, sfd, fd, count = 0;
285 FILE *f;
286
287 if ((sfd = open_write_dir(path, sizeof(path), prefix)) < 0)
288 return sfd;
289
290 tmp_name = alloca(strlen(name)+5);
291 sprintf(tmp_name, "%s.tmp", name);
292 if ((fd = openat(sfd, tmp_name, O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 0600)) < 0) {
293 pw_log_error("can't open file '%s': %m", tmp_name);
294 res = -errno;
295 goto error;
296 }
297
298 f = fdopen(fd, "w");
299 fprintf(f, "{");
300 count += pw_properties_serialize_dict(f, &conf->dict, PW_PROPERTIES_FLAG_NL);
301 fprintf(f, "%s}", count == 0 ? " " : "\n");
302 fclose(f);
303
304 if (renameat(sfd, tmp_name, sfd, name) < 0) {
305 pw_log_error("can't rename temp file '%s': %m", tmp_name);
306 res = -errno;
307 goto error;
308 }
309 res = 0;
310 pw_log_info("%p: saved state '%s%s'", conf, path, name);
311 error:
312 close(sfd);
313 return res;
314 }
315
conf_load(const char * path,struct pw_properties * conf)316 static int conf_load(const char *path, struct pw_properties *conf)
317 {
318 char *data;
319 struct stat sbuf;
320 int fd;
321
322 if ((fd = open(path, O_CLOEXEC | O_RDONLY)) < 0) {
323 pw_log_warn("%p: error loading config '%s': %m", conf, path);
324 return -errno;
325 }
326
327 pw_log_info("%p: loading config '%s'", conf, path);
328 if (fstat(fd, &sbuf) < 0)
329 goto error_close;
330 if ((data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
331 goto error_close;
332 close(fd);
333
334 pw_properties_update_string(conf, data, sbuf.st_size);
335 munmap(data, sbuf.st_size);
336
337 return 0;
338
339 error_close:
340 close(fd);
341 return -errno;
342 }
343
344 SPA_EXPORT
pw_conf_load_conf(const char * prefix,const char * name,struct pw_properties * conf)345 int pw_conf_load_conf(const char *prefix, const char *name, struct pw_properties *conf)
346 {
347 char path[PATH_MAX];
348
349 if (name == NULL) {
350 pw_log_debug("%p: config name must not be NULL", conf);
351 return -EINVAL;
352 }
353
354 if (get_config_path(path, sizeof(path), prefix, name) == 0) {
355 pw_log_debug("%p: can't load config '%s': %m", conf, path);
356 return -ENOENT;
357 }
358
359 return conf_load(path, conf);
360 }
361
362 SPA_EXPORT
pw_conf_load_state(const char * prefix,const char * name,struct pw_properties * conf)363 int pw_conf_load_state(const char *prefix, const char *name, struct pw_properties *conf)
364 {
365 char path[PATH_MAX];
366
367 if (name == NULL) {
368 pw_log_debug("%p: config name must not be NULL", conf);
369 return -EINVAL;
370 }
371
372 if (get_state_path(path, sizeof(path), prefix, name) == 0) {
373 pw_log_debug("%p: can't load config '%s': %m", conf, path);
374 return -ENOENT;
375 }
376
377 return conf_load(path, conf);
378 }
379
380 /* context.spa-libs = {
381 * <factory-name regex> = <library-name>
382 * }
383 */
parse_spa_libs(struct pw_context * context,char * str)384 static int parse_spa_libs(struct pw_context *context, char *str)
385 {
386 struct spa_json it[2];
387 char key[512], value[512];
388 int count = 0;
389
390 spa_json_init(&it[0], str, strlen(str));
391 if (spa_json_enter_object(&it[0], &it[1]) < 0) {
392 pw_log_error("config file error: context.spa-libs is not an object");
393 return -EINVAL;
394 }
395
396 while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
397 if (spa_json_get_string(&it[1], value, sizeof(value)) > 0) {
398 pw_context_add_spa_lib(context, key, value);
399 count++;
400 }
401 }
402 return count;
403 }
404
load_module(struct pw_context * context,const char * key,const char * args,const char * flags)405 static int load_module(struct pw_context *context, const char *key, const char *args, const char *flags)
406 {
407 if (pw_context_load_module(context, key, args, NULL) == NULL) {
408 if (errno == ENOENT && flags && strstr(flags, "ifexists") != NULL) {
409 pw_log_info("%p: skipping unavailable module %s",
410 context, key);
411 } else if (flags == NULL || strstr(flags, "nofail") == NULL) {
412 pw_log_error("%p: could not load mandatory module \"%s\": %m",
413 context, key);
414 return -errno;
415 } else {
416 pw_log_info("%p: could not load optional module \"%s\": %m",
417 context, key);
418 }
419 } else {
420 pw_log_info("%p: loaded module %s", context, key);
421 }
422 return 0;
423 }
424
425 /*
426 * context.modules = [
427 * { name = <module-name>
428 * [ args = { <key> = <value> ... } ]
429 * [ flags = [ [ ifexists ] [ nofail ] ]
430 * }
431 * ]
432 */
parse_modules(struct pw_context * context,char * str)433 static int parse_modules(struct pw_context *context, char *str)
434 {
435 struct spa_json it[3];
436 char key[512];
437 int res = 0, count = 0;
438
439 spa_json_init(&it[0], str, strlen(str));
440 if (spa_json_enter_array(&it[0], &it[1]) < 0) {
441 pw_log_error("config file error: context.modules is not an array");
442 return -EINVAL;
443 }
444
445 while (spa_json_enter_object(&it[1], &it[2]) > 0) {
446 char *name = NULL, *args = NULL, *flags = NULL;
447
448 while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
449 const char *val;
450 int len;
451
452 if ((len = spa_json_next(&it[2], &val)) <= 0)
453 break;
454
455 if (spa_streq(key, "name")) {
456 name = (char*)val;
457 spa_json_parse_stringn(val, len, name, len+1);
458 } else if (spa_streq(key, "args")) {
459 if (spa_json_is_container(val, len))
460 len = spa_json_container_len(&it[2], val, len);
461
462 args = (char*)val;
463 spa_json_parse_stringn(val, len, args, len+1);
464 } else if (spa_streq(key, "flags")) {
465 if (spa_json_is_container(val, len))
466 len = spa_json_container_len(&it[2], val, len);
467 flags = (char*)val;
468 spa_json_parse_stringn(val, len, flags, len+1);
469 }
470 }
471 if (name != NULL)
472 res = load_module(context, name, args, flags);
473
474 if (res < 0)
475 break;
476
477 res = ++count;
478 }
479 return res;
480 }
481
create_object(struct pw_context * context,const char * key,const char * args,const char * flags)482 static int create_object(struct pw_context *context, const char *key, const char *args, const char *flags)
483 {
484 struct pw_impl_factory *factory;
485 void *obj;
486
487 pw_log_debug("find factory %s", key);
488 factory = pw_context_find_factory(context, key);
489 if (factory == NULL) {
490 if (flags && strstr(flags, "nofail") != NULL)
491 return 0;
492 pw_log_error("can't find factory %s", key);
493 return -ENOENT;
494 }
495 pw_log_debug("create object with args %s", args);
496 obj = pw_impl_factory_create_object(factory,
497 NULL, NULL, 0,
498 args ? pw_properties_new_string(args) : NULL,
499 SPA_ID_INVALID);
500 if (obj == NULL) {
501 if (flags && strstr(flags, "nofail") != NULL)
502 return 0;
503 pw_log_error("can't create object from factory %s: %m", key);
504 return -errno;
505 }
506 return 0;
507 }
508
509 /*
510 * context.objects = [
511 * { factory = <factory-name>
512 * [ args = { <key> = <value> ... } ]
513 * [ flags = [ [ nofail ] ] ]
514 * }
515 * ]
516 */
parse_objects(struct pw_context * context,char * str)517 static int parse_objects(struct pw_context *context, char *str)
518 {
519 struct spa_json it[3];
520 char key[512];
521 int res = 0, count = 0;
522
523 spa_json_init(&it[0], str, strlen(str));
524 if (spa_json_enter_array(&it[0], &it[1]) < 0) {
525 pw_log_error("config file error: context.objects is not an array");
526 return -EINVAL;
527 }
528
529 while (spa_json_enter_object(&it[1], &it[2]) > 0) {
530 char *factory = NULL, *args = NULL, *flags = NULL;
531
532 while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
533 const char *val;
534 int len;
535
536 if ((len = spa_json_next(&it[2], &val)) <= 0)
537 break;
538
539 if (spa_streq(key, "factory")) {
540 factory = (char*)val;
541 spa_json_parse_stringn(val, len, factory, len+1);
542 } else if (spa_streq(key, "args")) {
543 if (spa_json_is_container(val, len))
544 len = spa_json_container_len(&it[2], val, len);
545
546 args = (char*)val;
547 spa_json_parse_stringn(val, len, args, len+1);
548 } else if (spa_streq(key, "flags")) {
549 if (spa_json_is_container(val, len))
550 len = spa_json_container_len(&it[2], val, len);
551
552 flags = (char*)val;
553 spa_json_parse_stringn(val, len, flags, len+1);
554 }
555 }
556 if (factory != NULL)
557 res = create_object(context, factory, args, flags);
558
559 if (res < 0)
560 break;
561 res = ++count;
562 }
563 return res;
564 }
565
do_exec(struct pw_context * context,const char * key,const char * args)566 static int do_exec(struct pw_context *context, const char *key, const char *args)
567 {
568 int pid, res, n_args;
569
570 pid = fork();
571
572 if (pid == 0) {
573 char *cmd, **argv;
574
575 cmd = spa_aprintf("%s %s", key, args ? args : "");
576 argv = pw_split_strv(cmd, " \t", INT_MAX, &n_args);
577 free(cmd);
578
579 pw_log_info("exec %s '%s'", key, args);
580 res = execvp(key, argv);
581 pw_free_strv(argv);
582
583 if (res == -1) {
584 res = -errno;
585 pw_log_error("execvp error '%s': %m", key);
586 return res;
587 }
588 }
589 else {
590 int status = 0;
591 res = waitpid(pid, &status, WNOHANG);
592 pw_log_info("exec got pid %d res:%d status:%d", pid, res, status);
593 }
594 return 0;
595 }
596
597 /*
598 * context.exec = [
599 * { path = <program-name>
600 * [ args = "<arguments>" ]
601 * }
602 * ]
603 */
parse_exec(struct pw_context * context,char * str)604 static int parse_exec(struct pw_context *context, char *str)
605 {
606 struct spa_json it[3];
607 char key[512];
608 int res = 0, count = 0;
609
610 spa_json_init(&it[0], str, strlen(str));
611 if (spa_json_enter_array(&it[0], &it[1]) < 0) {
612 pw_log_error("config file error: context.exec is not an array");
613 return -EINVAL;
614 }
615
616 while (spa_json_enter_object(&it[1], &it[2]) > 0) {
617 char *path = NULL, *args = NULL;
618
619 while (spa_json_get_string(&it[2], key, sizeof(key)) > 0) {
620 const char *val;
621 int len;
622
623 if ((len = spa_json_next(&it[2], &val)) <= 0)
624 break;
625
626 if (spa_streq(key, "path")) {
627 path = (char*)val;
628 spa_json_parse_stringn(val, len, path, len+1);
629 } else if (spa_streq(key, "args")) {
630 args = (char*)val;
631 spa_json_parse_stringn(val, len, args, len+1);
632 }
633 }
634 if (path != NULL)
635 res = do_exec(context, path, args);
636
637 if (res < 0)
638 break;
639
640 res = ++count;
641 }
642 return res;
643 }
644
645 SPA_EXPORT
pw_context_parse_conf_section(struct pw_context * context,struct pw_properties * conf,const char * section)646 int pw_context_parse_conf_section(struct pw_context *context,
647 struct pw_properties *conf, const char *section)
648 {
649 const char *str;
650 char *s;
651 int res;
652
653 if ((str = pw_properties_get(conf, section)) == NULL)
654 return 0;
655
656 s = strdup(str);
657
658 if (spa_streq(section, "context.spa-libs"))
659 res = parse_spa_libs(context, s);
660 else if (spa_streq(section, "context.modules"))
661 res = parse_modules(context, s);
662 else if (spa_streq(section, "context.objects"))
663 res = parse_objects(context, s);
664 else if (spa_streq(section, "context.exec"))
665 res = parse_exec(context, s);
666 else
667 res = -EINVAL;
668
669 free(s);
670
671 return res;
672 }
673