]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/path-lookup.c
Merge pull request #6840 from keszybz/more-docs
[thirdparty/systemd.git] / src / shared / path-lookup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "alloc-util.h"
26 #include "fileio.h"
27 #include "fs-util.h"
28 #include "install.h"
29 #include "log.h"
30 #include "macro.h"
31 #include "mkdir.h"
32 #include "path-lookup.h"
33 #include "path-util.h"
34 #include "rm-rf.h"
35 #include "stat-util.h"
36 #include "string-util.h"
37 #include "strv.h"
38 #include "user-util.h"
39 #include "util.h"
40
41 static int user_runtime_dir(char **ret, const char *suffix) {
42 const char *e;
43 char *j;
44
45 assert(ret);
46 assert(suffix);
47
48 e = getenv("XDG_RUNTIME_DIR");
49 if (!e)
50 return -ENXIO;
51
52 j = strappend(e, suffix);
53 if (!j)
54 return -ENOMEM;
55
56 *ret = j;
57 return 0;
58 }
59
60 static int user_config_dir(char **ret, const char *suffix) {
61 const char *e;
62 char *j;
63 int r;
64
65 assert(ret);
66
67 e = getenv("XDG_CONFIG_HOME");
68 if (e)
69 j = strappend(e, suffix);
70 else {
71 _cleanup_free_ char *home = NULL;
72
73 r = get_home_dir(&home);
74 if (r < 0)
75 return r;
76
77 j = strjoin(home, "/.config", suffix);
78 }
79
80 if (!j)
81 return -ENOMEM;
82
83 *ret = j;
84 return 0;
85 }
86
87 static int user_data_dir(char **ret, const char *suffix) {
88 const char *e;
89 char *j;
90 int r;
91
92 assert(ret);
93 assert(suffix);
94
95 /* We don't treat /etc/xdg/systemd here as the spec
96 * suggests because we assume that is a link to
97 * /etc/systemd/ anyway. */
98
99 e = getenv("XDG_DATA_HOME");
100 if (e)
101 j = strappend(e, suffix);
102 else {
103 _cleanup_free_ char *home = NULL;
104
105 r = get_home_dir(&home);
106 if (r < 0)
107 return r;
108
109 j = strjoin(home, "/.local/share", suffix);
110 }
111 if (!j)
112 return -ENOMEM;
113
114 *ret = j;
115 return 1;
116 }
117
118 static char** user_dirs(
119 const char *persistent_config,
120 const char *runtime_config,
121 const char *generator,
122 const char *generator_early,
123 const char *generator_late,
124 const char *transient,
125 const char *persistent_control,
126 const char *runtime_control) {
127
128 const char * const config_unit_paths[] = {
129 USER_CONFIG_UNIT_PATH,
130 "/etc/systemd/user",
131 NULL
132 };
133
134 const char * const data_unit_paths[] = {
135 "/usr/local/lib/systemd/user",
136 "/usr/local/share/systemd/user",
137 USER_DATA_UNIT_PATH,
138 "/usr/lib/systemd/user",
139 "/usr/share/systemd/user",
140 NULL
141 };
142
143 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
144 _cleanup_free_ char *data_home = NULL;
145 _cleanup_strv_free_ char **res = NULL;
146 const char *e;
147 char **tmp;
148 int r;
149
150 /* Implement the mechanisms defined in
151 *
152 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
153 *
154 * We look in both the config and the data dirs because we
155 * want to encourage that distributors ship their unit files
156 * as data, and allow overriding as configuration.
157 */
158
159 e = getenv("XDG_CONFIG_DIRS");
160 if (e) {
161 config_dirs = strv_split(e, ":");
162 if (!config_dirs)
163 return NULL;
164 }
165
166 r = user_data_dir(&data_home, "/systemd/user");
167 if (r < 0 && r != -ENXIO)
168 return NULL;
169
170 e = getenv("XDG_DATA_DIRS");
171 if (e)
172 data_dirs = strv_split(e, ":");
173 else
174 data_dirs = strv_new("/usr/local/share",
175 "/usr/share",
176 NULL);
177 if (!data_dirs)
178 return NULL;
179
180 /* Now merge everything we found. */
181 if (strv_extend(&res, persistent_control) < 0)
182 return NULL;
183
184 if (strv_extend(&res, runtime_control) < 0)
185 return NULL;
186
187 if (strv_extend(&res, transient) < 0)
188 return NULL;
189
190 if (strv_extend(&res, generator_early) < 0)
191 return NULL;
192
193 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
194 return NULL;
195
196 if (strv_extend(&res, persistent_config) < 0)
197 return NULL;
198
199 if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
200 return NULL;
201
202 if (strv_extend(&res, runtime_config) < 0)
203 return NULL;
204
205 if (strv_extend(&res, generator) < 0)
206 return NULL;
207
208 if (strv_extend(&res, data_home) < 0)
209 return NULL;
210
211 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
212 return NULL;
213
214 if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
215 return NULL;
216
217 if (strv_extend(&res, generator_late) < 0)
218 return NULL;
219
220 if (path_strv_make_absolute_cwd(res) < 0)
221 return NULL;
222
223 tmp = res;
224 res = NULL;
225
226 return tmp;
227 }
228
229 static int acquire_generator_dirs(
230 UnitFileScope scope,
231 const char *tempdir,
232 char **generator,
233 char **generator_early,
234 char **generator_late) {
235
236 _cleanup_(rmdir_and_freep) char *t = NULL;
237 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
238 const char *prefix;
239
240 assert(generator);
241 assert(generator_early);
242 assert(generator_late);
243 assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
244
245 if (scope == UNIT_FILE_GLOBAL)
246 return -EOPNOTSUPP;
247
248 if (tempdir)
249 prefix = tempdir;
250
251 else if (scope == UNIT_FILE_SYSTEM)
252 prefix = "/run/systemd";
253
254 else if (scope == UNIT_FILE_USER) {
255 const char *e;
256
257 e = getenv("XDG_RUNTIME_DIR");
258 if (!e)
259 return -ENXIO;
260
261 prefix = strjoina(e, "/systemd");
262 }
263
264 x = strappend(prefix, "/generator");
265 if (!x)
266 return -ENOMEM;
267
268 y = strappend(prefix, "/generator.early");
269 if (!y)
270 return -ENOMEM;
271
272 z = strappend(prefix, "/generator.late");
273 if (!z)
274 return -ENOMEM;
275
276 *generator = x;
277 *generator_early = y;
278 *generator_late = z;
279
280 x = y = z = NULL;
281 return 0;
282 }
283
284 static int acquire_transient_dir(
285 UnitFileScope scope,
286 const char *tempdir,
287 char **ret) {
288
289 char *transient;
290
291 assert(ret);
292 assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
293
294 if (scope == UNIT_FILE_GLOBAL)
295 return -EOPNOTSUPP;
296
297 if (tempdir)
298 transient = strjoin(tempdir, "/transient");
299 else if (scope == UNIT_FILE_SYSTEM)
300 transient = strdup("/run/systemd/transient");
301 else
302 return user_runtime_dir(ret, "/systemd/transient");
303
304 if (!transient)
305 return -ENOMEM;
306 *ret = transient;
307 return 0;
308 }
309
310 static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
311 _cleanup_free_ char *a = NULL, *b = NULL;
312 int r;
313
314 assert(persistent);
315 assert(runtime);
316
317 switch (scope) {
318
319 case UNIT_FILE_SYSTEM:
320 a = strdup(SYSTEM_CONFIG_UNIT_PATH);
321 b = strdup("/run/systemd/system");
322 break;
323
324 case UNIT_FILE_GLOBAL:
325 a = strdup(USER_CONFIG_UNIT_PATH);
326 b = strdup("/run/systemd/user");
327 break;
328
329 case UNIT_FILE_USER:
330 r = user_config_dir(&a, "/systemd/user");
331 if (r < 0 && r != -ENXIO)
332 return r;
333
334 r = user_runtime_dir(runtime, "/systemd/user");
335 if (r < 0) {
336 if (r != -ENXIO)
337 return r;
338
339 /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
340 * directory to NULL */
341 *runtime = NULL;
342 }
343
344 *persistent = a;
345 a = NULL;
346
347 return 0;
348
349 default:
350 assert_not_reached("Hmm, unexpected scope value.");
351 }
352
353 if (!a || !b)
354 return -ENOMEM;
355
356 *persistent = a;
357 *runtime = b;
358 a = b = NULL;
359
360 return 0;
361 }
362
363 static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
364 _cleanup_free_ char *a = NULL;
365 int r;
366
367 assert(persistent);
368 assert(runtime);
369
370 switch (scope) {
371
372 case UNIT_FILE_SYSTEM: {
373 _cleanup_free_ char *b = NULL;
374
375 a = strdup("/etc/systemd/system.control");
376 if (!a)
377 return -ENOMEM;
378
379 b = strdup("/run/systemd/system.control");
380 if (!b)
381 return -ENOMEM;
382
383 *runtime = b;
384 b = NULL;
385
386 break;
387 }
388
389 case UNIT_FILE_USER:
390 r = user_config_dir(&a, "/systemd/system.control");
391 if (r < 0 && r != -ENXIO)
392 return r;
393
394 r = user_runtime_dir(runtime, "/systemd/system.control");
395 if (r < 0) {
396 if (r != -ENXIO)
397 return r;
398
399 /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
400 * NULL */
401 *runtime = NULL;
402 }
403
404 break;
405
406 case UNIT_FILE_GLOBAL:
407 return -EOPNOTSUPP;
408
409 default:
410 assert_not_reached("Hmm, unexpected scope value.");
411 }
412
413 *persistent = a;
414 a = NULL;
415
416 return 0;
417 }
418
419 static int patch_root_prefix(char **p, const char *root_dir) {
420 char *c;
421
422 assert(p);
423
424 if (!*p)
425 return 0;
426
427 c = prefix_root(root_dir, *p);
428 if (!c)
429 return -ENOMEM;
430
431 free(*p);
432 *p = c;
433
434 return 0;
435 }
436
437 static int patch_root_prefix_strv(char **l, const char *root_dir) {
438 char **i;
439 int r;
440
441 if (!root_dir)
442 return 0;
443
444 STRV_FOREACH(i, l) {
445 r = patch_root_prefix(i, root_dir);
446 if (r < 0)
447 return r;
448 }
449
450 return 0;
451 }
452
453 int lookup_paths_init(
454 LookupPaths *p,
455 UnitFileScope scope,
456 LookupPathsFlags flags,
457 const char *root_dir) {
458
459 _cleanup_(rmdir_and_freep) char *tempdir = NULL;
460 _cleanup_free_ char
461 *root = NULL,
462 *persistent_config = NULL, *runtime_config = NULL,
463 *generator = NULL, *generator_early = NULL, *generator_late = NULL,
464 *transient = NULL,
465 *persistent_control = NULL, *runtime_control = NULL;
466 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
467 _cleanup_strv_free_ char **paths = NULL;
468 const char *e;
469 int r;
470
471 assert(p);
472 assert(scope >= 0);
473 assert(scope < _UNIT_FILE_SCOPE_MAX);
474
475 if (!isempty(root_dir) && !path_equal(root_dir, "/")) {
476 if (scope == UNIT_FILE_USER)
477 return -EINVAL;
478
479 r = is_dir(root_dir, true);
480 if (r < 0)
481 return r;
482 if (r == 0)
483 return -ENOTDIR;
484
485 root = strdup(root_dir);
486 if (!root)
487 return -ENOMEM;
488 }
489
490 if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
491 r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
492 if (r < 0)
493 return log_error_errno(r, "Failed to create temporary directory: %m");
494 }
495
496 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
497 r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
498 if (r < 0)
499 return r;
500
501 if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
502 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
503 r = acquire_generator_dirs(scope, tempdir,
504 &generator, &generator_early, &generator_late);
505 if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
506 return r;
507 }
508
509 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
510 r = acquire_transient_dir(scope, tempdir, &transient);
511 if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO)
512 return r;
513
514 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
515 r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
516 if (r < 0 && r != -EOPNOTSUPP)
517 return r;
518
519 /* First priority is whatever has been passed to us via env vars */
520 e = getenv("SYSTEMD_UNIT_PATH");
521 if (e) {
522 const char *k;
523
524 k = endswith(e, ":");
525 if (k) {
526 e = strndupa(e, k - e);
527 append = true;
528 }
529
530 /* FIXME: empty components in other places should be rejected. */
531
532 r = path_split_and_make_absolute(e, &paths);
533 if (r < 0)
534 return r;
535 }
536
537 if (!paths || append) {
538 /* Let's figure something out. */
539
540 _cleanup_strv_free_ char **add = NULL;
541
542 /* For the user units we include share/ in the search
543 * path in order to comply with the XDG basedir spec.
544 * For the system stuff we avoid such nonsense. OTOH
545 * we include /lib in the search path for the system
546 * stuff but avoid it for user stuff. */
547
548 switch (scope) {
549
550 case UNIT_FILE_SYSTEM:
551 add = strv_new(
552 /* If you modify this you also want to modify
553 * systemdsystemunitpath= in systemd.pc.in! */
554 STRV_IFNOTNULL(persistent_control),
555 STRV_IFNOTNULL(runtime_control),
556 STRV_IFNOTNULL(transient),
557 STRV_IFNOTNULL(generator_early),
558 persistent_config,
559 SYSTEM_CONFIG_UNIT_PATH,
560 "/etc/systemd/system",
561 runtime_config,
562 "/run/systemd/system",
563 STRV_IFNOTNULL(generator),
564 "/usr/local/lib/systemd/system",
565 SYSTEM_DATA_UNIT_PATH,
566 "/usr/lib/systemd/system",
567 #ifdef HAVE_SPLIT_USR
568 "/lib/systemd/system",
569 #endif
570 STRV_IFNOTNULL(generator_late),
571 NULL);
572 break;
573
574 case UNIT_FILE_GLOBAL:
575 add = strv_new(
576 /* If you modify this you also want to modify
577 * systemduserunitpath= in systemd.pc.in, and
578 * the arrays in user_dirs() above! */
579 STRV_IFNOTNULL(persistent_control),
580 STRV_IFNOTNULL(runtime_control),
581 STRV_IFNOTNULL(transient),
582 STRV_IFNOTNULL(generator_early),
583 persistent_config,
584 USER_CONFIG_UNIT_PATH,
585 "/etc/systemd/user",
586 runtime_config,
587 "/run/systemd/user",
588 STRV_IFNOTNULL(generator),
589 "/usr/local/lib/systemd/user",
590 "/usr/local/share/systemd/user",
591 USER_DATA_UNIT_PATH,
592 "/usr/lib/systemd/user",
593 "/usr/share/systemd/user",
594 STRV_IFNOTNULL(generator_late),
595 NULL);
596 break;
597
598 case UNIT_FILE_USER:
599 add = user_dirs(persistent_config, runtime_config,
600 generator, generator_early, generator_late,
601 transient,
602 persistent_config, runtime_control);
603 break;
604
605 default:
606 assert_not_reached("Hmm, unexpected scope?");
607 }
608
609 if (!add)
610 return -ENOMEM;
611
612 if (paths) {
613 r = strv_extend_strv(&paths, add, true);
614 if (r < 0)
615 return r;
616 } else {
617 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
618 * and don't have to copy anything */
619 paths = add;
620 add = NULL;
621 }
622 }
623
624 r = patch_root_prefix(&persistent_config, root);
625 if (r < 0)
626 return r;
627 r = patch_root_prefix(&runtime_config, root);
628 if (r < 0)
629 return r;
630
631 r = patch_root_prefix(&generator, root);
632 if (r < 0)
633 return r;
634 r = patch_root_prefix(&generator_early, root);
635 if (r < 0)
636 return r;
637 r = patch_root_prefix(&generator_late, root);
638 if (r < 0)
639 return r;
640
641 r = patch_root_prefix(&transient, root);
642 if (r < 0)
643 return r;
644
645 r = patch_root_prefix(&persistent_control, root);
646 if (r < 0)
647 return r;
648
649 r = patch_root_prefix(&runtime_control, root);
650 if (r < 0)
651 return r;
652
653 r = patch_root_prefix_strv(paths, root);
654 if (r < 0)
655 return -ENOMEM;
656
657 p->search_path = strv_uniq(paths);
658 paths = NULL;
659
660 p->persistent_config = persistent_config;
661 p->runtime_config = runtime_config;
662 persistent_config = runtime_config = NULL;
663
664 p->generator = generator;
665 p->generator_early = generator_early;
666 p->generator_late = generator_late;
667 generator = generator_early = generator_late = NULL;
668
669 p->transient = transient;
670 transient = NULL;
671
672 p->persistent_control = persistent_control;
673 p->runtime_control = runtime_control;
674 persistent_control = runtime_control = NULL;
675
676 p->root_dir = root;
677 root = NULL;
678
679 p->temporary_dir = tempdir;
680 tempdir = NULL;
681
682 return 0;
683 }
684
685 void lookup_paths_free(LookupPaths *p) {
686 if (!p)
687 return;
688
689 p->search_path = strv_free(p->search_path);
690
691 p->persistent_config = mfree(p->persistent_config);
692 p->runtime_config = mfree(p->runtime_config);
693
694 p->generator = mfree(p->generator);
695 p->generator_early = mfree(p->generator_early);
696 p->generator_late = mfree(p->generator_late);
697
698 p->transient = mfree(p->transient);
699
700 p->persistent_control = mfree(p->persistent_control);
701 p->runtime_control = mfree(p->runtime_control);
702
703 p->root_dir = mfree(p->root_dir);
704 }
705
706 int lookup_paths_reduce(LookupPaths *p) {
707 _cleanup_free_ struct stat *stats = NULL;
708 size_t n_stats = 0, allocated = 0;
709 unsigned c = 0;
710 int r;
711
712 assert(p);
713
714 /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
715 * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
716 * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
717 * account when following symlinks. When we have no root path set this restriction does not apply however. */
718
719 if (!p->search_path)
720 return 0;
721
722 while (p->search_path[c]) {
723 struct stat st;
724 unsigned k;
725
726 if (p->root_dir)
727 r = lstat(p->search_path[c], &st);
728 else
729 r = stat(p->search_path[c], &st);
730 if (r < 0) {
731 if (errno == ENOENT)
732 goto remove_item;
733
734 /* If something we don't grok happened, let's better leave it in. */
735 log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
736 c++;
737 continue;
738 }
739
740 for (k = 0; k < n_stats; k++) {
741 if (stats[k].st_dev == st.st_dev &&
742 stats[k].st_ino == st.st_ino)
743 break;
744 }
745
746 if (k < n_stats) /* Is there already an entry with the same device/inode? */
747 goto remove_item;
748
749 if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
750 return -ENOMEM;
751
752 stats[n_stats++] = st;
753 c++;
754 continue;
755
756 remove_item:
757 free(p->search_path[c]);
758 memmove(p->search_path + c,
759 p->search_path + c + 1,
760 (strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
761 }
762
763 if (strv_isempty(p->search_path)) {
764 log_debug("Ignoring unit files.");
765 p->search_path = strv_free(p->search_path);
766 } else {
767 _cleanup_free_ char *t;
768
769 t = strv_join(p->search_path, "\n\t");
770 if (!t)
771 return -ENOMEM;
772
773 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
774 }
775
776 return 0;
777 }
778
779 int lookup_paths_mkdir_generator(LookupPaths *p) {
780 int r, q;
781
782 assert(p);
783
784 if (!p->generator || !p->generator_early || !p->generator_late)
785 return -EINVAL;
786
787 r = mkdir_p_label(p->generator, 0755);
788
789 q = mkdir_p_label(p->generator_early, 0755);
790 if (q < 0 && r >= 0)
791 r = q;
792
793 q = mkdir_p_label(p->generator_late, 0755);
794 if (q < 0 && r >= 0)
795 r = q;
796
797 return r;
798 }
799
800 void lookup_paths_trim_generator(LookupPaths *p) {
801 assert(p);
802
803 /* Trim empty dirs */
804
805 if (p->generator)
806 (void) rmdir(p->generator);
807 if (p->generator_early)
808 (void) rmdir(p->generator_early);
809 if (p->generator_late)
810 (void) rmdir(p->generator_late);
811 }
812
813 void lookup_paths_flush_generator(LookupPaths *p) {
814 assert(p);
815
816 /* Flush the generated unit files in full */
817
818 if (p->generator)
819 (void) rm_rf(p->generator, REMOVE_ROOT);
820 if (p->generator_early)
821 (void) rm_rf(p->generator_early, REMOVE_ROOT);
822 if (p->generator_late)
823 (void) rm_rf(p->generator_late, REMOVE_ROOT);
824
825 if (p->temporary_dir)
826 (void) rm_rf(p->temporary_dir, REMOVE_ROOT);
827 }
828
829 char **generator_binary_paths(UnitFileScope scope) {
830
831 switch (scope) {
832
833 case UNIT_FILE_SYSTEM:
834 return strv_new("/run/systemd/system-generators",
835 "/etc/systemd/system-generators",
836 "/usr/local/lib/systemd/system-generators",
837 SYSTEM_GENERATOR_PATH,
838 NULL);
839
840 case UNIT_FILE_GLOBAL:
841 case UNIT_FILE_USER:
842 return strv_new("/run/systemd/user-generators",
843 "/etc/systemd/user-generators",
844 "/usr/local/lib/systemd/user-generators",
845 USER_GENERATOR_PATH,
846 NULL);
847
848 default:
849 assert_not_reached("Hmm, unexpected scope.");
850 }
851 }