]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/install.c
minor fixes to help texts
[thirdparty/systemd.git] / src / install.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 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 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/stat.h>
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <errno.h>
26 #include <unistd.h>
27
28 #include "log.h"
29 #include "path-lookup.h"
30 #include "util.h"
31 #include "macro.h"
32 #include "strv.h"
33 #include "conf-parser.h"
34
35 static bool arg_force = false;
36
37 static enum {
38 WHERE_SYSTEM,
39 WHERE_SESSION,
40 WHERE_GLOBAL,
41 } arg_where = WHERE_SYSTEM;
42
43 static enum {
44 ACTION_INVALID,
45 ACTION_ENABLE,
46 ACTION_DISABLE,
47 ACTION_TEST
48 } arg_action = ACTION_INVALID;
49
50 typedef struct {
51 char *name;
52 char *path;
53
54 char **aliases;
55 char **wanted_by;
56 } InstallInfo;
57
58 Hashmap *will_install = NULL, *have_installed = NULL;
59
60 static int help(void) {
61
62 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
63 "Install init system units.\n\n"
64 " -h --help Show this help\n"
65 " --force Override existing links\n"
66 " --system Install into system\n"
67 " --session Install into session\n"
68 " --global Install into all sessions\n\n"
69 "Commands:\n"
70 " enable [NAME...] Enable one or more units\n"
71 " disable [NAME...] Disable one or more units\n"
72 " test [NAME...] Test whether any of the specified units are enabled\n",
73 program_invocation_short_name);
74
75 return 0;
76 }
77
78 static int parse_argv(int argc, char *argv[]) {
79
80 enum {
81 ARG_SESSION = 0x100,
82 ARG_SYSTEM,
83 ARG_GLOBAL,
84 ARG_FORCE
85 };
86
87 static const struct option options[] = {
88 { "help", no_argument, NULL, 'h' },
89 { "session", no_argument, NULL, ARG_SESSION },
90 { "system", no_argument, NULL, ARG_SYSTEM },
91 { "global", no_argument, NULL, ARG_GLOBAL },
92 { "force", no_argument, NULL, ARG_FORCE },
93 { NULL, 0, NULL, 0 }
94 };
95
96 int c;
97
98 assert(argc >= 1);
99 assert(argv);
100
101 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
102
103 switch (c) {
104
105 case 'h':
106 help();
107 return 0;
108
109 case ARG_SESSION:
110 arg_where = WHERE_SESSION;
111 break;
112
113 case ARG_SYSTEM:
114 arg_where = WHERE_SYSTEM;
115 break;
116
117 case ARG_GLOBAL:
118 arg_where = WHERE_GLOBAL;
119 break;
120
121 case ARG_FORCE:
122 arg_force = true;
123 break;
124
125 case '?':
126 return -EINVAL;
127
128 default:
129 log_error("Unknown option code %c", c);
130 return -EINVAL;
131 }
132 }
133
134 if (optind >= argc) {
135 log_error("Missing verb.");
136 return -EINVAL;
137 }
138
139 if (streq(argv[optind], "enable"))
140 arg_action = ACTION_ENABLE;
141 else if (streq(argv[optind], "disable"))
142 arg_action = ACTION_DISABLE;
143 else if (streq(argv[optind], "test"))
144 arg_action = ACTION_TEST;
145 else {
146 log_error("Unknown verb %s", argv[optind]);
147 return -EINVAL;
148 }
149
150 optind++;
151
152 if (optind >= argc) {
153 log_error("Missing unit name.");
154 return -EINVAL;
155 }
156
157 return 1;
158 }
159
160 static void install_info_free(InstallInfo *i) {
161 assert(i);
162
163 free(i->name);
164 free(i->path);
165 strv_free(i->aliases);
166 strv_free(i->wanted_by);
167 free(i);
168 }
169
170 static void install_info_hashmap_free(Hashmap *m) {
171 InstallInfo *i;
172
173 while ((i = hashmap_steal_first(m)))
174 install_info_free(i);
175
176 hashmap_free(m);
177 }
178
179 static bool unit_name_valid(const char *name) {
180
181 /* This is a minimal version of unit_name_valid() from
182 * unit-name.c */
183
184 if (!*name)
185 return false;
186
187 if (ignore_file(name))
188 return false;
189
190 return true;
191 }
192
193 static int install_info_add(const char *name) {
194 InstallInfo *i;
195 int r;
196
197 if (!unit_name_valid(name))
198 return -EINVAL;
199
200 if (hashmap_get(have_installed, name) ||
201 hashmap_get(will_install, name))
202 return 0;
203
204 if (!(i = new0(InstallInfo, 1))) {
205 r = -ENOMEM;
206 goto fail;
207 }
208
209 if (!(i->name = strdup(name))) {
210 r = -ENOMEM;
211 goto fail;
212 }
213
214 if ((r = hashmap_put(will_install, i->name, i)) < 0)
215 goto fail;
216
217 return 0;
218
219 fail:
220 if (i)
221 install_info_free(i);
222
223 return r;
224 }
225
226 static int config_parse_also(
227 const char *filename,
228 unsigned line,
229 const char *section,
230 const char *lvalue,
231 const char *rvalue,
232 void *data,
233 void *userdata) {
234
235 char *w;
236 size_t l;
237 char *state;
238
239 assert(filename);
240 assert(lvalue);
241 assert(rvalue);
242
243 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
244 char *n;
245 int r;
246
247 if (!(n = strndup(w, l)))
248 return -ENOMEM;
249
250 r = install_info_add(n);
251 free(n);
252
253 if (r < 0)
254 return r;
255 }
256
257 return 0;
258 }
259
260 static int create_symlink(const char *old_path, const char *new_path) {
261 int r;
262
263 assert(old_path);
264 assert(new_path);
265
266 if (arg_action == ACTION_ENABLE) {
267 char *dest;
268
269 mkdir_parents(new_path, 0755);
270
271 if (symlink(old_path, new_path) >= 0)
272 return 0;
273
274 if (errno != EEXIST) {
275 log_error("Cannot link %s to %s: %m", old_path, new_path);
276 return -errno;
277 }
278
279 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
280
281 if (errno == EINVAL) {
282 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
283 return -EEXIST;
284 }
285
286 log_error("readlink() failed: %s", strerror(-r));
287 return r;
288 }
289
290 if (streq(dest, old_path)) {
291 free(dest);
292 return 0;
293 }
294
295 if (!arg_force) {
296 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
297 free(dest);
298 return -EEXIST;
299 }
300
301 free(dest);
302 unlink(new_path);
303
304 if (symlink(old_path, new_path) >= 0)
305 return 0;
306
307 log_error("Cannot link %s to %s: %m", old_path, new_path);
308 return -errno;
309
310 } else if (arg_action == ACTION_DISABLE) {
311 char *dest;
312
313 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
314 if (errno == ENOENT)
315 return 0;
316
317 if (errno == EINVAL) {
318 log_warning("File %s not a symlink, ignoring.", old_path);
319 return 0;
320 }
321
322 log_error("readlink() failed: %s", strerror(-r));
323 return r;
324 }
325
326 if (!streq(dest, old_path)) {
327 log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
328 free(dest);
329 return 0;
330 }
331
332 free(dest);
333 if (unlink(new_path) >= 0)
334 return 0;
335
336 log_error("Cannot unlink %s: %m", new_path);
337 return -errno;
338
339 } else if (arg_action == ACTION_TEST) {
340 char *dest;
341
342 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
343
344 if (errno == ENOENT || errno == EINVAL)
345 return 0;
346
347 log_error("readlink() failed: %s", strerror(-r));
348 return r;
349 }
350
351 if (streq(dest, old_path)) {
352 free(dest);
353 return 1;
354 }
355
356 return 0;
357 }
358
359 assert_not_reached("Unknown action.");
360 }
361
362 static int install_info_symlink_alias(InstallInfo *i, const char *config_path) {
363 char **s;
364 char *alias_path = NULL;
365 int r;
366
367 assert(i);
368
369 STRV_FOREACH(s, i->aliases) {
370
371 if (!unit_name_valid(*s)) {
372 log_error("Invalid name %s.", *s);
373 r = -EINVAL;
374 goto finish;
375 }
376
377 free(alias_path);
378 if (!(alias_path = path_make_absolute(*s, config_path))) {
379 log_error("Out of memory");
380 r = -ENOMEM;
381 goto finish;
382 }
383
384 if ((r = create_symlink(i->path, alias_path)) != 0)
385 goto finish;
386
387 if (arg_action == ACTION_DISABLE)
388 rmdir_parents(alias_path, config_path);
389 }
390
391 r = 0;
392
393 finish:
394 free(alias_path);
395
396 return r;
397 }
398
399 static int install_info_symlink_wants(InstallInfo *i, const char *config_path) {
400 char **s;
401 char *alias_path = NULL;
402 int r;
403
404 assert(i);
405
406 STRV_FOREACH(s, i->wanted_by) {
407 if (!unit_name_valid(*s)) {
408 log_error("Invalid name %s.", *s);
409 r = -EINVAL;
410 goto finish;
411 }
412
413 free(alias_path);
414 alias_path = NULL;
415
416 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
417 log_error("Out of memory");
418 r = -ENOMEM;
419 goto finish;
420 }
421
422 if ((r = create_symlink(i->path, alias_path)) != 0)
423 goto finish;
424
425 if (arg_action == ACTION_DISABLE)
426 rmdir_parents(alias_path, config_path);
427 }
428
429 r = 0;
430
431 finish:
432 free(alias_path);
433
434 return r;
435 }
436
437 static int install_info_apply(LookupPaths *paths, InstallInfo *i, const char *config_path) {
438
439 const ConfigItem items[] = {
440 { "Alias", config_parse_strv, &i->aliases, "Install" },
441 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
442 { "Also", config_parse_also, NULL, "Install" },
443
444 { NULL, NULL, NULL, NULL }
445 };
446
447 char **p;
448 char *filename = NULL;
449 FILE *f = NULL;
450 int r;
451
452 assert(paths);
453 assert(i);
454
455 STRV_FOREACH(p, paths->unit_path) {
456
457 if (!(filename = path_make_absolute(i->name, *p))) {
458 log_error("Out of memory");
459 return -ENOMEM;
460 }
461
462 if ((f = fopen(filename, "re")))
463 break;
464
465 free(filename);
466 filename = NULL;
467
468 if (errno != ENOENT) {
469 log_error("Failed to open %s: %m", filename);
470 return -errno;
471 }
472 }
473
474 if (!f) {
475 log_error("Couldn't find %s.", i->name);
476 return -ENOENT;
477 }
478
479 i->path = filename;
480
481 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
482 fclose(f);
483 return r;
484 }
485
486 fclose(f);
487
488 if ((r = install_info_symlink_alias(i, config_path)) != 0)
489 return r;
490
491 if ((r = install_info_symlink_wants(i, config_path)) != 0)
492 return r;
493
494 return 0;
495 }
496
497 static char *get_config_path(void) {
498
499 switch (arg_where) {
500
501 case WHERE_SYSTEM:
502 return strdup(SYSTEM_CONFIG_UNIT_PATH);
503
504 case WHERE_GLOBAL:
505 return strdup(SESSION_CONFIG_UNIT_PATH);
506
507 case WHERE_SESSION: {
508 char *p;
509
510 if (session_config_home(&p) < 0)
511 return NULL;
512
513 return p;
514 }
515
516 default:
517 assert_not_reached("Unknown config path.");
518 }
519 }
520
521 int main(int argc, char *argv[]) {
522 int r, retval = 1, j;
523 LookupPaths paths;
524 InstallInfo *i;
525 char *config_path = NULL;
526
527 zero(paths);
528
529 log_parse_environment();
530
531 if ((r = parse_argv(argc, argv)) < 0)
532 goto finish;
533 else if (r == 0) {
534 retval = 0;
535 goto finish;
536 }
537
538 if ((r = lookup_paths_init(&paths, arg_where == WHERE_SYSTEM ? MANAGER_SYSTEM : MANAGER_SESSION)) < 0) {
539 log_error("Failed to determine lookup paths: %s", strerror(-r));
540 goto finish;
541 }
542
543 if (!(config_path = get_config_path())) {
544 log_error("Failed to determine config path");
545 goto finish;
546 }
547
548 will_install = hashmap_new(string_hash_func, string_compare_func);
549 have_installed = hashmap_new(string_hash_func, string_compare_func);
550
551 if (!will_install || !have_installed) {
552 log_error("Failed to allocate unit sets.");
553 goto finish;
554 }
555
556 for (j = optind; j < argc; j++)
557 if ((r = install_info_add(argv[j])) < 0)
558 goto finish;
559
560 while ((i = hashmap_first(will_install))) {
561 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
562
563 if ((r = install_info_apply(&paths, i, config_path)) != 0) {
564
565 if (r < 0)
566 goto finish;
567
568 /* In test mode and found something */
569 retval = 0;
570 goto finish;
571 }
572 }
573
574 retval = arg_action == ACTION_TEST ? 1 : 0;
575
576 finish:
577 install_info_hashmap_free(will_install);
578 install_info_hashmap_free(have_installed);
579
580 lookup_paths_free(&paths);
581
582 free(config_path);
583
584 return retval;
585 }