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