Branch data Line data Source code
1 : : /*
2 : : * update-alternatives
3 : : *
4 : : * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
5 : : * Copyright © 2000-2002 Wichert Akkerman <wakkerma@debian.org>
6 : : * Copyright © 2006-2017 Guillem Jover <guillem@debian.org>
7 : : * Copyright © 2008 Pierre Habouzit <madcoder@debian.org>
8 : : * Copyright © 2009-2010 Raphaël Hertzog <hertzog@debian.org>
9 : : *
10 : : * This program is free software; you can redistribute it and/or modify
11 : : * it under the terms of the GNU General Public License as published by
12 : : * the Free Software Foundation; either version 2 of the License, or
13 : : * (at your option) any later version.
14 : : *
15 : : * This program is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : : * GNU General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU General Public License
21 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 : : */
23 : :
24 : : #include <config.h>
25 : : #include <compat.h>
26 : :
27 : : #include <sys/types.h>
28 : : #include <sys/time.h>
29 : : #include <sys/stat.h>
30 : : #include <sys/wait.h>
31 : :
32 : : #include <errno.h>
33 : : #include <stdarg.h>
34 : : #include <stdbool.h>
35 : : #include <stdlib.h>
36 : : #include <stdio.h>
37 : : #include <unistd.h>
38 : : #include <string.h>
39 : : #include <dirent.h>
40 : : #include <time.h>
41 : : #include <setjmp.h>
42 : : #include <assert.h>
43 : : #include <locale.h>
44 : : #include <ctype.h>
45 : : #include <limits.h>
46 : :
47 : : #include <dpkg/macros.h>
48 : : #include <dpkg/i18n.h>
49 : :
50 : : /* Global variables: */
51 : :
52 : : #define PROGNAME "update-alternatives"
53 : :
54 : : static const char *altdir = SYSCONFDIR "/alternatives";
55 : : static char *admdir = NULL;
56 : : static const char *instdir = "";
57 : : static size_t instdir_len;
58 : :
59 : : static const char *prog_path = "update-alternatives";
60 : :
61 : : enum action {
62 : : ACTION_NONE,
63 : : ACTION_INSTALL,
64 : : ACTION_SET,
65 : : ACTION_SET_SELECTIONS,
66 : : ACTION_GET_SELECTIONS,
67 : : ACTION_AUTO,
68 : : ACTION_CONFIG,
69 : : ACTION_CONFIG_ALL,
70 : : ACTION_REMOVE,
71 : : ACTION_REMOVE_ALL,
72 : : ACTION_LIST,
73 : : ACTION_QUERY,
74 : : ACTION_DISPLAY,
75 : : };
76 : :
77 : : static struct action_name {
78 : : enum action action;
79 : : const char *name;
80 : : } action_names[] = {
81 : : { ACTION_NONE, "" },
82 : : { ACTION_INSTALL, "install" },
83 : : { ACTION_SET, "set" },
84 : : { ACTION_SET_SELECTIONS, "set-selections" },
85 : : { ACTION_GET_SELECTIONS, "get-selections" },
86 : : { ACTION_AUTO, "auto" },
87 : : { ACTION_CONFIG, "config" },
88 : : { ACTION_CONFIG_ALL, "all" },
89 : : { ACTION_REMOVE, "remove" },
90 : : { ACTION_REMOVE_ALL, "remove-all" },
91 : : { ACTION_LIST, "list" },
92 : : { ACTION_QUERY, "query" },
93 : : { ACTION_DISPLAY, "display" },
94 : : };
95 : :
96 : : enum output_mode {
97 : : OUTPUT_QUIET = -1,
98 : : OUTPUT_NORMAL = 0,
99 : : OUTPUT_VERBOSE = 1,
100 : : OUTPUT_DEBUG = 2,
101 : : };
102 : :
103 : : /* Action to perform */
104 : : static enum action action = ACTION_NONE;
105 : : static char *log_file = NULL;
106 : : static FILE *fh_log = NULL;
107 : : /* Skip alternatives properly configured in auto mode (for --config) */
108 : : static int opt_skip_auto = 0;
109 : : static int opt_verbose = OUTPUT_NORMAL;
110 : : static int opt_force = 0;
111 : :
112 : : /*
113 : : * Functions.
114 : : */
115 : :
116 : : static void
117 : 0 : version(void)
118 : : {
119 : 0 : printf(_("Debian %s version %s.\n"), PROGNAME, VERSION);
120 : 0 : printf("\n");
121 : :
122 : 0 : printf(_(
123 : : "This is free software; see the GNU General Public License version 2 or\n"
124 : : "later for copying conditions. There is NO warranty.\n"));
125 : 0 : }
126 : :
127 : : static void
128 : 0 : usage(void)
129 : : {
130 : 0 : printf(_(
131 : : "Usage: %s [<option> ...] <command>\n"
132 : : "\n"), PROGNAME);
133 : :
134 : 0 : printf(_(
135 : : "Commands:\n"
136 : : " --install <link> <name> <path> <priority>\n"
137 : : " [--slave <link> <name> <path>] ...\n"
138 : : " add a group of alternatives to the system.\n"
139 : : " --remove <name> <path> remove <path> from the <name> group alternative.\n"
140 : : " --remove-all <name> remove <name> group from the alternatives system.\n"
141 : : " --auto <name> switch the master link <name> to automatic mode.\n"
142 : : " --display <name> display information about the <name> group.\n"
143 : : " --query <name> machine parseable version of --display <name>.\n"
144 : : " --list <name> display all targets of the <name> group.\n"
145 : : " --get-selections list master alternative names and their status.\n"
146 : : " --set-selections read alternative status from standard input.\n"
147 : : " --config <name> show alternatives for the <name> group and ask the\n"
148 : : " user to select which one to use.\n"
149 : : " --set <name> <path> set <path> as alternative for <name>.\n"
150 : : " --all call --config on all alternatives.\n"
151 : : "\n"));
152 : :
153 : 0 : printf(_(
154 : : "<link> is the symlink pointing to %s/<name>.\n"
155 : : " (e.g. /usr/bin/pager)\n"
156 : : "<name> is the master name for this link group.\n"
157 : : " (e.g. pager)\n"
158 : : "<path> is the location of one of the alternative target files.\n"
159 : : " (e.g. /usr/bin/less)\n"
160 : : "<priority> is an integer; options with higher numbers have higher priority in\n"
161 : : " automatic mode.\n"
162 : : "\n"), altdir);
163 : :
164 : 0 : printf(_(
165 : : "Options:\n"
166 : : " --altdir <directory> change the alternatives directory\n"
167 : : " (default is %s).\n"
168 : : " --admindir <directory> change the administrative directory\n"
169 : : " (default is %s).\n"
170 : : " --instdir <directory> change the installation directory.\n"
171 : : " --root <directory> change the filesystem root directory.\n"
172 : : " --log <file> change the log file.\n"
173 : : " --force allow replacing files with alternative links.\n"
174 : : " --skip-auto skip prompt for alternatives correctly configured\n"
175 : : " in automatic mode (relevant for --config only)\n"
176 : : " --quiet quiet operation, minimal output.\n"
177 : : " --verbose verbose operation, more output.\n"
178 : : " --debug debug output, way more output.\n"
179 : : " --help show this help message.\n"
180 : : " --version show the version.\n"
181 : : ), altdir, admdir);
182 : 0 : }
183 : :
184 : : static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
185 : 14 : error(char const *fmt, ...)
186 : : {
187 : : va_list args;
188 : :
189 : 14 : fprintf(stderr, "%s: %s: ", PROGNAME, _("error"));
190 : 14 : va_start(args, fmt);
191 : 14 : vfprintf(stderr, fmt, args);
192 : 14 : va_end(args);
193 : 14 : fprintf(stderr, "\n");
194 : 14 : exit(2);
195 : : }
196 : :
197 : : static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
198 : 2 : syserr(char const *fmt, ...)
199 : : {
200 : : va_list args;
201 : :
202 : 2 : fprintf(stderr, "%s: %s: ", PROGNAME, _("error"));
203 : 2 : va_start(args, fmt);
204 : 2 : vfprintf(stderr, fmt, args);
205 : 2 : va_end(args);
206 : 2 : fprintf(stderr, ": %s\n", strerror(errno));
207 : 2 : exit(2);
208 : : }
209 : :
210 : : static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(1)
211 : 1 : badusage(char const *fmt, ...)
212 : : {
213 : : va_list args;
214 : :
215 : 1 : fprintf(stderr, "%s: ", PROGNAME);
216 : 1 : va_start(args, fmt);
217 : 1 : vfprintf(stderr, fmt, args);
218 : 1 : va_end(args);
219 : 1 : fprintf(stderr, "\n\n");
220 : 1 : fprintf(stderr, _("Use '%s --help' for program usage information."),
221 : : PROGNAME);
222 : 1 : fprintf(stderr, "\n");
223 : 1 : exit(2);
224 : : }
225 : :
226 : : static void DPKG_ATTR_PRINTF(1)
227 : 28 : warning(char const *fmt, ...)
228 : : {
229 : : va_list args;
230 : :
231 [ + - ]: 28 : if (opt_verbose < OUTPUT_NORMAL)
232 : 28 : return;
233 : :
234 : 0 : fprintf(stderr, "%s: %s: ", PROGNAME, _("warning"));
235 : 0 : va_start(args, fmt);
236 : 0 : vfprintf(stderr, fmt, args);
237 : 0 : va_end(args);
238 : 0 : fprintf(stderr, "\n");
239 : : }
240 : :
241 : : static void DPKG_ATTR_PRINTF(1)
242 : 184 : debug(char const *fmt, ...)
243 : : {
244 : : va_list args;
245 : :
246 [ + + ]: 184 : if (opt_verbose < OUTPUT_DEBUG)
247 : 169 : return;
248 : :
249 : 15 : fprintf(stderr, "DEBUG: ");
250 : 15 : va_start(args, fmt);
251 : 15 : vfprintf(stderr, fmt, args);
252 : 15 : va_end(args);
253 : 15 : fprintf(stderr, "\n");
254 : : }
255 : :
256 : : static void DPKG_ATTR_PRINTF(1)
257 : 31 : verbose(char const *fmt, ...)
258 : : {
259 : : va_list args;
260 : :
261 [ + - ]: 31 : if (opt_verbose < OUTPUT_VERBOSE)
262 : 31 : return;
263 : :
264 : 0 : printf("%s: ", PROGNAME);
265 : 0 : va_start(args, fmt);
266 : 0 : vprintf(fmt, args);
267 : 0 : va_end(args);
268 : 0 : printf("\n");
269 : : }
270 : :
271 : : static void DPKG_ATTR_PRINTF(1)
272 : 41 : info(char const *fmt, ...)
273 : : {
274 : : va_list args;
275 : :
276 [ + - ]: 41 : if (opt_verbose < OUTPUT_NORMAL)
277 : 41 : return;
278 : :
279 : 0 : printf("%s: ", PROGNAME);
280 : 0 : va_start(args, fmt);
281 : 0 : vprintf(fmt, args);
282 : 0 : va_end(args);
283 : 0 : printf("\n");
284 : : }
285 : :
286 : : static void DPKG_ATTR_PRINTF(1)
287 : 440 : pr(char const *fmt, ...)
288 : : {
289 : : va_list args;
290 : :
291 : 440 : va_start(args, fmt);
292 : 440 : vprintf(fmt, args);
293 : 440 : va_end(args);
294 : 440 : printf("\n");
295 : 440 : }
296 : :
297 : : static void *
298 : 3923 : xmalloc(size_t size)
299 : : {
300 : : void *ptr;
301 : :
302 : 3923 : ptr = malloc(size);
303 [ - + ]: 3923 : if (!ptr)
304 : 0 : error(_("malloc failed (%zu bytes)"), size);
305 : :
306 : 3923 : return ptr;
307 : : }
308 : :
309 : : static char *
310 : 3417 : xstrdup(const char *str)
311 : : {
312 : : char *new_str;
313 : :
314 [ + + ]: 3417 : if (!str)
315 : 60 : return NULL;
316 : :
317 : 3357 : new_str = strdup(str);
318 [ - + ]: 3357 : if (!new_str)
319 : 0 : error(_("failed to allocate memory"));
320 : :
321 : 3357 : return new_str;
322 : : }
323 : :
324 : : static char *
325 : 0 : xstrndup(const char *str, size_t n)
326 : : {
327 : : char *new_str;
328 : :
329 [ # # ]: 0 : if (!str)
330 : 0 : return NULL;
331 : :
332 : 0 : new_str = strndup(str, n);
333 [ # # ]: 0 : if (!new_str)
334 : 0 : error(_("failed to allocate memory"));
335 : :
336 : 0 : return new_str;
337 : : }
338 : :
339 : : static char * DPKG_ATTR_VPRINTF(1)
340 : 3229 : xvasprintf(const char *fmt, va_list args)
341 : : {
342 : : char *str;
343 : :
344 [ - + ]: 3229 : if (vasprintf(&str, fmt, args) < 0)
345 : 0 : error(_("failed to allocate memory"));
346 : :
347 : 3229 : return str;
348 : : }
349 : :
350 : : static char * DPKG_ATTR_PRINTF(1)
351 : 3166 : xasprintf(const char *fmt, ...)
352 : : {
353 : : va_list args;
354 : : char *str;
355 : :
356 : 3166 : va_start(args, fmt);
357 : 3166 : str = xvasprintf(fmt, args);
358 : 3166 : va_end(args);
359 : :
360 : 3166 : return str;
361 : : }
362 : :
363 : : static char *
364 : 225 : areadlink(const char *linkname)
365 : : {
366 : : struct stat st;
367 : : char *buf;
368 : : ssize_t size;
369 : :
370 : : /* Allocate required memory to store the value of the symlink */
371 [ + + ]: 225 : if (lstat(linkname, &st))
372 : 36 : return NULL;
373 : :
374 [ + + ]: 189 : if (!S_ISLNK(st.st_mode)) {
375 : 2 : errno = EINVAL;
376 : 2 : return NULL;
377 : : }
378 : :
379 : 187 : buf = xmalloc(st.st_size + 1);
380 : :
381 : : /* Read it and terminate the string properly */
382 : 187 : size = readlink(linkname, buf, st.st_size);
383 [ - + ]: 187 : if (size == -1) {
384 : 0 : int saved_errno = errno;
385 : :
386 : 0 : free(buf);
387 : 0 : errno = saved_errno;
388 : :
389 : 0 : return NULL;
390 : : }
391 : 187 : buf[size] = '\0';
392 : :
393 : 187 : return buf;
394 : : }
395 : :
396 : : static int
397 : 0 : spawn(const char *prog, const char *args[])
398 : : {
399 : : pid_t pid, dead_pid;
400 : : int status;
401 : :
402 : 0 : pid = fork();
403 [ # # ]: 0 : if (pid == -1)
404 : 0 : error(_("fork failed"));
405 [ # # ]: 0 : if (pid == 0) {
406 : 0 : execvp(prog, (char *const *)args);
407 : 0 : syserr(_("unable to execute %s (%s)"), prog, prog);
408 : : }
409 [ # # # # ]: 0 : while ((dead_pid = waitpid(pid, &status, 0)) == -1 && errno == EINTR) ;
410 [ # # ]: 0 : if (dead_pid != pid)
411 : 0 : error(_("wait for subprocess %s failed"), prog);
412 : :
413 : 0 : return status;
414 : : }
415 : :
416 : : static bool
417 : 237 : rename_mv(const char *src, const char *dst)
418 : : {
419 : 237 : const char *args[] = { "mv", src, dst, NULL };
420 : : int rc;
421 : :
422 [ + - ]: 237 : if (rename(src, dst) == 0)
423 : 237 : return true;
424 [ # # ]: 0 : if (errno == ENOENT)
425 : 0 : return false;
426 : :
427 : 0 : rc = spawn("mv", args);
428 [ # # # # ]: 0 : if (WIFEXITED(rc) && WEXITSTATUS(rc) == 0)
429 : 0 : return true;
430 : :
431 : 0 : return false;
432 : : }
433 : :
434 : : static void
435 : 237 : xrename(const char *src, const char *dst)
436 : : {
437 [ - + ]: 237 : if (!rename_mv(src, dst))
438 : 0 : syserr(_("unable to install '%.250s' as '%.250s'"), src, dst);
439 : 237 : }
440 : :
441 : : static void DPKG_ATTR_PRINTF(1)
442 : 5 : xunlink_args(const char *fmt, ...)
443 : : {
444 : : va_list args;
445 : : char *path;
446 : :
447 : 5 : va_start(args, fmt);
448 : 5 : path = xvasprintf(fmt, args);
449 : 5 : va_end(args);
450 : :
451 [ - + - - ]: 5 : if (unlink(path) < 0 && errno != ENOENT)
452 : 0 : syserr(_("unable to remove '%s'"), path);
453 : :
454 : 5 : free(path);
455 : 5 : }
456 : :
457 : : static char *
458 : 0 : xdirname(const char *pathname)
459 : : {
460 : : char *dirname, *slash;
461 : :
462 : 0 : slash = strrchr(pathname, '/');
463 [ # # ]: 0 : if (slash)
464 : 0 : dirname = xstrndup(pathname, slash - pathname);
465 : : else
466 : 0 : dirname = xstrdup(".");
467 : :
468 : 0 : return dirname;
469 : : }
470 : :
471 : : static int
472 : 0 : make_path(const char *pathname, mode_t mode)
473 : : {
474 : : char *dirname, *slash;
475 : :
476 : 0 : dirname = xstrdup(pathname);
477 : :
478 : : /* Find the first slash, and ignore it, as it will be either the
479 : : * slash for the root directory, for the current directory in a
480 : : * relative pathname or its parent. */
481 : 0 : slash = strchr(dirname, '/');
482 : :
483 [ # # ]: 0 : while (slash != NULL) {
484 : 0 : slash = strchr(slash + 1, '/');
485 [ # # ]: 0 : if (slash)
486 : 0 : *slash = '\0';
487 : :
488 [ # # # # ]: 0 : if (mkdir(dirname, mode) < 0 && errno != EEXIST) {
489 : 0 : free(dirname);
490 : 0 : return -1;
491 : : }
492 [ # # ]: 0 : if (slash)
493 : 0 : *slash = '/';
494 : : }
495 : :
496 : 0 : free(dirname);
497 : :
498 : 0 : return 0;
499 : : }
500 : :
501 : : static void DPKG_ATTR_PRINTF(1)
502 : 107 : log_msg(const char *fmt, ...)
503 : : {
504 : : va_list args;
505 : :
506 [ + + ]: 107 : if (fh_log == NULL) {
507 : 53 : fh_log = fopen(log_file, "a");
508 [ - + - - ]: 53 : if (fh_log == NULL && errno == ENOENT) {
509 : 0 : char *log_dir = xdirname(log_file);
510 : :
511 [ # # ]: 0 : if (make_path(log_dir, 0755) < 0)
512 : 0 : syserr(_("cannot create log directory '%s'"),
513 : : log_dir);
514 : 0 : free(log_dir);
515 : :
516 : 0 : fh_log = fopen(log_file, "a");
517 : : }
518 [ - + - - ]: 53 : if (fh_log == NULL && errno != EACCES)
519 : 0 : syserr(_("cannot append to '%s'"), log_file);
520 : : }
521 : :
522 [ + - ]: 107 : if (fh_log) {
523 : : char timestamp[64];
524 : : time_t now;
525 : : struct tm tm;
526 : :
527 : 107 : time(&now);
528 [ - + ]: 107 : if (localtime_r(&now, &tm) == NULL)
529 : 0 : syserr(_("cannot get local time to log into '%s'"), log_file);
530 : 107 : strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S",
531 : : &tm);
532 : 107 : fprintf(fh_log, "%s %s: ", PROGNAME, timestamp);
533 : 107 : va_start(args, fmt);
534 : 107 : vfprintf(fh_log, fmt, args);
535 : 107 : va_end(args);
536 : 107 : fprintf(fh_log, "\n");
537 : : }
538 : 107 : }
539 : :
540 : : /*
541 : : * Filesystem access for alernative handling.
542 : : */
543 : :
544 : : static char *
545 : 2370 : fsys_get_path(const char *pathpart)
546 : : {
547 : 2370 : return xasprintf("%s%s", instdir, pathpart);
548 : : }
549 : :
550 : : static const char *
551 : 116 : fsys_set_dir(const char *dir)
552 : : {
553 [ + + ]: 116 : if (dir == NULL) {
554 : : const char *instdir_env;
555 : :
556 : 107 : instdir_env = getenv(INSTDIR_ENVVAR);
557 [ + + ]: 107 : if (instdir_env)
558 : 10 : dir = instdir_env;
559 : : else
560 : 97 : dir = "";
561 : : }
562 : :
563 : 116 : instdir_len = strlen(dir);
564 : :
565 : 116 : return dir;
566 : : }
567 : :
568 : : static char *
569 : 106 : fsys_gen_admindir(void)
570 : : {
571 : 106 : return fsys_get_path(ADMINDIR "/alternatives");
572 : : }
573 : :
574 : : static bool
575 : 626 : fsys_pathname_is_missing(const char *pathname)
576 : : {
577 : : struct stat st;
578 : : char *root_pathname;
579 : :
580 : 626 : root_pathname = fsys_get_path(pathname);
581 : :
582 : 626 : errno = 0;
583 [ + + - + ]: 626 : if (stat(root_pathname, &st) < 0 && errno != ENOENT)
584 : 0 : syserr(_("cannot stat file '%s'"), root_pathname);
585 : :
586 : 626 : free(root_pathname);
587 : :
588 [ + + ]: 626 : if (errno == ENOENT)
589 : 153 : return true;
590 : :
591 : 473 : return false;
592 : : }
593 : :
594 : : static int
595 : 337 : fsys_lstat(const char *linkname, struct stat *st)
596 : : {
597 : : char *root_linkname;
598 : : int rc;
599 : :
600 : 337 : root_linkname = fsys_get_path(linkname);
601 : :
602 : 337 : errno = 0;
603 : 337 : rc = lstat(root_linkname, st);
604 : :
605 : 337 : free(root_linkname);
606 : :
607 : 337 : return rc;
608 : : }
609 : :
610 : : static char *
611 : 225 : fsys_areadlink(const char *linkname)
612 : : {
613 : : char *root_linkname;
614 : : char *target;
615 : :
616 : 225 : root_linkname = fsys_get_path(linkname);
617 : 225 : target = areadlink(root_linkname);
618 : 225 : free(root_linkname);
619 : :
620 : 225 : return target;
621 : : }
622 : :
623 : : static char *
624 : 32 : fsys_xreadlink(const char *linkname)
625 : : {
626 : : char *buf;
627 : :
628 : 32 : buf = fsys_areadlink(linkname);
629 [ - + ]: 32 : if (buf == NULL)
630 : 0 : syserr(_("unable to read link '%s%.255s'"), instdir, linkname);
631 : :
632 : 32 : return buf;
633 : : }
634 : :
635 : : static void
636 : 139 : fsys_set_ref_time(const char *linkname, const char *target)
637 : : {
638 : : #ifdef HAVE_LUTIMES
639 : : /* If the symlink did not exist, then copy the timestamps
640 : : * from the target. This is needed so we can get reproducible
641 : : * installations, for programs that track these timestamps on
642 : : * their databases. */
643 : : struct stat st;
644 : : struct timeval tv[2];
645 : : char *root_linkname;
646 : :
647 [ - + ]: 139 : if (fsys_lstat(target, &st) < 0) {
648 [ # # ]: 0 : if (errno != ENOENT)
649 : 0 : syserr(_("unable to get file '%s%s' metadata"),
650 : : instdir, target);
651 : 0 : return;
652 : : }
653 : :
654 : 139 : tv[0].tv_sec = st.st_mtime;
655 : 139 : tv[0].tv_usec = 0;
656 : 139 : tv[1].tv_sec = st.st_mtime;
657 : 139 : tv[1].tv_usec = 0;
658 : :
659 : 139 : root_linkname = fsys_get_path(linkname);
660 [ - + - - ]: 139 : if (lutimes(root_linkname, tv) < 0 && errno != ENOSYS)
661 : 0 : syserr(_("cannot set symlink '%s' timestamp"), root_linkname);
662 : 139 : free(root_linkname);
663 : : #endif
664 : : }
665 : :
666 : : static void
667 : 194 : fsys_symlink(const char *filename, const char *linkname)
668 : : {
669 : : char *root_linkname;
670 : :
671 : 194 : root_linkname = fsys_get_path(linkname);
672 : :
673 [ + + - + ]: 194 : if (unlink(root_linkname) < 0 && errno != ENOENT)
674 : 0 : syserr(_("unable to remove '%s'"), root_linkname);
675 : :
676 [ + + ]: 194 : if (symlink(filename, root_linkname))
677 : 2 : syserr(_("error creating symbolic link '%.255s'"), root_linkname);
678 : :
679 : 192 : free(root_linkname);
680 : 192 : }
681 : :
682 : : static void
683 : 192 : fsys_mv(const char *src, const char *dst)
684 : : {
685 : : char *root_src;
686 : : char *root_dst;
687 : :
688 : 192 : root_src = fsys_get_path(src);
689 : 192 : root_dst = fsys_get_path(dst);
690 : :
691 : 192 : xrename(root_src, root_dst);
692 : :
693 : 192 : free(root_src);
694 : 192 : free(root_dst);
695 : 192 : }
696 : :
697 : : static void
698 : 136 : fsys_rm(const char *f)
699 : : {
700 : : char *root_f;
701 : :
702 : 136 : root_f = fsys_get_path(f);
703 : :
704 [ + + - + ]: 136 : if (unlink(root_f) < 0 && errno != ENOENT)
705 : 0 : syserr(_("unable to remove '%s'"), root_f);
706 : :
707 : 136 : free(root_f);
708 : 136 : }
709 : :
710 : : static void DPKG_ATTR_PRINTF(1)
711 : 57 : fsys_rm_args(const char *fmt, ...)
712 : : {
713 : : va_list args;
714 : : char *path;
715 : :
716 : 57 : va_start(args, fmt);
717 : 57 : path = xvasprintf(fmt, args);
718 : 57 : va_end(args);
719 : :
720 : 57 : fsys_rm(path);
721 : 57 : free(path);
722 : 57 : }
723 : :
724 : : /*
725 : : * OBJECTS
726 : : */
727 : :
728 : : struct fileset {
729 : : struct fileset *next;
730 : :
731 : : char *master_file;
732 : : int priority;
733 : :
734 : : struct slave_file {
735 : : struct slave_file *next;
736 : : char *name;
737 : : char *file;
738 : : } *slaves;
739 : : };
740 : :
741 : : static struct fileset *
742 : 186 : fileset_new(const char *master_file, int prio)
743 : : {
744 : : struct fileset *fs;
745 : :
746 : 186 : fs = xmalloc(sizeof(*fs));
747 : 186 : fs->next = NULL;
748 : 186 : fs->master_file = xstrdup(master_file);
749 : 186 : fs->priority = prio;
750 : 186 : fs->slaves = NULL;
751 : :
752 : 186 : return fs;
753 : : }
754 : :
755 : : static void
756 : 165 : fileset_free(struct fileset *fs)
757 : : {
758 : : struct slave_file *slave, *next;
759 : :
760 : 165 : free(fs->master_file);
761 [ + + ]: 706 : for (slave = fs->slaves; slave; slave = next) {
762 : 541 : next = slave->next;
763 : 541 : free(slave->name);
764 : 541 : free(slave->file);
765 : 541 : free(slave);
766 : : }
767 : 165 : free(fs);
768 : 165 : }
769 : :
770 : : static void
771 : 579 : fileset_add_slave(struct fileset *fs, const char *name, const char *file)
772 : : {
773 : 579 : struct slave_file *sl, *cur, *prev = NULL;
774 : :
775 : : /* Replace existing first */
776 [ + + ]: 1380 : for (cur = fs->slaves; cur; cur = cur->next) {
777 [ - + ]: 801 : if (strcmp(cur->name, name) == 0) {
778 : 0 : free(cur->file);
779 : 0 : cur->file = xstrdup(file);
780 : 0 : return;
781 : : }
782 : 801 : prev = cur;
783 : : }
784 : :
785 : : /* Otherwise add new at the end */
786 : 579 : sl = xmalloc(sizeof(*sl));
787 : 579 : sl->next = NULL;
788 : 579 : sl->name = xstrdup(name);
789 : 579 : sl->file = xstrdup(file);
790 [ + + ]: 579 : if (prev)
791 : 402 : prev->next = sl;
792 : : else
793 : 177 : fs->slaves = sl;
794 : : }
795 : :
796 : : static const char *
797 : 1439 : fileset_get_slave(struct fileset *fs, const char *name)
798 : : {
799 : : struct slave_file *slave;
800 : :
801 [ + + ]: 3337 : for (slave = fs->slaves; slave; slave = slave->next) {
802 [ + + ]: 3280 : if (strcmp(slave->name, name) == 0)
803 : 1382 : return slave->file;
804 : : }
805 : :
806 : 57 : return NULL;
807 : : }
808 : :
809 : : static bool
810 : 815 : fileset_has_slave(struct fileset *fs, const char *name)
811 : : {
812 : 815 : const char *file = fileset_get_slave(fs, name);
813 : :
814 [ + + ]: 815 : if (file == NULL)
815 : 57 : return false;
816 : :
817 : 758 : return file[0] != '\0';
818 : : }
819 : :
820 : : static bool
821 : 163 : fileset_can_install_slave(struct fileset *fs, const char *slave_name)
822 : : {
823 : : /* Decide whether the slave alternative must be setup */
824 [ + + ]: 163 : if (fileset_has_slave(fs, slave_name)) {
825 : 130 : const char *slave = fileset_get_slave(fs, slave_name);
826 : :
827 [ + + ]: 130 : if (!fsys_pathname_is_missing(slave))
828 : 122 : return true;
829 : : }
830 : :
831 : 41 : return false;
832 : : }
833 : :
834 : : struct slave_link {
835 : : struct slave_link *next;
836 : : char *name;
837 : : char *link;
838 : : bool updated;
839 : : };
840 : :
841 : : struct commit_operation {
842 : : struct commit_operation *next;
843 : :
844 : : enum opcode {
845 : : OPCODE_NOP,
846 : : OPCODE_RM,
847 : : OPCODE_MV,
848 : : OPCODE_REF_TIME,
849 : : } opcode;
850 : :
851 : : char *arg_a;
852 : : char *arg_b;
853 : : };
854 : :
855 : : enum alternative_update_reason {
856 : : ALT_UPDATE_NO,
857 : : ALT_UPDATE_SLAVE_CHANGED,
858 : : ALT_UPDATE_LINK_BROKEN,
859 : : };
860 : :
861 : : struct alternative {
862 : : char *master_name;
863 : : char *master_link;
864 : : char *current;
865 : :
866 : : enum alternative_status {
867 : : ALT_ST_UNKNOWN,
868 : : ALT_ST_AUTO,
869 : : ALT_ST_MANUAL,
870 : : } status;
871 : :
872 : : struct slave_link *slaves;
873 : : struct fileset *choices;
874 : :
875 : : struct commit_operation *commit_ops;
876 : :
877 : : int ref_count;
878 : : bool modified;
879 : : bool known_current;
880 : : };
881 : :
882 : : static void
883 : 347 : slave_link_free(struct slave_link *slave)
884 : : {
885 : 347 : free(slave->name);
886 : 347 : free(slave->link);
887 : 347 : free(slave);
888 : 347 : }
889 : :
890 : : static void
891 : 387 : commit_operation_free(struct commit_operation *commit_op)
892 : : {
893 : 387 : free(commit_op->arg_a);
894 : 387 : free(commit_op->arg_b);
895 : 387 : free(commit_op);
896 : 387 : }
897 : :
898 : : static struct alternative *
899 : 166 : alternative_new(const char *name)
900 : : {
901 : : struct alternative *alt;
902 : :
903 : 166 : alt = xmalloc(sizeof(*alt));
904 : 166 : alt->master_name = xstrdup(name);
905 : 166 : alt->master_link = NULL;
906 : 166 : alt->current = NULL;
907 : 166 : alt->status = ALT_ST_UNKNOWN;
908 : 166 : alt->slaves = NULL;
909 : 166 : alt->choices = NULL;
910 : 166 : alt->commit_ops = NULL;
911 : 166 : alt->modified = false;
912 : 166 : alt->known_current = false;
913 : 166 : alt->ref_count = 1;
914 : :
915 : 166 : return alt;
916 : : }
917 : :
918 : : static inline void
919 : 234 : alternative_ref(struct alternative *a)
920 : : {
921 : 234 : a->ref_count++;
922 : 234 : }
923 : :
924 : : static inline bool
925 : 301 : alternative_unref(struct alternative *a)
926 : : {
927 : 301 : return --a->ref_count == 0;
928 : : }
929 : :
930 : : static void
931 : 225 : alternative_choices_free(struct alternative *a)
932 : : {
933 : : struct fileset *fs;
934 : :
935 [ + + ]: 225 : if (a->choices)
936 : 91 : a->modified = true;
937 : :
938 [ + + ]: 373 : while (a->choices) {
939 : 148 : fs = a->choices;
940 : 148 : a->choices = fs->next;
941 : 148 : fileset_free(fs);
942 : : }
943 : 225 : }
944 : :
945 : : static void
946 : 269 : alternative_commit_operations_free(struct alternative *a)
947 : : {
948 : : struct commit_operation *op;
949 : :
950 [ + + ]: 656 : while (a->commit_ops) {
951 : 387 : op = a->commit_ops;
952 : 387 : a->commit_ops = op->next;
953 : 387 : commit_operation_free(op);
954 : : }
955 : 269 : }
956 : :
957 : : static void
958 : 223 : alternative_reset(struct alternative *alt)
959 : : {
960 : : struct slave_link *slave;
961 : :
962 : 223 : free(alt->current);
963 : 223 : alt->current = NULL;
964 : 223 : free(alt->master_link);
965 : 223 : alt->master_link = NULL;
966 [ + + ]: 562 : while (alt->slaves) {
967 : 339 : slave = alt->slaves;
968 : 339 : alt->slaves = slave->next;
969 : 339 : slave_link_free(slave);
970 : : }
971 : 223 : alternative_choices_free(alt);
972 : 223 : alternative_commit_operations_free(alt);
973 : 223 : alt->modified = false;
974 : 223 : alt->known_current = false;
975 : 223 : }
976 : :
977 : : static void
978 : 272 : alternative_free(struct alternative *alt)
979 : : {
980 [ + + ]: 272 : if (!alternative_unref(alt))
981 : 142 : return;
982 : :
983 : 130 : alternative_reset(alt);
984 : 130 : free(alt->master_name);
985 : 130 : free(alt);
986 : : }
987 : :
988 : : static int
989 : 105 : alternative_choices_count(struct alternative *alt)
990 : : {
991 : : struct fileset *fs;
992 : 105 : int count = 0;
993 : :
994 [ + + ]: 270 : for (fs = alt->choices; fs; fs = fs->next)
995 : 165 : count++;
996 : :
997 : 105 : return count;
998 : : }
999 : :
1000 : : static int
1001 : 109 : alternative_slaves_count(struct alternative *alt)
1002 : : {
1003 : : struct slave_link *sl;
1004 : 109 : int count = 0;
1005 : :
1006 [ + + ]: 472 : for (sl = alt->slaves; sl; sl = sl->next)
1007 : 363 : count++;
1008 : :
1009 : 109 : return count;
1010 : : }
1011 : :
1012 : : static int
1013 : 28 : compare_fileset(const void *va, const void *vb)
1014 : : {
1015 : 28 : const struct fileset *a = *(const struct fileset **)va;
1016 : 28 : const struct fileset *b = *(const struct fileset **)vb;
1017 : :
1018 [ + - + - ]: 28 : assert(a && a->master_file);
1019 [ + - + - ]: 28 : assert(b && b->master_file);
1020 : :
1021 : 28 : return strcmp(a->master_file, b->master_file);
1022 : : }
1023 : :
1024 : : static int
1025 : 143 : compare_slave_link(const void *va, const void *vb)
1026 : : {
1027 : 143 : const struct slave_link *a = *(const struct slave_link **)va;
1028 : 143 : const struct slave_link *b = *(const struct slave_link **)vb;
1029 : :
1030 [ + - + - ]: 143 : assert(a && a->name);
1031 [ + - + - ]: 143 : assert(b && b->name);
1032 : :
1033 : 143 : return strcmp(a->name, b->name);
1034 : : }
1035 : :
1036 : : static void
1037 : 45 : alternative_sort_choices(struct alternative *a)
1038 : : {
1039 : : int count, i;
1040 : : struct fileset **table, *fs;
1041 : :
1042 : 45 : count = alternative_choices_count(a);
1043 [ + + ]: 45 : if (count < 2) /* Nothing to sort */
1044 : 22 : return;
1045 : :
1046 : : /* Store objects in a table instead of a linked list */
1047 : 23 : table = xmalloc(sizeof(fs) * count);
1048 [ + + ]: 74 : for (fs = a->choices, i = 0; fs; fs = fs->next) {
1049 [ - + ]: 51 : assert(fs->master_file);
1050 : 51 : table[i++] = fs;
1051 : : }
1052 : :
1053 : 23 : qsort(table, count, sizeof(fs), compare_fileset);
1054 : :
1055 : : /* Rewrite the linked list from the sorted table */
1056 : 23 : a->choices = fs = table[0];
1057 : 23 : table[count - 1]->next = NULL;
1058 [ + + ]: 51 : for (i = 1; i < count; fs = fs->next, i++)
1059 : 28 : fs->next = table[i];
1060 : 23 : free(table);
1061 : : }
1062 : :
1063 : : static void
1064 : 45 : alternative_sort_slaves(struct alternative *a)
1065 : : {
1066 : : int count, i;
1067 : : struct slave_link **table, *sl;
1068 : :
1069 : 45 : count = alternative_slaves_count(a);
1070 [ + + ]: 45 : if (count < 2) /* Nothing to sort */
1071 : 11 : return;
1072 : :
1073 : : /* Store objects in a table instead of a linked list */
1074 : 34 : table = xmalloc(sizeof(sl) * count);
1075 [ + + ]: 169 : for (sl = a->slaves, i = 0; sl; sl = sl->next, i++) {
1076 : 135 : table[i] = sl;
1077 : : }
1078 : :
1079 : 34 : qsort(table, count, sizeof(sl), compare_slave_link);
1080 : :
1081 : : /* Rewrite the linked list from the sorted table */
1082 : 34 : a->slaves = sl = table[0];
1083 : 34 : table[count - 1]->next = NULL;
1084 [ + + ]: 135 : for (i = 1; i < count; sl = sl->next, i++)
1085 : 101 : sl->next = table[i];
1086 : 34 : free(table);
1087 : : }
1088 : :
1089 : : static struct fileset *
1090 : 297 : alternative_get_fileset(struct alternative *a, const char *file)
1091 : : {
1092 : : struct fileset *fs;
1093 : :
1094 [ + + ]: 422 : for (fs = a->choices; fs; fs = fs->next)
1095 [ + + ]: 274 : if (strcmp(fs->master_file, file) == 0)
1096 : 149 : return fs;
1097 : :
1098 : 148 : return NULL;
1099 : : }
1100 : :
1101 : : static struct slave_link *
1102 : 416 : alternative_get_slave(struct alternative *a, const char *name)
1103 : : {
1104 : : struct slave_link *sl;
1105 : :
1106 [ + + ]: 967 : for (sl = a->slaves; sl; sl = sl->next)
1107 [ + + ]: 582 : if (strcmp(sl->name, name) == 0)
1108 : 31 : return sl;
1109 : :
1110 : 385 : return NULL;
1111 : : }
1112 : :
1113 : : static bool
1114 : 92 : alternative_has_slave(struct alternative *a, const char *name)
1115 : : {
1116 : 92 : return alternative_get_slave(a, name) != NULL;
1117 : : }
1118 : :
1119 : : static bool
1120 : 57 : alternative_has_choice(struct alternative *a, const char *file)
1121 : : {
1122 : 57 : return alternative_get_fileset(a, file) != NULL;
1123 : : }
1124 : :
1125 : : static void
1126 : 174 : alternative_add_choice(struct alternative *a, struct fileset *fs)
1127 : : {
1128 : 174 : struct fileset *cur, *prev = NULL;
1129 : :
1130 : : /* Replace if already existing */
1131 [ + + ]: 250 : for (cur = a->choices; cur; cur = cur->next) {
1132 [ + + ]: 86 : if (strcmp(cur->master_file, fs->master_file) == 0) {
1133 : 10 : fs->next = cur->next;
1134 : 10 : fileset_free(cur);
1135 [ + + ]: 10 : if (prev)
1136 : 4 : prev->next = fs;
1137 : : else
1138 : 6 : a->choices = fs;
1139 : :
1140 : : /* XXX: Be smarter in detecting change? */
1141 : 10 : a->modified = true;
1142 : 10 : return;
1143 : : }
1144 : 76 : prev = cur;
1145 : : }
1146 : :
1147 : : /* Otherwise add at the end */
1148 [ + + ]: 164 : if (prev == NULL)
1149 : 103 : a->choices = fs;
1150 : : else
1151 : 61 : prev->next = fs;
1152 : 164 : fs->next = NULL;
1153 : 164 : a->modified = true;
1154 : : }
1155 : :
1156 : : static struct slave_link *
1157 : 416 : alternative_add_slave(struct alternative *a,
1158 : : const char *slave_name, const char *slave_link)
1159 : : {
1160 : : struct slave_link *sl, *new;
1161 : :
1162 : : /* Replace if already existing */
1163 [ + + ]: 706 : for (sl = a->slaves; sl; sl = sl->next) {
1164 [ + + ]: 582 : if (strcmp(sl->name, slave_name) == 0) {
1165 : 31 : free(sl->link);
1166 : 31 : sl->link = xstrdup(slave_link);
1167 : 31 : return sl;
1168 : : }
1169 [ + + ]: 551 : if (sl->next == NULL)
1170 : 261 : break;
1171 : : }
1172 : :
1173 : : /* Otherwise create new and add at the end */
1174 : 385 : new = xmalloc(sizeof(*new));
1175 : 385 : new->name = xstrdup(slave_name);
1176 : 385 : new->link = xstrdup(slave_link);
1177 : 385 : new->updated = false;
1178 : 385 : new->next = NULL;
1179 [ + + ]: 385 : if (sl)
1180 : 261 : sl->next = new;
1181 : : else
1182 : 124 : a->slaves = new;
1183 : :
1184 : 385 : return new;
1185 : : }
1186 : :
1187 : : static void
1188 : 40 : alternative_copy_slave(struct alternative *a, struct slave_link *sl)
1189 : : {
1190 : : struct slave_link *sl_new;
1191 : :
1192 : 40 : sl_new = alternative_add_slave(a, sl->name, sl->link);
1193 : 40 : sl_new->updated = sl->updated;
1194 : 40 : }
1195 : :
1196 : : static const char *
1197 : 82 : alternative_status_string(enum alternative_status status)
1198 : : {
1199 [ + + ]: 82 : return (status == ALT_ST_AUTO) ? "auto" : "manual";
1200 : : }
1201 : :
1202 : : static const char *
1203 : 9 : alternative_status_describe(enum alternative_status status)
1204 : : {
1205 [ + + ]: 9 : return (status == ALT_ST_AUTO) ? _("auto mode") : _("manual mode");
1206 : : }
1207 : :
1208 : : static void
1209 : 163 : alternative_set_status(struct alternative *a, enum alternative_status status)
1210 : : {
1211 [ + + + + ]: 163 : if (a->status == ALT_ST_UNKNOWN || status != a->status)
1212 : 162 : a->modified = true;
1213 : :
1214 [ + + + + ]: 163 : if (a->status != ALT_ST_UNKNOWN && status != a->status)
1215 : 14 : log_msg("status of link group %s set to %s", a->master_link,
1216 : : alternative_status_string(status));
1217 : :
1218 : 163 : a->status = status;
1219 : 163 : }
1220 : :
1221 : : static void
1222 : 151 : alternative_set_link(struct alternative *a, const char *linkname)
1223 : : {
1224 [ + + + + ]: 151 : if (a->master_link == NULL || strcmp(linkname, a->master_link) != 0)
1225 : 135 : a->modified = true;
1226 : :
1227 : 151 : free(a->master_link);
1228 : 151 : a->master_link = xstrdup(linkname);
1229 : 151 : }
1230 : :
1231 : : static bool
1232 : 7 : alternative_remove_choice(struct alternative *a, const char *file)
1233 : : {
1234 : : struct fileset *fs, *fs_prev;
1235 : :
1236 : 7 : fs_prev = NULL;
1237 [ + - ]: 10 : for (fs = a->choices; fs; fs = fs->next) {
1238 [ + + ]: 10 : if (strcmp(fs->master_file, file) != 0) {
1239 : 3 : fs_prev = fs;
1240 : 3 : continue;
1241 : : }
1242 [ + + ]: 7 : if (fs_prev)
1243 : 3 : fs_prev->next = fs->next;
1244 : : else
1245 : 4 : a->choices = fs->next;
1246 : 7 : fileset_free(fs);
1247 : 7 : a->modified = true;
1248 : 7 : return true;
1249 : : }
1250 : :
1251 : 0 : return false;
1252 : : }
1253 : :
1254 : : /*
1255 : : * Alternatives Database Load/Store functions.
1256 : : */
1257 : :
1258 : : enum altdb_flags {
1259 : : ALTDB_LAX_PARSER = 1 << 0,
1260 : : ALTDB_WARN_PARSER = 1 << 1,
1261 : : };
1262 : :
1263 : : struct altdb_context {
1264 : : FILE *fh;
1265 : : char *filename;
1266 : : enum altdb_flags flags;
1267 : : bool modified;
1268 : : void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(2)
1269 : : (*bad_format)(struct altdb_context *, const char *format, ...);
1270 : : jmp_buf on_error;
1271 : : };
1272 : :
1273 : : static void
1274 : 21 : altdb_context_free(struct altdb_context *ctx)
1275 : : {
1276 [ + + ]: 21 : if (ctx->fh)
1277 : 3 : fclose(ctx->fh);
1278 : 21 : free(ctx->filename);
1279 : 21 : }
1280 : :
1281 : : static int
1282 : 117 : altdb_filter_namelist(const struct dirent *entry)
1283 : : {
1284 [ + + ]: 117 : if (strcmp(entry->d_name, ".") == 0 ||
1285 [ + + ]: 74 : strcmp(entry->d_name, "..") == 0 ||
1286 [ + - ]: 31 : (strlen(entry->d_name) > strlen(ALT_TMP_EXT) &&
1287 [ - + ]: 31 : strcmp(entry->d_name + strlen(entry->d_name) -
1288 : : strlen(ALT_TMP_EXT), ALT_TMP_EXT) == 0))
1289 : 86 : return 0;
1290 : 31 : return 1;
1291 : : }
1292 : :
1293 : : static int
1294 : 58 : altdb_get_namelist(struct dirent ***table)
1295 : : {
1296 : : int count;
1297 : :
1298 : 58 : count = scandir(admdir, table, altdb_filter_namelist, alphasort);
1299 [ + + ]: 58 : if (count < 0) {
1300 [ - + ]: 15 : if (errno != ENOENT)
1301 : 0 : syserr(_("cannot scan directory '%.255s'"), admdir);
1302 : : /* The directory does not exist, proceed anyway. */
1303 : 15 : *table = NULL;
1304 : 15 : count = 0;
1305 : : }
1306 : :
1307 : 58 : return count;
1308 : : }
1309 : :
1310 : : static void
1311 : 58 : altdb_free_namelist(struct dirent **table, int n)
1312 : : {
1313 [ + + ]: 89 : while (n--)
1314 : 31 : free(table[n]);
1315 : 58 : free(table);
1316 : 58 : }
1317 : :
1318 : : static char *
1319 : 1691 : altdb_get_line(struct altdb_context *ctx, const char *name)
1320 : : {
1321 : : char *buf, *line;
1322 : : size_t len, bufsz, i;
1323 : :
1324 : 1691 : bufsz = 1024;
1325 : 1691 : buf = xmalloc(bufsz);
1326 : :
1327 : 1691 : for (i = 0; true; i += strlen(line)) {
1328 : 1691 : errno = 0;
1329 : 1691 : line = fgets(buf + i, bufsz - i, ctx->fh);
1330 [ + - ]: 1691 : if (line) {
1331 [ - + - - ]: 1691 : if (strlen(buf) < bufsz - 1 || buf[bufsz - 2] == '\n')
1332 : : break;
1333 : : /* Need more space */
1334 : 0 : bufsz *= 2;
1335 : 0 : buf = realloc(buf, bufsz);
1336 [ # # ]: 0 : if (!buf)
1337 : 0 : error(_("failed to allocate memory"));
1338 : 0 : continue;
1339 : : }
1340 [ # # ]: 0 : if (feof(ctx->fh))
1341 : 0 : ctx->bad_format(ctx, _("unexpected end of file while trying "
1342 : : "to read %s"), name);
1343 : 0 : ctx->bad_format(ctx, _("while reading %s: %s"),
1344 : 0 : name, strerror(errno));
1345 : : }
1346 : :
1347 : 1691 : len = strlen(buf);
1348 [ + - - + ]: 1691 : if (len == 0 || buf[len - 1] != '\n') {
1349 : 0 : ctx->bad_format(ctx, _("line not terminated while trying "
1350 : : "to read %s"), name);
1351 : : }
1352 : 1691 : line[len - 1] = '\0';
1353 : :
1354 : 1691 : return buf;
1355 : : }
1356 : :
1357 : : static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(2)
1358 : 1 : altdb_parse_error(struct altdb_context *ctx, const char *format, ...)
1359 : : {
1360 : : char *msg;
1361 : : va_list args;
1362 : :
1363 : 1 : va_start(args, format);
1364 : 1 : msg = xvasprintf(format, args);
1365 : 1 : va_end(args);
1366 : :
1367 : 1 : error(_("%s corrupt: %s"), ctx->filename, msg);
1368 : : }
1369 : :
1370 : : static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(2)
1371 : 1 : altdb_parse_stop(struct altdb_context *ctx, const char *format, ...)
1372 : : {
1373 : 1 : longjmp(ctx->on_error, 1);
1374 : : }
1375 : :
1376 : : static void
1377 : 873 : altdb_print_line(struct altdb_context *ctx, const char *line)
1378 : : {
1379 [ - + ]: 873 : if (strchr(line, '\n') != NULL)
1380 : 0 : error(_("newlines prohibited in update-alternatives files (%s)"),
1381 : : line);
1382 : :
1383 [ - + ]: 873 : if (fprintf(ctx->fh, "%s\n", line) < (int) strlen(line) + 1)
1384 : 0 : syserr(_("unable to write file '%s'"), ctx->filename);
1385 : 873 : }
1386 : :
1387 : : static bool
1388 : 372 : alternative_parse_slave(struct alternative *a, struct altdb_context *ctx)
1389 : : {
1390 : : char *name, *linkname;
1391 : : struct slave_link *sl;
1392 : :
1393 : 372 : name = altdb_get_line(ctx, _("slave name"));
1394 [ + + ]: 372 : if (!strlen(name)) { /* End of list */
1395 : 88 : free(name);
1396 : 88 : return false;
1397 : : }
1398 : 284 : sl = alternative_get_slave(a, name);
1399 [ - + ]: 284 : if (sl) {
1400 : 0 : free(name);
1401 : 0 : ctx->bad_format(ctx, _("duplicate slave name %s"), sl->name);
1402 : : }
1403 : :
1404 : 284 : linkname = altdb_get_line(ctx, _("slave link"));
1405 [ - + ]: 284 : if (strcmp(linkname, a->master_link) == 0) {
1406 : 0 : free(linkname);
1407 : 0 : free(name);
1408 : 0 : ctx->bad_format(ctx, _("slave link same as main link %s"),
1409 : : a->master_link);
1410 : : }
1411 [ + + ]: 674 : for (sl = a->slaves; sl; sl = sl->next) {
1412 [ - + ]: 390 : if (strcmp(linkname, sl->link) == 0) {
1413 : 0 : free(linkname);
1414 : 0 : free(name);
1415 : 0 : ctx->bad_format(ctx, _("duplicate slave link %s"),
1416 : : sl->link);
1417 : : }
1418 : : }
1419 : :
1420 : 284 : alternative_add_slave(a, name, linkname);
1421 : 284 : free(linkname);
1422 : 284 : free(name);
1423 : :
1424 : 284 : return true;
1425 : : }
1426 : :
1427 : : static bool
1428 : 229 : alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx)
1429 : : {
1430 : : struct fileset *fs;
1431 : : struct slave_link *sl;
1432 : : char *master_file;
1433 : :
1434 : 229 : master_file = altdb_get_line(ctx, _("master file"));
1435 [ + + ]: 229 : if (!strlen(master_file)) { /* End of list */
1436 : 88 : free(master_file);
1437 : 88 : return false;
1438 : : }
1439 : :
1440 : 141 : fs = alternative_get_fileset(a, master_file);
1441 [ - + ]: 141 : if (fs)
1442 : 0 : ctx->bad_format(ctx, _("duplicate path %s"), master_file);
1443 : :
1444 [ - + ]: 141 : if (fsys_pathname_is_missing(master_file)) {
1445 : : char *junk;
1446 : :
1447 : : /* File not found - remove. */
1448 [ # # ]: 0 : if (ctx->flags & ALTDB_WARN_PARSER)
1449 : 0 : warning(_("alternative %s (part of link group %s) "
1450 : : "doesn't exist; removing from list of "
1451 : : "alternatives"), master_file, a->master_name);
1452 : 0 : junk = altdb_get_line(ctx, _("priority"));
1453 : 0 : free(junk);
1454 [ # # ]: 0 : for (sl = a->slaves; sl; sl = sl->next) {
1455 : 0 : junk = altdb_get_line(ctx, _("slave file"));
1456 : 0 : free(junk);
1457 : : }
1458 : 0 : ctx->modified = true;
1459 : : } else {
1460 : : char *prio_str, *prio_end;
1461 : : long prio;
1462 : :
1463 : 141 : prio_str = altdb_get_line(ctx, _("priority"));
1464 : 141 : errno = 0;
1465 : 141 : prio = strtol(prio_str, &prio_end, 10);
1466 : : /* XXX: Leak master_file/prio_str on non-fatal error */
1467 [ + - - + ]: 141 : if (prio_str == prio_end || *prio_end != '\0')
1468 : 0 : ctx->bad_format(ctx, _("priority of %s: %s"),
1469 : : master_file, prio_str);
1470 [ + - + - : 141 : if (prio < INT_MIN || prio > INT_MAX || errno == ERANGE)
- + ]
1471 : 0 : ctx->bad_format(ctx,
1472 : 0 : _("priority of %s is out of range: %s"),
1473 : : master_file, prio_str);
1474 : 141 : free(prio_str);
1475 : :
1476 : 141 : fs = fileset_new(master_file, prio);
1477 [ + + ]: 628 : for (sl = a->slaves; sl; sl = sl->next) {
1478 : 487 : char *slave_file = altdb_get_line(ctx, _("slave file"));
1479 : 487 : fileset_add_slave(fs, sl->name, slave_file);
1480 : 487 : free(slave_file);
1481 : : }
1482 : 141 : alternative_add_choice(a, fs);
1483 : : }
1484 : 141 : free(master_file);
1485 : :
1486 : 141 : return true;
1487 : : }
1488 : :
1489 : : static bool
1490 : 110 : alternative_load(struct alternative *a, enum altdb_flags flags)
1491 : : {
1492 : : struct altdb_context ctx;
1493 : : struct stat st;
1494 : : char *status;
1495 : : char *master_link;
1496 : :
1497 : : /* Initialize parse context */
1498 : 110 : ctx.modified = false;
1499 : 110 : ctx.flags = flags;
1500 [ + + ]: 110 : if (flags & ALTDB_LAX_PARSER)
1501 : 31 : ctx.bad_format = altdb_parse_stop;
1502 : : else
1503 : 79 : ctx.bad_format = altdb_parse_error;
1504 : 110 : ctx.filename = xasprintf("%s/%s", admdir, a->master_name);
1505 : :
1506 : : /* Open the alternative file. */
1507 : 110 : ctx.fh = fopen(ctx.filename, "r");
1508 [ + + ]: 110 : if (ctx.fh == NULL) {
1509 [ + - ]: 18 : if (errno == ENOENT) {
1510 : 18 : altdb_context_free(&ctx);
1511 : 18 : return false;
1512 : : }
1513 : :
1514 : 0 : syserr(_("unable to open file '%s'"), ctx.filename);
1515 : : }
1516 : :
1517 [ + + ]: 92 : if (setjmp(ctx.on_error)) {
1518 : 1 : altdb_context_free(&ctx);
1519 : 1 : alternative_reset(a);
1520 : 1 : return false;
1521 : : }
1522 : :
1523 : : /* Verify the alternative is not empty. */
1524 [ - + ]: 92 : if (fstat(fileno(ctx.fh), &st) == -1)
1525 : 0 : syserr(_("cannot stat file '%s'"), ctx.filename);
1526 [ + + ]: 92 : if (st.st_size == 0) {
1527 : 2 : altdb_context_free(&ctx);
1528 : 2 : alternative_reset(a);
1529 : 2 : return false;
1530 : : }
1531 : :
1532 : : /* Start parsing mandatory attributes (link+status) of the alternative */
1533 : 90 : alternative_reset(a);
1534 : 90 : status = altdb_get_line(&ctx, _("status"));
1535 [ + + + + ]: 90 : if (strcmp(status, "auto") != 0 && strcmp(status, "manual") != 0)
1536 : 2 : ctx.bad_format(&ctx, _("invalid status"));
1537 [ + + ]: 88 : alternative_set_status(a, (strcmp(status, "auto") == 0) ?
1538 : : ALT_ST_AUTO : ALT_ST_MANUAL);
1539 : 88 : free(status);
1540 : :
1541 : 88 : master_link = altdb_get_line(&ctx, _("master link"));
1542 : 88 : alternative_set_link(a, master_link);
1543 : 88 : free(master_link);
1544 : :
1545 : : /* Parse the description of the slaves links of the alternative */
1546 [ + + ]: 372 : while (alternative_parse_slave(a, &ctx));
1547 : :
1548 : : /* Parse the available choices in the alternative */
1549 [ + + ]: 229 : while (alternative_parse_fileset(a, &ctx)) ;
1550 : :
1551 : : /* Close database file */
1552 [ - + ]: 88 : if (fclose(ctx.fh))
1553 : 0 : syserr(_("unable to close file '%s'"), ctx.filename);
1554 : 88 : free(ctx.filename);
1555 : :
1556 : : /* Initialize the modified field which has been erroneously changed
1557 : : * by the various alternative_(add|set)_* calls:
1558 : : * false unless a choice has been auto-cleaned */
1559 : 88 : a->modified = ctx.modified;
1560 : :
1561 : 88 : return true;
1562 : : }
1563 : :
1564 : : static void
1565 : 45 : alternative_save(struct alternative *a)
1566 : : {
1567 : : struct altdb_context ctx;
1568 : : struct slave_link *sl, *sl_prev;
1569 : : struct fileset *fs;
1570 : : char *filenew, *file;
1571 : :
1572 : : /* Cleanup unused slaves before writing admin file. */
1573 : 45 : sl_prev = NULL;
1574 : 45 : sl = a->slaves;
1575 [ + + ]: 199 : while (sl) {
1576 : 154 : bool has_slave = false;
1577 : :
1578 [ + + ]: 231 : for (fs = a->choices; fs; fs = fs->next) {
1579 [ + + ]: 223 : if (fileset_has_slave(fs, sl->name)) {
1580 : 146 : has_slave = true;
1581 : 146 : break;
1582 : : }
1583 : : }
1584 : :
1585 [ + + ]: 154 : if (!has_slave) {
1586 : : struct slave_link *sl_rm;
1587 : :
1588 : 8 : verbose(_("discarding obsolete slave link %s (%s)"),
1589 : : sl->name, sl->link);
1590 [ + + ]: 8 : if (sl_prev)
1591 : 6 : sl_prev->next = sl->next;
1592 : : else
1593 : 2 : a->slaves = sl->next;
1594 : 8 : sl_rm = sl;
1595 : 8 : sl = sl->next;
1596 : 8 : slave_link_free(sl_rm);
1597 : : } else {
1598 : 146 : sl_prev = sl;
1599 : 146 : sl = sl->next;
1600 : : }
1601 : : }
1602 : :
1603 : : /* Sort entries */
1604 : 45 : alternative_sort_slaves(a);
1605 : 45 : alternative_sort_choices(a);
1606 : :
1607 : : /* Write admin file. */
1608 : 45 : file = xasprintf("%s/%s", admdir, a->master_name);
1609 : 45 : filenew = xasprintf("%s" ALT_TMP_EXT, file);
1610 : :
1611 : 45 : ctx.filename = filenew;
1612 : 45 : ctx.fh = fopen(ctx.filename, "w");
1613 [ - + - - ]: 45 : if (ctx.fh == NULL && errno == ENOENT) {
1614 [ # # ]: 0 : if (make_path(admdir, 0755) < 0)
1615 : 0 : syserr(_("cannot create administrative directory '%s'"),
1616 : : admdir);
1617 : 0 : ctx.fh = fopen(ctx.filename, "w");
1618 : : }
1619 [ - + ]: 45 : if (ctx.fh == NULL)
1620 : 0 : syserr(_("unable to create file '%s'"), ctx.filename);
1621 : :
1622 : 45 : altdb_print_line(&ctx, alternative_status_string(a->status));
1623 : 45 : altdb_print_line(&ctx, a->master_link);
1624 [ + + ]: 191 : for (sl = a->slaves; sl; sl = sl->next) {
1625 : 146 : altdb_print_line(&ctx, sl->name);
1626 : 146 : altdb_print_line(&ctx, sl->link);
1627 : : }
1628 : 45 : altdb_print_line(&ctx, "");
1629 : :
1630 [ + + ]: 118 : for (fs = a->choices; fs; fs = fs->next) {
1631 : : char *prio;
1632 : :
1633 : 73 : altdb_print_line(&ctx, fs->master_file);
1634 : :
1635 : 73 : prio = xasprintf("%d", fs->priority);
1636 : 73 : altdb_print_line(&ctx, prio);
1637 : 73 : free(prio);
1638 : :
1639 [ + + ]: 328 : for (sl = a->slaves; sl; sl = sl->next) {
1640 [ + + ]: 255 : if (fileset_has_slave(fs, sl->name))
1641 : 168 : altdb_print_line(&ctx,
1642 : 168 : fileset_get_slave(fs, sl->name));
1643 : : else
1644 : 87 : altdb_print_line(&ctx, "");
1645 : : }
1646 : : }
1647 : 45 : altdb_print_line(&ctx, "");
1648 : :
1649 : : /* Close database file */
1650 [ - + ]: 45 : if (fflush(ctx.fh))
1651 : 0 : syserr(_("unable to flush file '%s'"), ctx.filename);
1652 [ - + ]: 45 : if (fsync(fileno(ctx.fh)))
1653 : 0 : syserr(_("unable to sync file '%s'"), ctx.filename);
1654 [ - + ]: 45 : if (fclose(ctx.fh))
1655 : 0 : syserr(_("unable to close file '%s'"), ctx.filename);
1656 : :
1657 : : /* Put in place atomically. */
1658 : 45 : xrename(filenew, file);
1659 : :
1660 : 45 : free(filenew);
1661 : 45 : free(file);
1662 : 45 : }
1663 : :
1664 : : static const char *
1665 : 91 : alternative_set_current(struct alternative *a, char *new_choice)
1666 : : {
1667 : 91 : a->known_current = true;
1668 : 91 : a->current = new_choice;
1669 : :
1670 : 91 : return new_choice;
1671 : : }
1672 : :
1673 : : static const char *
1674 : 166 : alternative_get_current(struct alternative *a)
1675 : : {
1676 : : char *curlink;
1677 : : char *file;
1678 : :
1679 [ + + ]: 166 : if (a->known_current)
1680 : 75 : return a->current;
1681 : :
1682 : 91 : curlink = xasprintf("%s/%s", altdir, a->master_name);
1683 : 91 : file = fsys_areadlink(curlink);
1684 [ + + - + ]: 91 : if (file == NULL && errno != ENOENT)
1685 : 0 : syserr(_("cannot stat file '%s%s'"), instdir, curlink);
1686 : 91 : free(curlink);
1687 : :
1688 : 91 : return alternative_set_current(a, file);
1689 : : }
1690 : :
1691 : : static struct fileset *
1692 : 64 : alternative_get_best(struct alternative *a)
1693 : : {
1694 : : struct fileset *fs, *best;
1695 : : const char *current;
1696 : :
1697 : 64 : current = alternative_get_current(a);
1698 [ + + ]: 64 : if (current)
1699 : 48 : best = alternative_get_fileset(a, current);
1700 : : else
1701 : 16 : best = NULL;
1702 : :
1703 [ + + ]: 64 : if (best == NULL)
1704 : 23 : best = a->choices;
1705 : :
1706 [ + + ]: 163 : for (fs = a->choices; fs; fs = fs->next)
1707 [ + + ]: 99 : if (fs->priority > best->priority)
1708 : 11 : best = fs;
1709 : :
1710 : 64 : return best;
1711 : : }
1712 : :
1713 : : static void
1714 : 23 : alternative_display_query(struct alternative *a)
1715 : : {
1716 : : struct fileset *best, *fs;
1717 : : struct slave_link *sl;
1718 : : const char *current;
1719 : :
1720 : 23 : pr("Name: %s", a->master_name);
1721 : 23 : pr("Link: %s", a->master_link);
1722 [ + - ]: 23 : if (alternative_slaves_count(a) > 0) {
1723 : 23 : pr("Slaves:");
1724 [ + + ]: 97 : for (sl = a->slaves; sl; sl = sl->next)
1725 : 74 : pr(" %s %s", sl->name, sl->link);
1726 : : }
1727 : 23 : pr("Status: %s", alternative_status_string(a->status));
1728 : 23 : best = alternative_get_best(a);
1729 [ + - ]: 23 : if (best)
1730 : 23 : pr("Best: %s", best->master_file);
1731 : 23 : current = alternative_get_current(a);
1732 [ + - ]: 23 : pr("Value: %s", current ? current : "none");
1733 : :
1734 [ + + ]: 64 : for (fs = a->choices; fs; fs = fs->next) {
1735 : 41 : printf("\n");
1736 : 41 : pr("Alternative: %s", fs->master_file);
1737 : 41 : pr("Priority: %d", fs->priority);
1738 [ - + ]: 41 : if (alternative_slaves_count(a) == 0)
1739 : 0 : continue;
1740 : 41 : pr("Slaves:");
1741 [ + + ]: 184 : for (sl = a->slaves; sl; sl = sl->next) {
1742 [ + + ]: 143 : if (fileset_has_slave(fs, sl->name))
1743 : 87 : pr(" %s %s", sl->name,
1744 : 87 : fileset_get_slave(fs, sl->name));
1745 : : }
1746 : : }
1747 : 23 : }
1748 : :
1749 : : static void
1750 : 0 : alternative_display_user(struct alternative *a)
1751 : : {
1752 : : const char *current;
1753 : : struct fileset *fs;
1754 : : struct slave_link *sl;
1755 : :
1756 : 0 : pr("%s - %s", a->master_name, alternative_status_describe(a->status));
1757 : 0 : fs = alternative_get_best(a);
1758 [ # # ]: 0 : if (fs)
1759 : 0 : pr(_(" link best version is %s"), fs->master_file);
1760 : : else
1761 : 0 : pr(_(" link best version not available"));
1762 : 0 : current = alternative_get_current(a);
1763 [ # # ]: 0 : if (current) {
1764 : 0 : pr(_(" link currently points to %s"), current);
1765 : : } else {
1766 : 0 : pr(_(" link currently absent"));
1767 : : }
1768 : 0 : pr(_(" link %s is %s"), a->master_name, a->master_link);
1769 [ # # ]: 0 : for (sl = a->slaves; sl; sl = sl->next)
1770 : 0 : pr(_(" slave %s is %s"), sl->name, sl->link);
1771 : :
1772 [ # # ]: 0 : for (fs = a->choices; fs; fs = fs->next) {
1773 : 0 : pr(_("%s - priority %d"), fs->master_file, fs->priority);
1774 [ # # ]: 0 : for (sl = a->slaves; sl; sl = sl->next) {
1775 [ # # ]: 0 : if (fileset_has_slave(fs, sl->name))
1776 : 0 : pr(_(" slave %s: %s"), sl->name,
1777 : 0 : fileset_get_slave(fs, sl->name));
1778 : : }
1779 : : }
1780 : 0 : }
1781 : :
1782 : : static void
1783 : 0 : alternative_display_list(struct alternative *a)
1784 : : {
1785 : : struct fileset *fs;
1786 : :
1787 [ # # ]: 0 : for (fs = a->choices; fs; fs = fs->next)
1788 : 0 : pr("%s", fs->master_file);
1789 : 0 : }
1790 : :
1791 : : static void
1792 : 9 : alternative_print_choice(struct alternative *a, enum alternative_status status,
1793 : : struct fileset *fs, int idx, int len)
1794 : : {
1795 : 9 : const char *current = alternative_get_current(a);
1796 : : int mark;
1797 : :
1798 [ + + + - ]: 9 : if (a->status == status &&
1799 [ + + ]: 5 : current && strcmp(current, fs->master_file) == 0)
1800 : 3 : mark = '*';
1801 : : else
1802 : 6 : mark = ' ';
1803 : :
1804 : 9 : pr("%c %-12d %-*s % -10d %s", mark, idx, len,
1805 : : fs->master_file, fs->priority, alternative_status_describe(status));
1806 : 9 : }
1807 : :
1808 : : static char *
1809 : 3 : alternative_select_choice(struct alternative *a)
1810 : : {
1811 : : const char *current;
1812 : : char *ret, selection[_POSIX_PATH_MAX];
1813 : : struct fileset *best, *fs;
1814 : : int n_choices;
1815 : : int len, idx;
1816 : :
1817 : 3 : n_choices = alternative_choices_count(a);
1818 : 3 : current = alternative_get_current(a);
1819 : 3 : best = alternative_get_best(a);
1820 [ - + ]: 3 : assert(best);
1821 : :
1822 : 3 : len = 15;
1823 [ + + ]: 9 : for (fs = a->choices; fs; fs = fs->next)
1824 [ - + ]: 6 : len = max(len, (int)strlen(fs->master_file) + 1);
1825 : :
1826 : : for (;;) {
1827 : 3 : pr(P_("There is %d choice for the alternative %s (providing %s).",
1828 : : "There are %d choices for the alternative %s (providing %s).",
1829 : : n_choices), n_choices, a->master_name, a->master_link);
1830 : 3 : printf("\n");
1831 : :
1832 : 3 : pr(" %-12.12s %-*.*s %-10.10s %s", _("Selection"), len, len,
1833 : : _("Path"), _("Priority"), _("Status"));
1834 : 3 : pr("------------------------------------------------------------");
1835 : 3 : idx = 0;
1836 : 3 : alternative_print_choice(a, ALT_ST_AUTO, best, idx++, len);
1837 [ + + ]: 9 : for (fs = a->choices; fs; fs = fs->next, idx++)
1838 : 6 : alternative_print_choice(a, ALT_ST_MANUAL, fs, idx, len);
1839 : 3 : printf("\n");
1840 : 3 : printf(_("Press <enter> to keep the current choice[*], "
1841 : : "or type selection number: "));
1842 : 3 : ret = fgets(selection, sizeof(selection), stdin);
1843 [ + - - + ]: 3 : if (ret == NULL || strlen(selection) == 0) {
1844 : 0 : return NULL;
1845 : : }
1846 : 3 : selection[strlen(selection) - 1] = '\0';
1847 [ - + ]: 3 : if (strlen(selection) == 0)
1848 : 0 : return xstrdup(current);
1849 : 3 : errno = 0;
1850 : 3 : idx = strtol(selection, &ret, 10);
1851 [ + - + - : 3 : if (idx >= 0 && errno == 0 && *ret == '\0') {
+ + ]
1852 : : /* Look up by index */
1853 [ + - ]: 1 : if (idx == 0) {
1854 : 1 : alternative_set_status(a, ALT_ST_AUTO);
1855 : 1 : return xstrdup(best->master_file);
1856 : : }
1857 : 0 : idx--;
1858 [ # # # # ]: 0 : for (fs = a->choices; idx && fs; idx--)
1859 : 0 : fs = fs->next;
1860 [ # # ]: 0 : if (fs) {
1861 : 0 : alternative_set_status(a, ALT_ST_MANUAL);
1862 : 0 : return xstrdup(fs->master_file);
1863 : : }
1864 : : } else {
1865 : : /* Look up by name */
1866 : 2 : fs = alternative_get_fileset(a, selection);
1867 [ + - ]: 2 : if (fs) {
1868 : 2 : alternative_set_status(a, ALT_ST_MANUAL);
1869 : 2 : return xstrdup(selection);
1870 : : }
1871 : : }
1872 : : }
1873 : : }
1874 : :
1875 : : static char *
1876 : 3 : alternative_config(struct alternative *a, const char *current_choice)
1877 : : {
1878 : 3 : char *new_choice = NULL;
1879 : :
1880 [ - + ]: 3 : if (alternative_choices_count(a) == 0) {
1881 : 0 : pr(_("There is no program which provides %s."),
1882 : : a->master_name);
1883 : 0 : pr(_("Nothing to configure."));
1884 [ - + - - ]: 3 : } else if (opt_skip_auto && a->status == ALT_ST_AUTO) {
1885 : 0 : alternative_display_user(a);
1886 : : } else {
1887 : 3 : new_choice = alternative_select_choice(a);
1888 : : }
1889 : :
1890 : 3 : return new_choice;
1891 : : }
1892 : :
1893 : : static void
1894 : 395 : alternative_add_commit_op(struct alternative *a, enum opcode opcode,
1895 : : const char *arg_a, const char *arg_b)
1896 : : {
1897 : : struct commit_operation *op, *cur;
1898 : :
1899 : 395 : op = xmalloc(sizeof(*op));
1900 : 395 : op->opcode = opcode;
1901 : 395 : op->arg_a = xstrdup(arg_a);
1902 : 395 : op->arg_b = xstrdup(arg_b);
1903 : 395 : op->next = NULL;
1904 : :
1905 : : /* Add at the end */
1906 : 395 : cur = a->commit_ops;
1907 [ + + + + ]: 2515 : while (cur && cur->next)
1908 : 2120 : cur = cur->next;
1909 [ + + ]: 395 : if (cur)
1910 : 360 : cur->next = op;
1911 : : else
1912 : 35 : a->commit_ops = op;
1913 : 395 : }
1914 : :
1915 : : static void
1916 : 46 : alternative_commit(struct alternative *a)
1917 : : {
1918 : : struct commit_operation *op;
1919 : :
1920 [ + + ]: 433 : for (op = a->commit_ops; op; op = op->next) {
1921 [ - + + + : 387 : switch (op->opcode) {
- ]
1922 : 0 : case OPCODE_NOP:
1923 : 0 : break;
1924 : 60 : case OPCODE_RM:
1925 : 60 : fsys_rm(op->arg_a);
1926 : 60 : break;
1927 : 188 : case OPCODE_MV:
1928 : 188 : fsys_mv(op->arg_a, op->arg_b);
1929 : 188 : break;
1930 : 139 : case OPCODE_REF_TIME:
1931 : 139 : fsys_set_ref_time(op->arg_a, op->arg_b);
1932 : 139 : break;
1933 : : }
1934 : : }
1935 : :
1936 : 46 : alternative_commit_operations_free(a);
1937 : 46 : }
1938 : :
1939 : : enum alternative_path_status {
1940 : : ALT_PATH_SYMLINK,
1941 : : ALT_PATH_MISSING,
1942 : : ALT_PATH_OTHER,
1943 : : };
1944 : :
1945 : : static enum alternative_path_status
1946 : 198 : alternative_path_classify(const char *linkname)
1947 : : {
1948 : : struct stat st;
1949 : :
1950 [ + + ]: 198 : if (fsys_lstat(linkname, &st) == -1) {
1951 [ - + ]: 93 : if (errno != ENOENT)
1952 : 0 : syserr(_("cannot stat file '%s%s'"), instdir, linkname);
1953 : 93 : return ALT_PATH_MISSING;
1954 [ + + ]: 105 : } else if (S_ISLNK(st.st_mode)) {
1955 : 82 : return ALT_PATH_SYMLINK;
1956 : : } else {
1957 : 23 : return ALT_PATH_OTHER;
1958 : : }
1959 : : }
1960 : :
1961 : : static bool
1962 : 50 : alternative_path_can_remove(const char *linkname)
1963 : : {
1964 [ + + ]: 50 : if (opt_force)
1965 : 8 : return true;
1966 : :
1967 [ + + ]: 42 : if (alternative_path_classify(linkname) == ALT_PATH_OTHER)
1968 : 4 : return false;
1969 : : else
1970 : 38 : return true;
1971 : : }
1972 : :
1973 : : static bool
1974 : 121 : alternative_path_needs_update(const char *linkname, const char *filename)
1975 : : {
1976 : : char *linktarget;
1977 : : bool update;
1978 : :
1979 [ + + ]: 121 : if (opt_force)
1980 : 7 : return true;
1981 : :
1982 [ + + + ]: 114 : switch (alternative_path_classify(linkname)) {
1983 : 32 : case ALT_PATH_SYMLINK:
1984 : 32 : linktarget = fsys_xreadlink(linkname);
1985 [ + - ]: 32 : if (strcmp(linktarget, filename) == 0)
1986 : 32 : update = false;
1987 : : else
1988 : 0 : update = true;
1989 : 32 : free(linktarget);
1990 : :
1991 : 32 : return update;
1992 : 16 : case ALT_PATH_OTHER:
1993 : 16 : warning(_("not replacing %s with a link"), linkname);
1994 : 16 : return false;
1995 : 66 : case ALT_PATH_MISSING:
1996 : : default:
1997 : 66 : return true;
1998 : : }
1999 : : }
2000 : :
2001 : : static void
2002 : 121 : alternative_prepare_install_single(struct alternative *a, const char *name,
2003 : : const char *linkname, const char *file)
2004 : : {
2005 : : char *fntmp, *fn;
2006 : :
2007 : : /* Create alternatives directory (/etc/alternatives) if missing. */
2008 [ - + ]: 121 : if (fsys_pathname_is_missing(altdir)) {
2009 : 0 : char *root_altdir = fsys_get_path(altdir);
2010 : :
2011 [ # # ]: 0 : if (make_path(root_altdir, 0755) < 0)
2012 : 0 : syserr(_("cannot create alternatives directory '%s'"),
2013 : : root_altdir);
2014 : :
2015 : 0 : free(root_altdir);
2016 : : }
2017 : :
2018 : 121 : fn = xasprintf("%s/%s", altdir, name);
2019 : :
2020 : : /* Create link in /etc/alternatives. */
2021 : 121 : fntmp = xasprintf("%s/%s" ALT_TMP_EXT, altdir, name);
2022 : 121 : fsys_symlink(file, fntmp);
2023 : 121 : alternative_add_commit_op(a, OPCODE_MV, fntmp, fn);
2024 [ + + ]: 121 : if (fsys_pathname_is_missing(fn))
2025 : 76 : alternative_add_commit_op(a, OPCODE_REF_TIME, fn, file);
2026 : 121 : free(fntmp);
2027 : :
2028 [ + + ]: 121 : if (alternative_path_needs_update(linkname, fn)) {
2029 : : /* Create alternative link. */
2030 : 73 : fntmp = xasprintf("%s" ALT_TMP_EXT, linkname);
2031 : 73 : fsys_symlink(fn, fntmp);
2032 : 71 : alternative_add_commit_op(a, OPCODE_MV, fntmp, linkname);
2033 [ + + ]: 71 : if (fsys_pathname_is_missing(linkname))
2034 : 67 : alternative_add_commit_op(a, OPCODE_REF_TIME, linkname, fn);
2035 : 71 : free(fntmp);
2036 : : }
2037 : 119 : free(fn);
2038 : 119 : }
2039 : :
2040 : : static void
2041 : 35 : alternative_prepare_install(struct alternative *a, const char *choice)
2042 : : {
2043 : : struct slave_link *sl;
2044 : : struct fileset *fs;
2045 : :
2046 : 35 : fs = alternative_get_fileset(a, choice);
2047 [ - + ]: 35 : if (fs == NULL)
2048 : 0 : error(_("can't install unknown choice %s"), choice);
2049 : :
2050 : : /* Take care of master alternative */
2051 : 35 : alternative_prepare_install_single(a, a->master_name, a->master_link,
2052 : : choice);
2053 : :
2054 : : /* Take care of slaves alternatives */
2055 [ + + ]: 150 : for (sl = a->slaves; sl; sl = sl->next) {
2056 : : char *fn;
2057 : :
2058 [ + + ]: 117 : if (fileset_can_install_slave(fs, sl->name)) {
2059 : 172 : alternative_prepare_install_single(a, sl->name,
2060 : 86 : sl->link, fileset_get_slave(fs, sl->name));
2061 : 85 : continue;
2062 : : }
2063 : :
2064 : : /* Slave can't be installed */
2065 [ + + ]: 31 : if (fileset_has_slave(fs, sl->name))
2066 : 6 : warning(_("skip creation of %s because associated "
2067 : : "file %s (of link group %s) doesn't exist"),
2068 : 6 : sl->link, fileset_get_slave(fs, sl->name),
2069 : : a->master_name);
2070 : :
2071 : : /* Drop unused slave. */
2072 : 31 : fn = xasprintf("%s/%s", altdir, sl->name);
2073 [ + + ]: 31 : if (alternative_path_can_remove(sl->link))
2074 : 29 : alternative_add_commit_op(a, OPCODE_RM, sl->link, NULL);
2075 : : else
2076 : 2 : warning(_("not removing %s since it's not a symlink"),
2077 : : sl->link);
2078 : 31 : alternative_add_commit_op(a, OPCODE_RM, fn, NULL);
2079 : 31 : free(fn);
2080 : : }
2081 : 33 : }
2082 : :
2083 : : static void
2084 : 5 : alternative_remove_files(struct alternative *a)
2085 : : {
2086 : : struct slave_link *sl;
2087 : :
2088 : 5 : fsys_rm_args("%s" ALT_TMP_EXT, a->master_link);
2089 [ + + ]: 5 : if (alternative_path_can_remove(a->master_link))
2090 : 4 : fsys_rm(a->master_link);
2091 : :
2092 : 5 : fsys_rm_args("%s/%s" ALT_TMP_EXT, altdir, a->master_name);
2093 : 5 : fsys_rm_args("%s/%s", altdir, a->master_name);
2094 : :
2095 [ + + ]: 19 : for (sl = a->slaves; sl; sl = sl->next) {
2096 : 14 : fsys_rm_args("%s" ALT_TMP_EXT, sl->link);
2097 [ + + ]: 14 : if (alternative_path_can_remove(sl->link))
2098 : 13 : fsys_rm(sl->link);
2099 : :
2100 : 14 : fsys_rm_args("%s/%s" ALT_TMP_EXT, altdir, sl->name);
2101 : 14 : fsys_rm_args("%s/%s", altdir, sl->name);
2102 : : }
2103 : : /* Drop admin file */
2104 : 5 : xunlink_args("%s/%s", admdir, a->master_name);
2105 : 5 : }
2106 : :
2107 : : static char *
2108 : 7 : alternative_remove(struct alternative *a, const char *current_choice,
2109 : : const char *path)
2110 : : {
2111 : 7 : char *new_choice = NULL;
2112 : :
2113 [ + - ]: 7 : if (alternative_has_choice(a, path))
2114 : 7 : alternative_remove_choice(a, path);
2115 : : else
2116 : 0 : verbose(_("alternative %s for %s not registered; not removing"),
2117 : : path, a->master_name);
2118 : :
2119 [ + - + - ]: 7 : if (current_choice && strcmp(current_choice, path) == 0) {
2120 : : struct fileset *best;
2121 : :
2122 : : /* Current choice is removed. */
2123 [ + + ]: 7 : if (a->status == ALT_ST_MANUAL) {
2124 : : /* And it was manual, switch to auto. */
2125 : 3 : info(_("removing manually selected alternative "
2126 : : "- switching %s to auto mode"),
2127 : : a->master_name);
2128 : 3 : alternative_set_status(a, ALT_ST_AUTO);
2129 : : }
2130 : 7 : best = alternative_get_best(a);
2131 [ + + ]: 7 : if (best)
2132 : 4 : new_choice = xstrdup(best->master_file);
2133 : : }
2134 : :
2135 : 7 : return new_choice;
2136 : : }
2137 : :
2138 : : static bool
2139 : 90 : alternative_has_broken_symlink(const char *linkname, const char *ref_target)
2140 : : {
2141 : : char *target;
2142 : :
2143 : 90 : target = fsys_areadlink(linkname);
2144 [ + + ]: 90 : if (!target)
2145 : 4 : return true;
2146 [ - + ]: 86 : if (strcmp(target, ref_target) != 0) {
2147 : 0 : free(target);
2148 : 0 : return true;
2149 : : }
2150 : 86 : free(target);
2151 : 86 : return false;
2152 : : }
2153 : :
2154 : : static bool
2155 : 46 : alternative_has_broken_slave(struct slave_link *sl, struct fileset *fs)
2156 : : {
2157 [ + + ]: 46 : if (fileset_can_install_slave(fs, sl->name)) {
2158 : : char *wanted;
2159 : : const char *sl_target;
2160 : :
2161 : : /* Verify link -> /etc/alternatives/foo */
2162 : 36 : wanted = xasprintf("%s/%s", altdir, sl->name);
2163 [ - + ]: 36 : if (alternative_has_broken_symlink(sl->link, wanted)) {
2164 : 0 : free(wanted);
2165 : 0 : return true;
2166 : : }
2167 : :
2168 : : /* Verify /etc/alternatives/foo -> file */
2169 : 36 : sl_target = fileset_get_slave(fs, sl->name);
2170 [ - + ]: 36 : if (alternative_has_broken_symlink(wanted, sl_target)) {
2171 : 0 : free(wanted);
2172 : 0 : return true;
2173 : : }
2174 : :
2175 : 36 : free(wanted);
2176 : : } else {
2177 : : char *sl_altlnk;
2178 : :
2179 : : /* Slave link must not exist. */
2180 [ - + ]: 10 : if (alternative_path_classify(sl->link) != ALT_PATH_MISSING)
2181 : 0 : return true;
2182 : 10 : sl_altlnk = xasprintf("%s/%s", altdir, sl->name);
2183 [ + + ]: 10 : if (alternative_path_classify(sl_altlnk) != ALT_PATH_MISSING) {
2184 : 1 : free(sl_altlnk);
2185 : 1 : return true;
2186 : : }
2187 : 9 : free(sl_altlnk);
2188 : : }
2189 : :
2190 : 45 : return false;
2191 : : }
2192 : :
2193 : : static enum alternative_update_reason
2194 : 18 : alternative_needs_update(struct alternative *a)
2195 : : {
2196 : 18 : enum alternative_update_reason reason = ALT_UPDATE_NO;
2197 : : const char *current;
2198 : : char *wanted;
2199 : : struct fileset *fs;
2200 : : struct slave_link *sl;
2201 : :
2202 : : /* Check master link */
2203 : 18 : wanted = xasprintf("%s/%s", altdir, a->master_name);
2204 [ + + ]: 18 : if (alternative_has_broken_symlink(a->master_link, wanted)) {
2205 : 4 : free(wanted);
2206 : 4 : return ALT_UPDATE_LINK_BROKEN;
2207 : : }
2208 : 14 : free(wanted);
2209 : :
2210 : : /* Stop if we have an unmanaged alternative */
2211 : 14 : current = alternative_get_current(a);
2212 [ - + ]: 14 : if (current == NULL)
2213 : 0 : return ALT_UPDATE_LINK_BROKEN;
2214 : :
2215 : 14 : fs = alternative_get_fileset(a, current);
2216 : :
2217 : : /* Stop if we do not have the choice. */
2218 [ - + ]: 14 : if (fs == NULL)
2219 : 0 : return ALT_UPDATE_NO;
2220 : :
2221 : : /* Check slaves */
2222 [ + + ]: 60 : for (sl = a->slaves; sl; sl = sl->next) {
2223 [ + + ]: 46 : if (alternative_has_broken_slave(sl, fs)) {
2224 [ + - ]: 1 : if (sl->updated)
2225 : 1 : reason = ALT_UPDATE_SLAVE_CHANGED;
2226 : : else
2227 : 0 : return ALT_UPDATE_LINK_BROKEN;
2228 : : }
2229 : : }
2230 : :
2231 : 14 : return reason;
2232 : : }
2233 : :
2234 : : struct alternative_map {
2235 : : struct alternative_map *next;
2236 : :
2237 : : const char *key;
2238 : : struct alternative *item;
2239 : : };
2240 : :
2241 : : static struct alternative_map *
2242 : 277 : alternative_map_new(const char *key, struct alternative *a)
2243 : : {
2244 : : struct alternative_map *am;
2245 : :
2246 : 277 : am = xmalloc(sizeof(*am));
2247 : 277 : am->next = NULL;
2248 : 277 : am->key = key;
2249 : 277 : am->item = a;
2250 : :
2251 : 277 : return am;
2252 : : }
2253 : :
2254 : : static struct alternative *
2255 : 266 : alternative_map_find(struct alternative_map *am, const char *key)
2256 : : {
2257 [ + + ]: 669 : for (; am; am = am->next)
2258 [ + + + + ]: 505 : if (am->key && strcmp(am->key, key) == 0)
2259 : 102 : return am->item;
2260 : :
2261 : 164 : return NULL;
2262 : : }
2263 : :
2264 : : static void
2265 : 234 : alternative_map_add(struct alternative_map *am, const char *key,
2266 : : struct alternative *a)
2267 : : {
2268 : 234 : alternative_ref(a);
2269 : :
2270 [ + + ]: 234 : if (am->key == NULL) {
2271 : 56 : am->key = key;
2272 : 56 : am->item = a;
2273 : : } else {
2274 : 178 : struct alternative_map *new = alternative_map_new(key, a);
2275 : :
2276 [ + + ]: 424 : while (am->next)
2277 : 246 : am = am->next;
2278 : 178 : am->next = new;
2279 : : }
2280 : 234 : }
2281 : :
2282 : : static void
2283 : 17 : alternative_map_load_names(struct alternative_map *alt_map_obj)
2284 : : {
2285 : : struct dirent **table;
2286 : : int i, count;
2287 : :
2288 : 17 : count = altdb_get_namelist(&table);
2289 [ + + ]: 19 : for (i = 0; i < count; i++) {
2290 : 2 : struct alternative *a_new = alternative_new(table[i]->d_name);
2291 : :
2292 [ - + ]: 2 : if (!alternative_load(a_new, ALTDB_LAX_PARSER)) {
2293 : 0 : alternative_free(a_new);
2294 : 0 : continue;
2295 : : }
2296 : 2 : alternative_map_add(alt_map_obj, a_new->master_name, a_new);
2297 : :
2298 : 2 : alternative_unref(a_new);
2299 : : }
2300 : 17 : altdb_free_namelist(table, count);
2301 : 17 : }
2302 : :
2303 : : static void
2304 : 41 : alternative_map_load_tree(struct alternative_map *alt_map_links,
2305 : : struct alternative_map *alt_map_parent)
2306 : : {
2307 : : struct dirent **table;
2308 : : int i, count;
2309 : :
2310 : 41 : count = altdb_get_namelist(&table);
2311 [ + + ]: 70 : for (i = 0; i < count; i++) {
2312 : : struct slave_link *sl;
2313 : 29 : struct alternative *a_new = alternative_new(table[i]->d_name);
2314 : :
2315 [ + + ]: 29 : if (!alternative_load(a_new, ALTDB_LAX_PARSER)) {
2316 : 2 : alternative_free(a_new);
2317 : 2 : continue;
2318 : : }
2319 : 27 : alternative_map_add(alt_map_links, a_new->master_link, a_new);
2320 : 27 : alternative_map_add(alt_map_parent, a_new->master_name, a_new);
2321 [ + + ]: 116 : for (sl = a_new->slaves; sl; sl = sl->next) {
2322 : 89 : alternative_map_add(alt_map_links, sl->link, a_new);
2323 : 89 : alternative_map_add(alt_map_parent, sl->name, a_new);
2324 : : }
2325 : :
2326 : 27 : alternative_unref(a_new);
2327 : : }
2328 : 41 : altdb_free_namelist(table, count);
2329 : 41 : }
2330 : :
2331 : : static void
2332 : 85 : alternative_map_free(struct alternative_map *am)
2333 : : {
2334 : : struct alternative_map *am_next;
2335 : :
2336 [ + + ]: 292 : while (am) {
2337 : 207 : am_next = am->next;
2338 [ + + ]: 207 : if (am->item)
2339 : 164 : alternative_free(am->item);
2340 : 207 : free(am);
2341 : 207 : am = am_next;
2342 : : }
2343 : 85 : }
2344 : :
2345 : : static char *
2346 : 7 : alternative_set_manual(struct alternative *a, const char *path)
2347 : : {
2348 : 7 : char *new_choice = NULL;
2349 : :
2350 [ + - ]: 7 : if (alternative_has_choice(a, path))
2351 : 7 : new_choice = xstrdup(path);
2352 : : else
2353 : 0 : error(_("alternative %s for %s not registered; "
2354 : : "not setting"), path, a->master_name);
2355 : 7 : alternative_set_status(a, ALT_ST_MANUAL);
2356 : :
2357 : 7 : return new_choice;
2358 : : }
2359 : :
2360 : : static char *
2361 : 1 : alternative_set_auto(struct alternative *a)
2362 : : {
2363 : 1 : char *new_choice = NULL;
2364 : :
2365 : 1 : alternative_set_status(a, ALT_ST_AUTO);
2366 [ - + ]: 1 : if (alternative_choices_count(a) == 0)
2367 : 0 : info(_("there is no program which provides %s"),
2368 : : a->master_name);
2369 : : else
2370 : 1 : new_choice = xstrdup(alternative_get_best(a)->master_file);
2371 : :
2372 : 1 : return new_choice;
2373 : : }
2374 : :
2375 : : static const char *
2376 : 53 : get_argv_string(int argc, char **argv)
2377 : : {
2378 : : static char string[2048];
2379 : : size_t cur_len;
2380 : : int i;
2381 : :
2382 : 53 : string[0] = '\0';
2383 : 53 : cur_len = 0;
2384 [ + + ]: 979 : for (i = 1; i < argc; i++) {
2385 : 926 : size_t arg_len = strlen(argv[i]);
2386 : :
2387 [ - + ]: 926 : if (cur_len + arg_len + 2 > sizeof(string))
2388 : 0 : break;
2389 [ + + ]: 926 : if (cur_len) {
2390 : 873 : strcpy(string + cur_len, " ");
2391 : 873 : cur_len++;
2392 : : }
2393 : 926 : strcpy(string + cur_len, argv[i]);
2394 : 926 : cur_len += arg_len;
2395 : : }
2396 : :
2397 : 53 : return string;
2398 : : }
2399 : :
2400 : : static void
2401 : 53 : alternative_select_mode(struct alternative *a, const char *current_choice)
2402 : : {
2403 [ + + ]: 53 : if (current_choice) {
2404 : : /* Detect manually modified alternative, switch to manual. */
2405 [ - + ]: 37 : if (!alternative_has_choice(a, current_choice)) {
2406 [ # # ]: 0 : if (fsys_pathname_is_missing(current_choice)) {
2407 : 0 : warning(_("%s%s/%s is dangling; it will be updated "
2408 : : "with best choice"), instdir, altdir,
2409 : : a->master_name);
2410 : 0 : alternative_set_status(a, ALT_ST_AUTO);
2411 [ # # ]: 0 : } else if (a->status != ALT_ST_MANUAL) {
2412 : 0 : warning(_("%s%s/%s has been changed (manually or by "
2413 : : "a script); switching to manual "
2414 : : "updates only"), instdir, altdir,
2415 : : a->master_name);
2416 : 0 : alternative_set_status(a, ALT_ST_MANUAL);
2417 : : }
2418 : : }
2419 : : } else {
2420 : : /* Lack of alternative link => automatic mode. */
2421 : 16 : verbose(_("setting up automatic selection of %s"),
2422 : : a->master_name);
2423 : 16 : alternative_set_status(a, ALT_ST_AUTO);
2424 : : }
2425 : 53 : }
2426 : :
2427 : : static void
2428 : 40 : alternative_evolve_slave(struct alternative *a, const char *cur_choice,
2429 : : struct slave_link *sl, struct fileset *fs)
2430 : : {
2431 : : struct slave_link *sl_old;
2432 : 40 : char *new_file = NULL;
2433 : : const char *old, *new;
2434 : :
2435 : 40 : sl_old = alternative_get_slave(a, sl->name);
2436 [ + + ]: 40 : if (sl_old == NULL) {
2437 : 9 : sl->updated = true;
2438 : 9 : return;
2439 : : }
2440 : :
2441 : 31 : old = sl_old->link;
2442 : 31 : new = sl->link;
2443 : :
2444 [ + + + + ]: 31 : if (cur_choice && strcmp(cur_choice, fs->master_file) == 0) {
2445 : 19 : new_file = xstrdup(fileset_get_slave(fs, sl->name));
2446 : : } else {
2447 : : char *lnk;
2448 : :
2449 : 12 : lnk = xasprintf("%s/%s", altdir, sl->name);
2450 : 12 : new_file = fsys_areadlink(lnk);
2451 : 12 : free(lnk);
2452 : : }
2453 [ + + + - ]: 35 : if (strcmp(old, new) != 0 &&
2454 : 4 : alternative_path_classify(old) == ALT_PATH_SYMLINK) {
2455 : 4 : bool rename_link = false;
2456 : :
2457 [ + + ]: 4 : if (new_file)
2458 : 3 : rename_link = !fsys_pathname_is_missing(new_file);
2459 : :
2460 [ + + ]: 4 : if (rename_link) {
2461 : 2 : info(_("renaming %s slave link from %s%s to %s%s"),
2462 : : sl->name, instdir, old, instdir, new);
2463 : 2 : fsys_mv(old, new);
2464 : : } else {
2465 : 2 : fsys_rm(old);
2466 : : }
2467 : :
2468 : 4 : sl->updated = true;
2469 : : }
2470 : 31 : free(new_file);
2471 : : }
2472 : :
2473 : : static void
2474 : 18 : alternative_evolve(struct alternative *a, struct alternative *b,
2475 : : const char *cur_choice, struct fileset *fs)
2476 : : {
2477 : : struct slave_link *sl;
2478 : : bool is_link;
2479 : :
2480 : 18 : is_link = alternative_path_classify(a->master_link) == ALT_PATH_SYMLINK;
2481 [ + + + + ]: 18 : if (is_link && strcmp(a->master_link, b->master_link) != 0) {
2482 : 2 : info(_("renaming %s link from %s%s to %s%s"), b->master_name,
2483 : : instdir, a->master_link, instdir, b->master_link);
2484 : 2 : fsys_mv(a->master_link, b->master_link);
2485 : : }
2486 : 18 : alternative_set_link(a, b->master_link);
2487 : :
2488 : : /* Check if new slaves have been added, or existing
2489 : : * ones renamed. */
2490 [ + + ]: 58 : for (sl = b->slaves; sl; sl = sl->next) {
2491 : 40 : alternative_evolve_slave(a, cur_choice, sl, fs);
2492 : 40 : alternative_copy_slave(a, sl);
2493 : : }
2494 : 18 : }
2495 : :
2496 : : static char *
2497 : 33 : alternative_install(struct alternative **aptr, struct alternative *inst_alt,
2498 : : const char *current_choice, struct fileset *fileset)
2499 : : {
2500 : 33 : struct alternative *a = *aptr;
2501 : 33 : char *new_choice = NULL;
2502 : :
2503 [ + + ]: 33 : if (a->master_link) {
2504 : : /* Alternative already exists, check if anything got
2505 : : * updated. */
2506 : 18 : alternative_evolve(a, inst_alt, current_choice, fileset);
2507 : 18 : alternative_free(inst_alt);
2508 : : } else {
2509 : : /* Alternative doesn't exist, create from parameters. */
2510 : 15 : alternative_free(a);
2511 : 15 : *aptr = a = inst_alt;
2512 : : }
2513 : 33 : alternative_add_choice(a, fileset);
2514 [ + + ]: 33 : if (a->status == ALT_ST_AUTO) {
2515 : 30 : new_choice = xstrdup(alternative_get_best(a)->master_file);
2516 : : } else {
2517 : 3 : verbose(_("automatic updates of %s/%s are disabled; "
2518 : : "leaving it alone"), altdir, a->master_name);
2519 : 3 : verbose(_("to return to automatic updates use "
2520 : : "'%s --auto %s'"), PROGNAME, a->master_name);
2521 : : }
2522 : 33 : return new_choice;
2523 : : }
2524 : :
2525 : : static void
2526 : 53 : alternative_update(struct alternative *a,
2527 : : const char *current_choice, const char *new_choice)
2528 : : {
2529 : : enum alternative_update_reason reason;
2530 : :
2531 : : /* No choice left, remove everything. */
2532 [ + + ]: 53 : if (!alternative_choices_count(a)) {
2533 : 5 : log_msg("link group %s fully removed", a->master_name);
2534 : 5 : alternative_remove_files(a);
2535 : 5 : return;
2536 : : }
2537 : :
2538 : : /* New choice wanted. */
2539 [ + + + + ]: 48 : if (new_choice &&
2540 [ + + ]: 29 : (!current_choice || strcmp(new_choice, current_choice) != 0)) {
2541 : 30 : log_msg("link group %s updated to point to %s", a->master_name,
2542 : : new_choice);
2543 [ + + ]: 30 : if (a->status == ALT_ST_AUTO)
2544 : 24 : info(_("using %s to provide %s (%s) in auto mode"),
2545 : : new_choice, a->master_link, a->master_name);
2546 : : else
2547 : 6 : info(_("using %s to provide %s (%s) in manual mode"),
2548 : : new_choice, a->master_link, a->master_name);
2549 : 30 : debug("prepare_install(%s)", new_choice);
2550 : 30 : alternative_prepare_install(a, new_choice);
2551 [ + + ]: 18 : } else if ((reason = alternative_needs_update(a))) {
2552 [ + + ]: 5 : if (reason == ALT_UPDATE_SLAVE_CHANGED) {
2553 : 1 : log_msg("link group %s updated with changed slaves",
2554 : : a->master_name);
2555 : 1 : info(_("updating alternative %s "
2556 : : "because link group %s has changed slave links"),
2557 : : current_choice, a->master_name);
2558 : : } else {
2559 : 4 : log_msg("auto-repair link group %s", a->master_name);
2560 : 4 : warning(_("forcing reinstallation of alternative %s "
2561 : : "because link group %s is broken"),
2562 : : current_choice, a->master_name);
2563 : : }
2564 : :
2565 [ + - - + ]: 5 : if (current_choice && !alternative_has_choice(a, current_choice)) {
2566 : 0 : struct fileset *best = alternative_get_best(a);
2567 : :
2568 : 0 : warning(_("current alternative %s is unknown, "
2569 : : "switching to %s for link group %s"),
2570 : : current_choice, best->master_file,
2571 : : a->master_name);
2572 : 0 : current_choice = best->master_file;
2573 : 0 : alternative_set_status(a, ALT_ST_AUTO);
2574 : : }
2575 : :
2576 [ + - ]: 5 : if (current_choice)
2577 : 5 : alternative_prepare_install(a, current_choice);
2578 : : }
2579 : :
2580 : : /* Save administrative file if needed. */
2581 [ + + ]: 46 : if (a->modified) {
2582 : 45 : debug("%s is modified and will be saved", a->master_name);
2583 : 45 : alternative_save(a);
2584 : : }
2585 : :
2586 : : /* Replace all symlinks in one pass. */
2587 : 46 : alternative_commit(a);
2588 : : }
2589 : :
2590 : : static void
2591 : 0 : alternative_config_all(void)
2592 : : {
2593 : : struct alternative_map *alt_map_obj;
2594 : : struct alternative_map *am;
2595 : :
2596 : 0 : alt_map_obj = alternative_map_new(NULL, NULL);
2597 : 0 : alternative_map_load_names(alt_map_obj);
2598 : :
2599 [ # # # # ]: 0 : for (am = alt_map_obj; am && am->item; am = am->next) {
2600 : : const char *current_choice;
2601 : : char *new_choice;
2602 : :
2603 : 0 : current_choice = alternative_get_current(am->item);
2604 : 0 : alternative_select_mode(am->item, current_choice);
2605 : :
2606 : 0 : new_choice = alternative_config(am->item, current_choice);
2607 : :
2608 : 0 : alternative_update(am->item, current_choice, new_choice);
2609 : :
2610 : 0 : free(new_choice);
2611 : : }
2612 : :
2613 : 0 : alternative_map_free(alt_map_obj);
2614 : 0 : }
2615 : :
2616 : : static void
2617 : 15 : alternative_get_selections(void)
2618 : : {
2619 : : struct alternative_map *alt_map_obj;
2620 : : struct alternative_map *am;
2621 : :
2622 : 15 : alt_map_obj = alternative_map_new(NULL, NULL);
2623 : 15 : alternative_map_load_names(alt_map_obj);
2624 : :
2625 [ + - - + ]: 15 : for (am = alt_map_obj; am && am->item; am = am->next) {
2626 : : const char *current;
2627 : :
2628 : 0 : current = alternative_get_current(am->item);
2629 [ # # ]: 0 : printf("%-30s %-8s %s\n", am->key,
2630 : 0 : alternative_status_string(am->item->status),
2631 : : current ? current : "");
2632 : : }
2633 : :
2634 : 15 : alternative_map_free(alt_map_obj);
2635 : 15 : }
2636 : :
2637 : : static void
2638 : 3 : alternative_set_selection(struct alternative_map *all, const char *name,
2639 : : const char *status, const char *choice)
2640 : : {
2641 : : struct alternative *a;
2642 : :
2643 : 3 : debug("set_selection(%s, %s, %s)", name, status, choice);
2644 : 3 : a = alternative_map_find(all, name);
2645 [ + + ]: 3 : if (a) {
2646 : 2 : char *new_choice = NULL;
2647 : :
2648 [ + + ]: 2 : if (strcmp(status, "auto") == 0) {
2649 : 1 : info(_("selecting alternative %s as auto"), name);
2650 : 1 : new_choice = alternative_set_auto(a);
2651 [ + - ]: 1 : } else if (alternative_has_choice(a, choice)) {
2652 : 1 : info(_("selecting alternative %s as choice %s"), name,
2653 : : choice);
2654 : 1 : new_choice = alternative_set_manual(a, choice);
2655 : : } else {
2656 : 0 : info(_("alternative %s unchanged because choice "
2657 : : "%s is not available"), name, choice);
2658 : : }
2659 : :
2660 [ + - ]: 2 : if (new_choice) {
2661 : : const char *current_choice;
2662 : :
2663 : 2 : current_choice = alternative_get_current(a);
2664 : 2 : alternative_select_mode(a, current_choice);
2665 : :
2666 : 2 : alternative_update(a, current_choice, new_choice);
2667 : :
2668 : 2 : free(new_choice);
2669 : : }
2670 : : } else {
2671 : 1 : info(_("skip unknown alternative %s"), name);
2672 : : }
2673 : 3 : }
2674 : :
2675 : : static void
2676 : 2 : alternative_set_selections(FILE *input, const char *desc)
2677 : : {
2678 : : struct alternative_map *alt_map_obj;
2679 : :
2680 : 2 : alt_map_obj = alternative_map_new(NULL, NULL);
2681 : 2 : alternative_map_load_names(alt_map_obj);
2682 : :
2683 : 3 : for (;;) {
2684 : : char line[1024], *res, *name, *status, *choice;
2685 : : size_t len, i;
2686 : :
2687 : 5 : errno = 0;
2688 : : /* Can't use scanf("%s %s %s") because choice can
2689 : : * contain a space */
2690 : 5 : res = fgets(line, sizeof(line), input);
2691 [ + + - + ]: 5 : if (res == NULL && errno) {
2692 : 0 : syserr(_("read error in %.250s"), desc);
2693 [ + + ]: 5 : } else if (res == NULL) {
2694 : 2 : break;
2695 : : }
2696 : 3 : len = strlen(line);
2697 [ + - - + ]: 3 : if (len == 0 || line[len - 1] != '\n') {
2698 : 0 : error(_("line too long or not terminated while "
2699 : : "trying to read %s"), desc);
2700 : : }
2701 : 3 : line[len - 1] = '\0';
2702 : 3 : len--;
2703 : :
2704 : : /* Delimit name string in line */
2705 : 3 : i = 0;
2706 : 3 : name = line;
2707 [ + - + + ]: 38 : while (i < len && !isblank(line[i]))
2708 : 35 : i++;
2709 [ - + ]: 3 : if (i >= len) {
2710 : 0 : info(_("skip invalid selection line: %s"), line);
2711 : 0 : continue;
2712 : : }
2713 : 3 : line[i++] = '\0';
2714 [ + - - + ]: 3 : while (i < len && isblank(line[i]))
2715 : 0 : i++;
2716 : :
2717 : : /* Delimit status string in line */
2718 : 3 : status = line + i;
2719 [ + - + + ]: 17 : while (i < len && !isblank(line[i]))
2720 : 14 : i++;
2721 [ - + ]: 3 : if (i >= len) {
2722 : 0 : info(_("skip invalid selection line: %s"), line);
2723 : 0 : continue;
2724 : : }
2725 : 3 : line[i++] = '\0';
2726 [ + - - + ]: 3 : while (i < len && isblank(line[i]))
2727 : 0 : i++;
2728 : :
2729 : : /* Delimit choice string in the line */
2730 [ - + ]: 3 : if (i >= len) {
2731 : 0 : info(_("skip invalid selection line: %s"), line);
2732 : 0 : continue;
2733 : : }
2734 : 3 : choice = line + i;
2735 : :
2736 : 3 : alternative_set_selection(alt_map_obj, name, status, choice);
2737 : : }
2738 : :
2739 : 2 : alternative_map_free(alt_map_obj);
2740 : 2 : }
2741 : :
2742 : : static void
2743 : 181 : alternative_check_name(const char *name)
2744 : : {
2745 [ + + ]: 181 : if (strpbrk(name, "/ \t"))
2746 : 2 : error(_("alternative name (%s) must not contain '/' "
2747 : : "and spaces"), name);
2748 : 179 : }
2749 : :
2750 : : static void
2751 : 134 : alternative_check_link(const char *linkname)
2752 : : {
2753 [ + + ]: 134 : if (linkname[0] != '/')
2754 : 1 : error(_("alternative link is not absolute as it should be: %s"),
2755 : : linkname);
2756 : 133 : }
2757 : :
2758 : : static void
2759 : 147 : alternative_check_path(const char *file)
2760 : : {
2761 [ + - + + ]: 147 : if (!file || file[0] != '/')
2762 : 1 : error(_("alternative path is not absolute as it should be: %s"),
2763 : : file);
2764 : 146 : }
2765 : :
2766 : : /**
2767 : : * Check the alternative installation arguments.
2768 : : *
2769 : : * That the caller doesn't mix links between alternatives, doesn't mix
2770 : : * alternatives between slave/master, and that the various parameters
2771 : : * are fine.
2772 : : */
2773 : : static void
2774 : 44 : alternative_check_install_args(struct alternative *inst_alt,
2775 : : struct fileset *fileset)
2776 : : {
2777 : : struct alternative_map *alt_map_links, *alt_map_parent;
2778 : : struct alternative *found;
2779 : : struct slave_link *sl;
2780 : :
2781 : 44 : alternative_check_name(inst_alt->master_name);
2782 : 43 : alternative_check_link(inst_alt->master_link);
2783 : 42 : alternative_check_path(fileset->master_file);
2784 : :
2785 : : /* Load information about all alternatives to check for mistakes. */
2786 : 41 : alt_map_links = alternative_map_new(NULL, NULL);
2787 : 41 : alt_map_parent = alternative_map_new(NULL, NULL);
2788 : 41 : alternative_map_load_tree(alt_map_links, alt_map_parent);
2789 : :
2790 : 41 : found = alternative_map_find(alt_map_parent, inst_alt->master_name);
2791 [ + + + + ]: 41 : if (found && strcmp(found->master_name, inst_alt->master_name) != 0) {
2792 : 1 : error(_("alternative %s can't be master: it is a slave of %s"),
2793 : : inst_alt->master_name, found->master_name);
2794 : : }
2795 : :
2796 : 40 : found = alternative_map_find(alt_map_links, inst_alt->master_link);
2797 [ + + + + ]: 40 : if (found && strcmp(found->master_name, inst_alt->master_name) != 0) {
2798 : 1 : found = alternative_map_find(alt_map_parent,
2799 : 1 : found->master_name);
2800 : 1 : error(_("alternative link %s is already managed by %s"),
2801 : : inst_alt->master_link, found->master_name);
2802 : : }
2803 : :
2804 [ + + ]: 39 : if (fsys_pathname_is_missing(fileset->master_file))
2805 : 1 : error(_("alternative path %s%s doesn't exist"),
2806 : : instdir, fileset->master_file);
2807 : :
2808 [ + + ]: 126 : for (sl = inst_alt->slaves; sl; sl = sl->next) {
2809 : 92 : const char *file = fileset_get_slave(fileset, sl->name);
2810 : :
2811 : 92 : alternative_check_name(sl->name);
2812 : 91 : alternative_check_link(sl->link);
2813 : 91 : alternative_check_path(file);
2814 : :
2815 : 91 : found = alternative_map_find(alt_map_parent, sl->name);
2816 [ + + ]: 91 : if (found &&
2817 [ + + ]: 32 : strcmp(found->master_name, inst_alt->master_name) != 0) {
2818 [ + - ]: 1 : if (strcmp(found->master_name, sl->name) == 0)
2819 : 1 : error(_("alternative %s can't be slave of %s: "
2820 : : "it is a master alternative"),
2821 : : sl->name, inst_alt->master_name);
2822 : : else
2823 : 0 : error(_("alternative %s can't be slave of %s: "
2824 : : "it is a slave of %s"),
2825 : : sl->name, inst_alt->master_name,
2826 : : found->master_name);
2827 : : }
2828 : :
2829 : 90 : found = alternative_map_find(alt_map_links, sl->link);
2830 [ + + ]: 90 : if (found &&
2831 [ + + ]: 29 : strcmp(found->master_name, inst_alt->master_name) != 0) {
2832 : 1 : error(_("alternative link %s is already "
2833 : : "managed by %s"), sl->link,
2834 : : found->master_name);
2835 : : }
2836 [ + + ]: 89 : if (found) {
2837 : : struct slave_link *sl2;
2838 : :
2839 [ + - ]: 56 : for (sl2 = found->slaves; sl2; sl2 = sl2->next)
2840 [ + + ]: 56 : if (strcmp(sl2->link, sl->link) == 0)
2841 : 28 : break;
2842 [ + - + + ]: 28 : if (sl2 && strcmp(sl2->name, sl->name) != 0)
2843 : 1 : error(_("alternative link %s is already "
2844 : : "managed by %s (slave of %s)"),
2845 : : sl->link, sl2->name,
2846 : : found->master_name);
2847 : : }
2848 : : }
2849 : :
2850 : 34 : alternative_map_free(alt_map_links);
2851 : 34 : alternative_map_free(alt_map_parent);
2852 : 34 : }
2853 : :
2854 : : /*
2855 : : * Main program
2856 : : */
2857 : :
2858 : : static void
2859 : 107 : set_action(enum action new_action)
2860 : : {
2861 [ - + ]: 107 : if (action)
2862 : 0 : badusage(_("two commands specified: --%s and --%s"),
2863 : : action_names[action].name, action_names[new_action].name);
2864 : 107 : action = new_action;
2865 : 107 : }
2866 : :
2867 : : static void
2868 : 62 : set_action_from_name(const char *new_action)
2869 : : {
2870 : : size_t i;
2871 : :
2872 [ + - ]: 526 : for (i = 0; i < array_count(action_names); i++) {
2873 [ + + ]: 526 : if (strcmp(new_action, action_names[i].name) == 0) {
2874 : 62 : set_action(action_names[i].action);
2875 : 62 : return;
2876 : : }
2877 : : }
2878 : :
2879 : 0 : assert(!"unknown action name");
2880 : : }
2881 : :
2882 : : static const char *
2883 : 9 : set_rootdir(const char *dir)
2884 : : {
2885 : 9 : instdir = fsys_set_dir(dir);
2886 : 9 : free(log_file);
2887 : 9 : log_file = fsys_get_path(LOGDIR "/alternatives.log");
2888 : 9 : altdir = SYSCONFDIR "/alternatives";
2889 : 9 : free(admdir);
2890 : 9 : admdir = fsys_gen_admindir();
2891 : :
2892 : 9 : return instdir;
2893 : : }
2894 : :
2895 : : static char *
2896 : 107 : admindir_init(void)
2897 : : {
2898 : : const char *basedir_env;
2899 : :
2900 : : /* Try to get the admindir from an environment variable, usually set
2901 : : * by the system package manager. */
2902 : 107 : basedir_env = getenv(ADMINDIR_ENVVAR);
2903 [ + + ]: 107 : if (basedir_env)
2904 : 10 : return xasprintf("%s%s", basedir_env, "/alternatives");
2905 : : else
2906 : 97 : return fsys_gen_admindir();
2907 : : }
2908 : :
2909 : : #define MISSING_ARGS(nb) (argc < i + nb + 1)
2910 : :
2911 : : int
2912 : 107 : main(int argc, char **argv)
2913 : : {
2914 : : /* Alternative worked on. */
2915 : 107 : struct alternative *a = NULL;
2916 : : /* Alternative to install. */
2917 : 107 : struct alternative *inst_alt = NULL;
2918 : : /* Set of files to install in the alternative. */
2919 : 107 : struct fileset *fileset = NULL;
2920 : : /* Path of alternative we are offering. */
2921 : 107 : const char *path = NULL;
2922 : 107 : const char *current_choice = NULL;
2923 : 107 : char *new_choice = NULL;
2924 : 107 : bool modifies_alt = false;
2925 : 107 : bool modifies_sys = false;
2926 : 107 : int i = 0;
2927 : :
2928 : 107 : setlocale(LC_ALL, "");
2929 : 107 : bindtextdomain(PACKAGE, LOCALEDIR);
2930 : 107 : textdomain(PACKAGE);
2931 : :
2932 : 107 : tzset();
2933 : 107 : umask(022);
2934 : :
2935 : 107 : instdir = fsys_set_dir(NULL);
2936 : 107 : admdir = admindir_init();
2937 : 107 : log_file = fsys_get_path(LOGDIR "/alternatives.log");
2938 : :
2939 [ - + ]: 107 : if (setvbuf(stdout, NULL, _IONBF, 0))
2940 : 0 : syserr("setvbuf failed");
2941 : :
2942 : 107 : prog_path = argv[0];
2943 : :
2944 [ + + ]: 725 : for (i = 1; i < argc; i++) {
2945 [ - + ]: 619 : if (strstr(argv[i], "--") != argv[i]) {
2946 : 0 : error(_("unknown argument '%s'"), argv[i]);
2947 [ - + ]: 619 : } else if (strcmp("--help", argv[i]) == 0) {
2948 : 0 : usage();
2949 : 0 : exit(0);
2950 [ - + ]: 619 : } else if (strcmp("--version", argv[i]) == 0) {
2951 : 0 : version();
2952 : 0 : exit(0);
2953 [ + + ]: 619 : } else if (strcmp("--quiet", argv[i]) == 0) {
2954 : 92 : opt_verbose = OUTPUT_QUIET;
2955 [ - + ]: 527 : } else if (strcmp("--verbose", argv[i]) == 0) {
2956 : 0 : opt_verbose = OUTPUT_VERBOSE;
2957 [ + + ]: 527 : } else if (strcmp("--debug", argv[i]) == 0) {
2958 : 15 : opt_verbose = OUTPUT_DEBUG;
2959 [ + + ]: 512 : } else if (strcmp("--install", argv[i]) == 0) {
2960 : : const char *alink, *aname, *apath;
2961 : : char *prio_str, *prio_end;
2962 : : long prio;
2963 : :
2964 : 45 : set_action(ACTION_INSTALL);
2965 [ - + ]: 45 : if (MISSING_ARGS(4))
2966 : 0 : badusage(_("--%s needs <link> <name> <path> "
2967 : 0 : "<priority>"), argv[i] + 2);
2968 : :
2969 : 45 : alink = argv[i + 1];
2970 : 45 : aname = argv[i + 2];
2971 : 45 : apath = argv[i + 3];
2972 : 45 : prio_str = argv[i + 4];
2973 : :
2974 [ - + ]: 45 : if (strcmp(alink, apath) == 0)
2975 : 0 : badusage(_("<link> '%s' is the same as <path>"),
2976 : : alink);
2977 : 45 : errno = 0;
2978 : 45 : prio = strtol(prio_str, &prio_end, 10);
2979 [ + - - + ]: 45 : if (prio_str == prio_end || *prio_end != '\0')
2980 : 0 : badusage(_("priority '%s' must be an integer"),
2981 : : prio_str);
2982 [ + - + - : 45 : if (prio < INT_MIN || prio > INT_MAX || errno == ERANGE)
- + ]
2983 : 0 : badusage(_("priority '%s' is out of range"),
2984 : : prio_str);
2985 : :
2986 : 45 : a = alternative_new(aname);
2987 : 45 : inst_alt = alternative_new(aname);
2988 : 45 : alternative_set_status(inst_alt, ALT_ST_AUTO);
2989 : 45 : alternative_set_link(inst_alt, alink);
2990 : 45 : fileset = fileset_new(apath, prio);
2991 : :
2992 : 45 : i += 4;
2993 [ + + ]: 467 : } else if (strcmp("--remove", argv[i]) == 0 ||
2994 [ + + ]: 459 : strcmp("--set", argv[i]) == 0) {
2995 : 14 : set_action_from_name(argv[i] + 2);
2996 [ - + ]: 14 : if (MISSING_ARGS(2))
2997 : 0 : badusage(_("--%s needs <name> <path>"), argv[i] + 2);
2998 : :
2999 : 14 : a = alternative_new(argv[i + 1]);
3000 : 14 : path = argv[i + 2];
3001 : :
3002 : 14 : alternative_check_name(a->master_name);
3003 : 14 : alternative_check_path(path);
3004 : :
3005 : 14 : i += 2;
3006 [ + - ]: 453 : } else if (strcmp("--display", argv[i]) == 0 ||
3007 [ + + ]: 453 : strcmp("--query", argv[i]) == 0 ||
3008 [ + - ]: 427 : strcmp("--auto", argv[i]) == 0 ||
3009 [ + + ]: 427 : strcmp("--config", argv[i]) == 0 ||
3010 [ + - ]: 424 : strcmp("--list", argv[i]) == 0 ||
3011 [ + + ]: 424 : strcmp("--remove-all", argv[i]) == 0) {
3012 : 31 : set_action_from_name(argv[i] + 2);
3013 [ - + ]: 31 : if (MISSING_ARGS(1))
3014 : 0 : badusage(_("--%s needs <name>"), argv[i] + 2);
3015 : 31 : a = alternative_new(argv[i + 1]);
3016 : :
3017 : 31 : alternative_check_name(a->master_name);
3018 : :
3019 : 31 : i++;
3020 [ + - ]: 422 : } else if (strcmp("--all", argv[i]) == 0 ||
3021 [ + + ]: 422 : strcmp("--get-selections", argv[i]) == 0 ||
3022 [ + + ]: 407 : strcmp("--set-selections", argv[i]) == 0) {
3023 : 17 : set_action_from_name(argv[i] + 2);
3024 [ + + ]: 405 : } else if (strcmp("--slave", argv[i]) == 0) {
3025 : : const char *slink, *sname, *spath;
3026 : : struct slave_link *sl;
3027 : :
3028 [ - + ]: 93 : if (action != ACTION_INSTALL)
3029 : 0 : badusage(_("--%s only allowed with --%s"),
3030 : 0 : argv[i] + 2, "install");
3031 [ - + ]: 93 : if (MISSING_ARGS(3))
3032 : 0 : badusage(_("--%s needs <link> <name> <path>"),
3033 : 0 : argv[i] + 2);
3034 : :
3035 : 93 : slink = argv[i + 1];
3036 : 93 : sname = argv[i + 2];
3037 : 93 : spath = argv[i + 3];
3038 : :
3039 [ - + ]: 93 : if (strcmp(slink, spath) == 0)
3040 : 0 : badusage(_("<link> '%s' is the same as <path>"),
3041 : : slink);
3042 [ - + ]: 93 : if (strcmp(inst_alt->master_name, sname) == 0)
3043 : 0 : badusage(_("<name> '%s' is both primary and slave"),
3044 : : sname);
3045 [ + + ]: 93 : if (strcmp(slink, inst_alt->master_link) == 0)
3046 : 1 : badusage(_("<link> '%s' is both primary and slave"),
3047 : : slink);
3048 [ - + ]: 92 : if (alternative_has_slave(inst_alt, sname))
3049 : 0 : badusage(_("duplicate slave <name> '%s'"), sname);
3050 : :
3051 [ + + ]: 203 : for (sl = inst_alt->slaves; sl; sl = sl->next) {
3052 : 111 : const char *linkname = sl->link;
3053 [ - + ]: 111 : if (linkname == NULL)
3054 : 0 : linkname = "";
3055 [ - + ]: 111 : if (strcmp(linkname, slink) == 0)
3056 : 0 : badusage(_("duplicate slave <link> '%s'"),
3057 : : slink);
3058 : : }
3059 : :
3060 : 92 : alternative_add_slave(inst_alt, sname, slink);
3061 : 92 : fileset_add_slave(fileset, sname, spath);
3062 : :
3063 : 92 : i+= 3;
3064 [ + + ]: 312 : } else if (strcmp("--log", argv[i]) == 0) {
3065 [ - + ]: 107 : if (MISSING_ARGS(1))
3066 : 0 : badusage(_("--%s needs a <file> argument"),
3067 : 0 : argv[i] + 2);
3068 : 107 : free(log_file);
3069 : 107 : log_file = fsys_get_path(argv[i + 1]);
3070 : 107 : i++;
3071 [ + + ]: 205 : } else if (strcmp("--altdir", argv[i]) == 0) {
3072 [ - + ]: 92 : if (MISSING_ARGS(1))
3073 : 0 : badusage(_("--%s needs a <directory> argument"),
3074 : 0 : argv[i] + 2);
3075 : 92 : altdir = argv[i + 1];
3076 : 92 : i++;
3077 : :
3078 : : /* If altdir is below instdir, convert it to a relative
3079 : : * path, as we will prepend instdir as needed. */
3080 [ + - ]: 92 : if (strncmp(altdir, instdir, instdir_len) == 0)
3081 : 92 : altdir += instdir_len;
3082 [ + + ]: 113 : } else if (strcmp("--admindir", argv[i]) == 0) {
3083 [ - + ]: 101 : if (MISSING_ARGS(1))
3084 : 0 : badusage(_("--%s needs a <directory> argument"),
3085 : 0 : argv[i] + 2);
3086 : 101 : free(admdir);
3087 : 101 : admdir = xstrdup(argv[i + 1]);
3088 : 101 : i++;
3089 [ - + ]: 12 : } else if (strcmp("--instdir", argv[i]) == 0) {
3090 [ # # ]: 0 : if (MISSING_ARGS(1))
3091 : 0 : badusage(_("--%s needs a <directory> argument"),
3092 : 0 : argv[i] + 2);
3093 : 0 : fsys_set_dir(argv[i + 1]);
3094 : 0 : i++;
3095 : :
3096 : : /* If altdir is below instdir, convert it to a relative
3097 : : * path, as we will prepend instdir as needed. */
3098 [ # # ]: 0 : if (strncmp(altdir, instdir, instdir_len) == 0)
3099 : 0 : altdir += instdir_len;
3100 [ + + ]: 12 : } else if (strcmp("--root", argv[i]) == 0) {
3101 [ - + ]: 9 : if (MISSING_ARGS(1))
3102 : 0 : badusage(_("--%s needs a <directory> argument"),
3103 : 0 : argv[i] + 2);
3104 : 9 : set_rootdir(argv[i + 1]);
3105 : 9 : i++;
3106 [ - + ]: 3 : } else if (strcmp("--skip-auto", argv[i]) == 0) {
3107 : 0 : opt_skip_auto = 1;
3108 [ + - ]: 3 : } else if (strcmp("--force", argv[i]) == 0) {
3109 : 3 : opt_force = 1;
3110 : : } else {
3111 : 0 : badusage(_("unknown option '%s'"), argv[i]);
3112 : : }
3113 : : }
3114 : :
3115 [ - + ]: 106 : if (action == ACTION_NONE)
3116 : 0 : badusage(_("need --%s, --%s, --%s, --%s, --%s, --%s, --%s, "
3117 : : "--%s, --%s, --%s, --%s or --%s"),
3118 : : "display", "query", "list", "get-selections",
3119 : : "config", "set", "set-selections", "install",
3120 : : "remove", "all", "remove-all", "auto");
3121 : :
3122 : 106 : debug("root=%s admdir=%s altdir=%s", instdir, admdir, altdir);
3123 : :
3124 : : /* The following actions might modify the current alternative. */
3125 [ + + ]: 106 : if (action == ACTION_SET ||
3126 [ + - ]: 100 : action == ACTION_AUTO ||
3127 [ + + ]: 100 : action == ACTION_CONFIG ||
3128 [ + + ]: 97 : action == ACTION_REMOVE ||
3129 [ + + ]: 89 : action == ACTION_REMOVE_ALL ||
3130 [ + + ]: 87 : action == ACTION_INSTALL)
3131 : 63 : modifies_alt = true;
3132 : :
3133 : : /* The following actions might modify the system somehow. */
3134 [ + + ]: 106 : if (modifies_alt ||
3135 [ + - ]: 43 : action == ACTION_CONFIG_ALL ||
3136 [ + + ]: 43 : action == ACTION_SET_SELECTIONS)
3137 : 65 : modifies_sys = true;
3138 : :
3139 [ + + ]: 106 : if (action == ACTION_INSTALL)
3140 : 44 : alternative_check_install_args(inst_alt, fileset);
3141 : :
3142 [ + - ]: 96 : if (action == ACTION_DISPLAY ||
3143 [ + + ]: 96 : action == ACTION_QUERY ||
3144 [ + - ]: 70 : action == ACTION_LIST ||
3145 [ + + ]: 70 : action == ACTION_SET ||
3146 [ + - ]: 64 : action == ACTION_AUTO ||
3147 [ + + ]: 64 : action == ACTION_CONFIG ||
3148 [ + + ]: 61 : action == ACTION_REMOVE_ALL) {
3149 : : /* Load the alternative info, stop on failure. */
3150 [ + + ]: 37 : if (!alternative_load(a, ALTDB_WARN_PARSER))
3151 : 3 : error(_("no alternatives for %s"), a->master_name);
3152 [ + + ]: 59 : } else if (action == ACTION_REMOVE) {
3153 : : /* XXX: Be consistent for now with the case when we
3154 : : * try to remove a non-existing path from an existing
3155 : : * link group file. */
3156 [ + + ]: 8 : if (!alternative_load(a, ALTDB_WARN_PARSER)) {
3157 : 1 : verbose(_("no alternatives for %s"), a->master_name);
3158 : 1 : alternative_free(a);
3159 : 1 : free(log_file);
3160 : 1 : free(admdir);
3161 : 1 : exit(0);
3162 : : }
3163 [ + + ]: 51 : } else if (action == ACTION_INSTALL) {
3164 : : /* Load the alternative info, ignore failures. */
3165 : 34 : alternative_load(a, ALTDB_WARN_PARSER);
3166 : : }
3167 : :
3168 [ + + ]: 91 : if (modifies_sys)
3169 : 53 : log_msg("run with %s", get_argv_string(argc, argv));
3170 : :
3171 [ + + ]: 91 : if (modifies_alt) {
3172 : 51 : current_choice = alternative_get_current(a);
3173 : 51 : alternative_select_mode(a, current_choice);
3174 : : }
3175 : :
3176 : : /* Handle actions. */
3177 [ - + ]: 91 : if (action == ACTION_CONFIG_ALL) {
3178 : 0 : alternative_config_all();
3179 [ + + ]: 91 : } else if (action == ACTION_GET_SELECTIONS) {
3180 : 15 : alternative_get_selections();
3181 [ + + ]: 76 : } else if (action == ACTION_SET_SELECTIONS) {
3182 : 2 : alternative_set_selections(stdin, _("<standard input>"));
3183 [ - + ]: 74 : } else if (action == ACTION_DISPLAY) {
3184 : 0 : alternative_display_user(a);
3185 [ + + ]: 74 : } else if (action == ACTION_QUERY) {
3186 : 23 : alternative_display_query(a);
3187 [ - + ]: 51 : } else if (action == ACTION_LIST) {
3188 : 0 : alternative_display_list(a);
3189 [ + + ]: 51 : } else if (action == ACTION_SET) {
3190 : 6 : new_choice = alternative_set_manual(a, path);
3191 [ - + ]: 45 : } else if (action == ACTION_AUTO) {
3192 : 0 : new_choice = alternative_set_auto(a);
3193 [ + + ]: 45 : } else if (action == ACTION_CONFIG) {
3194 : 3 : new_choice = alternative_config(a, current_choice);
3195 [ + + ]: 42 : } else if (action == ACTION_REMOVE) {
3196 : 7 : new_choice = alternative_remove(a, current_choice, path);
3197 [ + + ]: 35 : } else if (action == ACTION_REMOVE_ALL) {
3198 : 2 : alternative_choices_free(a);
3199 [ + - ]: 33 : } else if (action == ACTION_INSTALL) {
3200 : 33 : new_choice = alternative_install(&a, inst_alt, current_choice,
3201 : : fileset);
3202 : : }
3203 : :
3204 [ + + ]: 91 : if (modifies_alt)
3205 : 51 : alternative_update(a, current_choice, new_choice);
3206 : :
3207 [ + + ]: 89 : if (a)
3208 : 72 : alternative_free(a);
3209 : 89 : free(new_choice);
3210 : 89 : free(log_file);
3211 : 89 : free(admdir);
3212 : :
3213 : 89 : return 0;
3214 : : }
|