diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-07-06 10:10:02 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-07-06 10:10:02 +0000 |
commit | e3ecf7ac9cb7f9881937aa9d01ad2575b53f2713 (patch) | |
tree | 993ffe6b4fb4603fe81f949f27159c69062dcc89 /utils | |
parent | d1aa1653467440da1d8a011877732dac1d557058 (diff) | |
parent | ec0b42c0d5e4fc95a9a6bfb15606c34e3ad0e403 (diff) | |
download | dpkg-e3ecf7ac9cb7f9881937aa9d01ad2575b53f2713.tar.gz |
Merge git://git.debian.org/git/dpkg/dpkg
Conflicts:
debian/changelog
Diffstat (limited to 'utils')
-rw-r--r-- | utils/Makefile.am | 14 | ||||
-rw-r--r-- | utils/start-stop-daemon.c | 241 | ||||
-rw-r--r-- | utils/update-alternatives.c | 602 |
3 files changed, 491 insertions, 366 deletions
diff --git a/utils/Makefile.am b/utils/Makefile.am index b82362723..8a743d985 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -16,7 +16,7 @@ EXTRA_DIST = \ bin_PROGRAMS = -if WITH_UPDATE_ALTERNATIVES +if BUILD_UPDATE_ALTERNATIVES bin_PROGRAMS += update-alternatives endif @@ -30,7 +30,7 @@ update_alternatives_LDADD = \ sbin_PROGRAMS = -if WITH_START_STOP_DAEMON +if BUILD_START_STOP_DAEMON sbin_PROGRAMS += start-stop-daemon start_stop_daemon_SOURCES = \ @@ -41,7 +41,7 @@ start_stop_daemon_LDADD = \ $(SSD_LIBS) endif -if WITH_INSTALL_INFO +if BUILD_INSTALL_INFO sbin_PROGRAMS += dpkg-install-info # Automake has its own install-info rule, gah @@ -54,15 +54,15 @@ endif transform = s/dpkg-install-info/install-info/; $(program_transform_name) install-data-local: -if WITH_UPDATE_ALTERNATIVES - $(mkdir_p) $(DESTDIR)$(sysconfdir)/alternatives - $(mkdir_p) $(DESTDIR)$(admindir)/alternatives +if BUILD_UPDATE_ALTERNATIVES + $(MKDIR_P) $(DESTDIR)$(sysconfdir)/alternatives + $(MKDIR_P) $(DESTDIR)$(admindir)/alternatives $(INSTALL_DATA) $(srcdir)/README.alternatives $(DESTDIR)$(sysconfdir)/alternatives/README endif uninstall-local: rm -f $(DESTDIR)$(sysconfdir)/alternatives/README -if WITH_INSTALL_INFO +if BUILD_INSTALL_INFO rm -f $(DESTDIR)$(sbindir)/install-info endif diff --git a/utils/start-stop-daemon.c b/utils/start-stop-daemon.c index 120d5f976..2bb731fe0 100644 --- a/utils/start-stop-daemon.c +++ b/utils/start-stop-daemon.c @@ -159,6 +159,7 @@ static int testmode = 0; static int quietmode = 0; static int exitnodo = 1; static int background = 0; +static int close_io = 1; static int mpidfile = 0; static int signal_nr = SIGTERM; static int user_id = -1; @@ -301,6 +302,19 @@ tmul(struct timeval *a, int b) a->tv_usec %= 1000000; } +static char * +newpath(const char *dirname, const char *filename) +{ + char *path; + size_t path_len; + + path_len = strlen(dirname) + 1 + strlen(filename) + 1; + path = xmalloc(path_len); + snprintf(path, path_len, "%s/%s", dirname, filename); + + return path; +} + static long get_open_fd_max(void) { @@ -365,6 +379,27 @@ daemonize(void) } static void +write_pidfile(const char *filename, pid_t pid) +{ + FILE *fp; + int fd; + + fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666); + if (fd < 0) + fp = NULL; + else + fp = fdopen(fd, "w"); + + if (fp == NULL) + fatal("unable to open pidfile '%s' for writing", filename); + + fprintf(fp, "%d\n", pid); + + if (fclose(fp)) + fatal("unable to close pidfile '%s'", filename); +} + +static void pid_list_push(struct pid_list **list, pid_t pid) { struct pid_list *p; @@ -424,6 +459,7 @@ usage(void) " scheduler (default prio is 4)\n" " -k|--umask <mask> change the umask to <mask> before starting\n" " -b|--background force the process to detach\n" +" -C|--no-close do not close any file descriptor\n" " -m|--make-pidfile create the pidfile before starting\n" " -R|--retry <schedule> check whether processes die, and retry\n" " -t|--test test mode, don't do anything\n" @@ -504,19 +540,22 @@ static const struct sigpair siglist[] = { }; static int -parse_integer(const char *string, int *value_r) +parse_unsigned(const char *string, int base, int *value_r) { - unsigned long ul; - char *ep; + long value; + char *endptr; if (!string[0]) return -1; - ul = strtoul(string, &ep, 10); - if (string == ep || ul > INT_MAX || *ep != '\0') + errno = 0; + value = strtol(string, &endptr, base); + if (string == endptr || *endptr != '\0' || errno != 0) + return -1; + if (value < 0 || value > INT_MAX) return -1; - *value_r = ul; + *value_r = value; return 0; } @@ -525,7 +564,7 @@ parse_signal(const char *sig_str, int *sig_num) { unsigned int i; - if (parse_integer(sig_str, sig_num) == 0) + if (parse_unsigned(sig_str, 10, sig_num) == 0) return 0; for (i = 0; i < array_count(siglist); i++) { @@ -540,15 +579,7 @@ parse_signal(const char *sig_str, int *sig_num) static int parse_umask(const char *string, int *value_r) { - if (!string[0]) - return -1; - - errno = 0; - *value_r = strtoul(string, NULL, 0); - if (errno) - return -1; - else - return 0; + return parse_unsigned(string, 0, value_r); } static void @@ -577,7 +608,7 @@ parse_proc_schedule(const char *string) policy_str = strtok(policy_str, ":"); prio_str = strtok(NULL, ":"); - if (prio_str && parse_integer(prio_str, &prio) != 0) + if (prio_str && parse_unsigned(prio_str, 10, &prio) != 0) fatal("invalid process scheduler priority"); proc_sched = xmalloc(sizeof(*proc_sched)); @@ -608,7 +639,7 @@ parse_io_schedule(const char *string) class_str = strtok(class_str, ":"); prio_str = strtok(NULL, ":"); - if (prio_str && parse_integer(prio_str, &prio) != 0) + if (prio_str && parse_unsigned(prio_str, 10, &prio) != 0) fatal("invalid IO scheduler priority"); io_sched = xmalloc(sizeof(*io_sched)); @@ -671,11 +702,11 @@ parse_schedule_item(const char *string, struct schedule_item *item) { const char *after_hyph; - if (!strcmp(string, "forever")) { + if (strcmp(string, "forever") == 0) { item->type = sched_forever; } else if (isdigit(string[0])) { item->type = sched_timeout; - if (parse_integer(string, &item->value) != 0) + if (parse_unsigned(string, 10, &item->value) != 0) badusage("invalid timeout value in schedule"); } else if ((after_hyph = string + (string[0] == '-')) && parse_signal(after_hyph, &item->value) == 0) { @@ -787,6 +818,7 @@ parse_options(int argc, char * const *argv) { "iosched", 1, NULL, 'I'}, { "umask", 1, NULL, 'k'}, { "background", 0, NULL, 'b'}, + { "no-close", 0, NULL, 'C'}, { "make-pidfile", 0, NULL, 'm'}, { "retry", 1, NULL, 'R'}, { "chdir", 1, NULL, 'd'}, @@ -879,6 +911,9 @@ parse_options(int argc, char * const *argv) case 'b': /* --background */ background = 1; break; + case 'C': /* --no-close */ + close_io = 0; + break; case 'm': /* --make-pidfile */ mpidfile = 1; break; @@ -939,6 +974,9 @@ parse_options(int argc, char * const *argv) if (background && action != action_start) badusage("--background is only relevant with --start"); + + if (!close_io && !background) + badusage("--no-close is only relevant with --background"); } #if defined(OSHurd) @@ -962,7 +1000,7 @@ init_procset(void) } static struct proc_stat * -get_proc_stat (pid_t pid, ps_flags_t flags) +get_proc_stat(pid_t pid, ps_flags_t flags) { struct proc_stat *ps; ps_flags_t wanted_flags = PSTAT_PID | flags; @@ -1006,6 +1044,25 @@ pid_is_exec(pid_t pid, const struct stat *esb) return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); } +#elif defined(OSHurd) +static bool +pid_is_exec(pid_t pid, const struct stat *esb) +{ + struct proc_stat *ps; + struct stat sb; + const char *filename; + + ps = get_proc_stat(pid, PSTAT_ARGS); + if (ps == NULL) + return false; + + filename = proc_stat_args(ps); + + if (stat(filename, &sb) != 0) + return false; + + return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); +} #elif defined(OShpux) static bool pid_is_exec(pid_t pid, const struct stat *esb) @@ -1019,12 +1076,15 @@ pid_is_exec(pid_t pid, const struct stat *esb) } #elif defined(HAVE_KVM_H) static bool -pid_is_exec(pid_t pid, const char *name) +pid_is_exec(pid_t pid, const struct stat *esb) { kvm_t *kd; - int nentries; + int nentries, argv_len = 0; struct kinfo_proc *kp; - char errbuf[_POSIX2_LINE_MAX], *pidexec; + struct stat sb; + char errbuf[_POSIX2_LINE_MAX], buf[_POSIX2_LINE_MAX]; + char **pid_argv_p; + char *start_argv_0_p, *end_argv_0_p; kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); if (kd == NULL) @@ -1032,10 +1092,32 @@ pid_is_exec(pid_t pid, const char *name) kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries); if (kp == NULL) errx(1, "%s", kvm_geterr(kd)); - pidexec = (&kp->kp_proc)->p_comm; - if (strlen(name) != strlen(pidexec)) + pid_argv_p = kvm_getargv(kd, kp, argv_len); + if (pid_argv_p == NULL) + errx(1, "%s", kvm_geterr(kd)); + + /* Find and compare string. */ + start_argv_0_p = *pid_argv_p; + + /* Find end of argv[0] then copy and cut of str there. */ + end_argv_0_p = strchr(*pid_argv_p, ' '); + if (end_argv_0_p == NULL) + /* There seems to be no space, so we have the command + * already in its desired form. */ + start_argv_0_p = *pid_argv_p; + else { + /* Tests indicate that this never happens, since + * kvm_getargv itself cuts of tailing stuff. This is + * not what the manpage says, however. */ + strncpy(buf, *pid_argv_p, (end_argv_0_p - start_argv_0_p)); + buf[(end_argv_0_p - start_argv_0_p) + 1] = '\0'; + start_argv_0_p = buf; + } + + if (stat(start_argv_0_p, &sb) != 0) return false; - return (strcmp(name, pidexec) == 0) ? 1 : 0; + + return (sb.st_dev == esb->st_dev && sb.st_ino == esb->st_ino); } #endif @@ -1143,9 +1225,31 @@ static bool pid_is_cmd(pid_t pid, const char *name) { struct proc_stat *ps; + size_t argv0_len; + const char *argv0; + const char *binary_name; ps = get_proc_stat(pid, PSTAT_ARGS); - return ps && !strcmp(proc_stat_args(ps), name); + if (ps == NULL) + return false; + + argv0 = proc_stat_args(ps); + argv0_len = strlen(argv0) + 1; + + binary_name = basename(argv0); + if (strcmp(binary_name, name) == 0) + return true; + + /* XXX: This is all kinds of ugly, but on the Hurd there's no way to + * know the command name of a process, so we have to try to match + * also on argv[1] for the case of an interpreted script. */ + if (proc_stat_args_len(ps) > argv0_len) { + const char *script_name = basename(argv0 + argv0_len); + + return strcmp(script_name, name) == 0; + } + + return false; } #elif defined(OShpux) static bool @@ -1162,11 +1266,9 @@ static bool pid_is_cmd(pid_t pid, const char *name) { kvm_t *kd; - int nentries, argv_len = 0; + int nentries; struct kinfo_proc *kp; - char errbuf[_POSIX2_LINE_MAX], buf[_POSIX2_LINE_MAX]; - char **pid_argv_p; - char *start_argv_0_p, *end_argv_0_p; + char errbuf[_POSIX2_LINE_MAX], *process_name; kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf); if (kd == NULL) @@ -1174,31 +1276,10 @@ pid_is_cmd(pid_t pid, const char *name) kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &nentries); if (kp == NULL) errx(1, "%s", kvm_geterr(kd)); - pid_argv_p = kvm_getargv(kd, kp, argv_len); - if (pid_argv_p == NULL) - errx(1, "%s", kvm_geterr(kd)); - - /* Find and compare string. */ - start_argv_0_p = *pid_argv_p; - - /* Find end of argv[0] then copy and cut of str there. */ - end_argv_0_p = strchr(*pid_argv_p, ' '); - if (end_argv_0_p == NULL) - /* There seems to be no space, so we have the command - * already in its desired form. */ - start_argv_0_p = *pid_argv_p; - else { - /* Tests indicate that this never happens, since - * kvm_getargv itself cuts of tailing stuff. This is - * not what the manpage says, however. */ - strncpy(buf, *pid_argv_p, (end_argv_0_p - start_argv_0_p)); - buf[(end_argv_0_p - start_argv_0_p) + 1] = '\0'; - start_argv_0_p = buf; - } - - if (strlen(name) != strlen(start_argv_0_p)) + process_name = (&kp->kp_proc)->p_comm; + if (strlen(name) != strlen(process_name)) return false; - return (strcmp(name, start_argv_0_p) == 0) ? 1 : 0; + return (strcmp(name, process_name) == 0); } #endif @@ -1224,17 +1305,8 @@ pid_is_running(pid_t pid) static enum status_code pid_check(pid_t pid) { -#if defined(OSLinux) || defined(OShpux) if (execname && !pid_is_exec(pid, &exec_stat)) return status_dead; -#elif defined(HAVE_KVM_H) - if (execname && !pid_is_exec(pid, execname)) - return status_dead; -#elif defined(OSHurd) || defined(OSFreeBSD) || defined(OSNetBSD) - /* Let's try this to see if it works. */ - if (execname && !pid_is_cmd(pid, execname)) - return status_dead; -#endif if (userspec && !pid_is_user(pid, user_id)) return status_dead; if (cmdname && !pid_is_cmd(pid, cmdname)) @@ -1594,14 +1666,13 @@ main(int argc, char **argv) if (execname) { char *fullexecname; - if (changeroot) { - int fullexecname_len = strlen(changeroot) + 1 + - strlen(execname) + 1; + /* If it's a relative path, normalize it. */ + if (execname[0] != '/') + execname = newpath(changedir, execname); - fullexecname = xmalloc(fullexecname_len); - snprintf(fullexecname, fullexecname_len, "%s/%s", - changeroot, execname); - } else + if (changeroot) + fullexecname = newpath(changeroot, execname); + else fullexecname = execname; if (stat(fullexecname, &exec_stat)) @@ -1692,10 +1763,10 @@ main(int argc, char **argv) if (quietmode < 0) printf("Starting %s...\n", startas); *--argv = startas; - if (background) { + if (background) /* Ok, we need to detach this process. */ daemonize(); - + if (background && close_io) { devnull_fd = open("/dev/null", O_RDWR); if (devnull_fd < 0) fatal("unable to open '%s'", "/dev/null"); @@ -1711,17 +1782,9 @@ main(int argc, char **argv) set_io_schedule(io_sched); if (umask_value >= 0) umask(umask_value); - if (mpidfile && pidfile != NULL) { + if (mpidfile && pidfile != NULL) /* User wants _us_ to make the pidfile. */ - FILE *pidf = fopen(pidfile, "w"); - pid_t pidt = getpid(); - if (pidf == NULL) - fatal("unable to open pidfile '%s' for writing", - pidfile); - fprintf(pidf, "%d\n", pidt); - if (fclose(pidf)) - fatal("unable to close pidfile '%s'", pidfile); - } + write_pidfile(pidfile, getpid()); if (changeroot != NULL) { if (chdir(changeroot) < 0) fatal("unable to chdir() to %s", changeroot); @@ -1752,12 +1815,12 @@ main(int argc, char **argv) fatal("unable to set uid to %s", changeuser); } - if (background) { - int i; + /* Set a default umask for dumb programs. */ + if (background && umask_value < 0) + umask(022); - /* Set a default umask for dumb programs. */ - if (umask_value < 0) - umask(022); + if (background && close_io) { + int i; dup2(devnull_fd, 0); /* stdin */ dup2(devnull_fd, 1); /* stdout */ diff --git a/utils/update-alternatives.c b/utils/update-alternatives.c index 16fd8c66c..a4f38a332 100644 --- a/utils/update-alternatives.c +++ b/utils/update-alternatives.c @@ -3,7 +3,7 @@ * * Copyright © 1995 Ian Jackson <ian@davenant.greenend.org.uk> * Copyright © 2000-2002 Wichert Akkerman <wakkerma@debian.org> - * Copyright © 2006-2010 Guillem Jover <guillem@debian.org> + * Copyright © 2006-2012 Guillem Jover <guillem@debian.org> * Copyright © 2008 Pierre Habouzit <madcoder@debian.org> * Copyright © 2009-2010 Raphaël Hertzog <hertzog@debian.org> * @@ -81,11 +81,6 @@ version(void) printf("\n"); printf(_( -"Copyright (C) 1995 Ian Jackson.\n" -"Copyright (C) 2000-2002 Wichert Akkerman.\n" -"Copyright (C) 2009-2010 Raphael Hertzog.\n")); - - printf(_( "This is free software; see the GNU General Public License version 2 or\n" "later for copying conditions. There is NO warranty.\n")); } @@ -352,9 +347,10 @@ xasprintf(char **strp, const char *fmt, ...) static void set_action(const char *new_action) { - if (action) - badusage(_("two commands specified: --%s and --%s"), action, new_action); - action = new_action; + if (action) + badusage(_("two commands specified: --%s and --%s"), + action, new_action); + action = new_action; } static const char * @@ -393,7 +389,7 @@ log_msg(const char *fmt, ...) time(&now); strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", - localtime(&now)); + localtime(&now)); fprintf(fh_log, "%s %s: ", PROGNAME, timestamp); va_start(args, fmt); vfprintf(fh_log, fmt, args); @@ -403,38 +399,6 @@ log_msg(const char *fmt, ...) } static int -filter_altdir(const struct dirent *entry) -{ - if (strcmp(entry->d_name, ".") == 0 || - strcmp(entry->d_name, "..") == 0 || - (strlen(entry->d_name) > strlen(DPKG_TMP_EXT) && - strcmp(entry->d_name + strlen(entry->d_name) - - strlen(DPKG_TMP_EXT), DPKG_TMP_EXT) == 0)) - return 0; - return 1; -} - -static int -altdb_get_namelist(struct dirent ***table) -{ - int count; - - count = scandir(admdir, table, filter_altdir, alphasort); - if (count < 0) - syserr(_("cannot scan directory `%.255s'"), admdir); - - return count; -} - -static void -altdb_free_namelist(struct dirent **table, int n) -{ - while (n--) - free(table[n]); - free(table); -} - -static int spawn(const char *prog, const char *args[]) { const char **cmd; @@ -498,20 +462,6 @@ subcall(const char *prog, ...) exit(128); } -static void -config_all(void) -{ - struct dirent **table; - int i, count; - - count = altdb_get_namelist(&table); - for (i = 0; i < count; i++) { - subcall(prog_path, "--config", table[i]->d_name, NULL); - printf("\n"); - } - altdb_free_namelist(table, count); -} - static bool rename_mv(const char *src, const char *dst) { @@ -664,14 +614,13 @@ fileset_has_slave(struct fileset *fs, const char *name) if (file == NULL) return false; - return strlen(file) ? true : false; + return file[0] != '\0'; } static bool fileset_can_install_slave(struct fileset *fs, const char *slave_name) { struct stat st; - bool install_slave = false; /* Decide whether the slave alternative must be setup */ if (fileset_has_slave(fs, slave_name)) { @@ -680,10 +629,11 @@ fileset_can_install_slave(struct fileset *fs, const char *slave_name) errno = 0; if (stat(slave, &st) == -1 && errno != ENOENT) syserr(_("cannot stat file '%s'"), slave); - install_slave = (errno == 0) ? true : false; + if (errno == 0) + return true; } - return install_slave; + return false; } struct slave_link { @@ -952,13 +902,13 @@ alternative_get_slave(struct alternative *a, const char *name) static bool alternative_has_slave(struct alternative *a, const char *name) { - return alternative_get_slave(a, name) ? true : false; + return alternative_get_slave(a, name) != NULL; } static bool alternative_has_choice(struct alternative *a, const char *file) { - return alternative_get_fileset(a, file) ? true : false; + return alternative_get_fileset(a, file) != NULL; } static void @@ -1078,13 +1028,53 @@ alternative_remove_choice(struct alternative *a, const char *file) * Alternatives Database Load/Store functions. */ +enum altdb_flags { + altdb_lax_parser = 1 << 0, + altdb_warn_parser = 1 << 1, +}; + struct altdb_context { FILE *fh; char *filename; - void DPKG_ATTR_PRINTF(2) (*bad_format)(struct altdb_context *, const char *format, ...); + enum altdb_flags flags; + bool modified; + void DPKG_ATTR_PRINTF(2) (*bad_format)(struct altdb_context *, + const char *format, ...); jmp_buf on_error; }; +static int +altdb_filter_namelist(const struct dirent *entry) +{ + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0 || + (strlen(entry->d_name) > strlen(DPKG_TMP_EXT) && + strcmp(entry->d_name + strlen(entry->d_name) - + strlen(DPKG_TMP_EXT), DPKG_TMP_EXT) == 0)) + return 0; + return 1; +} + +static int +altdb_get_namelist(struct dirent ***table) +{ + int count; + + count = scandir(admdir, table, altdb_filter_namelist, alphasort); + if (count < 0) + syserr(_("cannot scan directory `%.255s'"), admdir); + + return count; +} + +static void +altdb_free_namelist(struct dirent **table, int n) +{ + while (n--) + free(table[n]); + free(table); +} + static char * altdb_get_line(struct altdb_context *ctx, const char *name) { @@ -1138,7 +1128,7 @@ altdb_parse_error(struct altdb_context *ctx, const char *format, ...) } static void DPKG_ATTR_NORET DPKG_ATTR_PRINTF(2) -altdb_interrupt_parsing(struct altdb_context *ctx, const char *format, ...) +altdb_parse_stop(struct altdb_context *ctx, const char *format, ...) { longjmp(ctx->on_error, 1); } @@ -1178,7 +1168,7 @@ alternative_parse_slave(struct alternative *a, struct altdb_context *ctx) ctx->bad_format(ctx, _("slave link same as main link %s"), a->master_link); } - for(sl = a->slaves; sl; sl = sl->next) { + for (sl = a->slaves; sl; sl = sl->next) { if (strcmp(linkname, sl->link) == 0) { free(linkname); free(name); @@ -1193,8 +1183,7 @@ alternative_parse_slave(struct alternative *a, struct altdb_context *ctx) } static bool -alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx, - bool *modified, bool must_not_die) +alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx) { struct fileset *fs; struct slave_link *sl; @@ -1222,28 +1211,33 @@ alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx, syserr(_("cannot stat file '%s'"), master_file); /* File not found - remove. */ - if (!must_not_die) + if (ctx->flags & altdb_warn_parser) warning(_("alternative %s (part of link group %s) " - "doesn't exist. Removing from list of " - "alternatives."), master_file, a->master_name); + "doesn't exist; removing from list of " + "alternatives"), master_file, a->master_name); junk = altdb_get_line(ctx, _("priority")); free(junk); for (sl = a->slaves; sl; sl = sl->next) { junk = altdb_get_line(ctx, _("slave file")); free(junk); } - *modified = true; + ctx->modified = true; } else { - char *endptr, *prio; - long int iprio; + char *prio_str, *prio_end; + long prio; - prio = altdb_get_line(ctx, _("priority")); - iprio = strtol(prio, &endptr, 10); - /* XXX: Leak master_file/prio on non-fatal error */ - if (*endptr != '\0') + prio_str = altdb_get_line(ctx, _("priority")); + errno = 0; + prio = strtol(prio_str, &prio_end, 10); + /* XXX: Leak master_file/prio_str on non-fatal error */ + if (prio_str == prio_end || *prio_end != '\0') ctx->bad_format(ctx, _("priority of %s: %s"), - master_file, prio); - fs = fileset_new(master_file, (int) iprio); + master_file, prio_str); + if (prio < INT_MIN || prio > INT_MAX || errno == ERANGE) + ctx->bad_format(ctx, + _("priority of %s is out of range: %s"), + master_file, prio_str); + fs = fileset_new(master_file, prio); for (sl = a->slaves; sl; sl = sl->next) { fileset_add_slave(fs, xstrdup(sl->name), altdb_get_line(ctx, _("slave file"))); @@ -1254,12 +1248,11 @@ alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx, } static bool -alternative_load(struct alternative *a, bool must_not_die) +alternative_load(struct alternative *a, enum altdb_flags flags) { struct altdb_context ctx; struct stat st; char *fn, *status; - bool modified = false; /* Initialize parse context */ if (setjmp(ctx.on_error)) { @@ -1269,8 +1262,10 @@ alternative_load(struct alternative *a, bool must_not_die) alternative_reset(a); return false; } - if (must_not_die) - ctx.bad_format = altdb_interrupt_parsing; + ctx.modified = false; + ctx.flags = flags; + if (flags & altdb_lax_parser) + ctx.bad_format = altdb_parse_stop; else ctx.bad_format = altdb_parse_error; xasprintf(&fn, "%s/%s", admdir, a->master_name); @@ -1307,7 +1302,7 @@ alternative_load(struct alternative *a, bool must_not_die) while (alternative_parse_slave(a, &ctx)); /* Parse the available choices in the alternative */ - while (alternative_parse_fileset(a, &ctx, &modified, must_not_die)); + while (alternative_parse_fileset(a, &ctx)) ; /* Close database file */ if (fclose(ctx.fh)) @@ -1317,7 +1312,7 @@ alternative_load(struct alternative *a, bool must_not_die) /* Initialize the modified field which has been erroneously changed * by the various alternative_(add|set)_* calls: * false unless a choice has been auto-cleaned */ - a->modified = modified; + a->modified = ctx.modified; return true; } @@ -1345,7 +1340,7 @@ alternative_save(struct alternative *a) if (!has_slave) { struct slave_link *sl_rm; - verbose(_("discarding obsolete slave link %s (%s)."), + verbose(_("discarding obsolete slave link %s (%s)"), sl->name, sl->link); if (sl_prev) sl_prev->next = sl->next; @@ -1426,34 +1421,22 @@ alternative_get_best(struct alternative *a) return best; } -static bool -alternative_has_current_link(struct alternative *a) +static char * +alternative_get_current(struct alternative *a) { struct stat st; char *curlink; + char *file; xasprintf(&curlink, "%s/%s", altdir, a->master_name); if (lstat(curlink, &st)) { if (errno == ENOENT) { free(curlink); - return false; + return NULL; } syserr(_("cannot stat file '%s'"), curlink); - } else { - free(curlink); - return true; } -} - -static char * -alternative_get_current(struct alternative *a) -{ - char *curlink, *file; - - if (!alternative_has_current_link(a)) - return NULL; - xasprintf(&curlink, "%s/%s", altdir, a->master_name); file = xreadlink(curlink); free(curlink); @@ -1467,7 +1450,13 @@ alternative_display_query(struct alternative *a) struct slave_link *sl; char *current; - pr("Link: %s", a->master_name); + pr("Name: %s", a->master_name); + pr("Link: %s", a->master_link); + if (alternative_slaves_count(a) > 0) { + pr("Slaves:"); + for (sl = a->slaves; sl; sl = sl->next) + pr(" %s %s", sl->name, sl->link); + } pr("Status: %s", alternative_status_string(a->status)); best = alternative_get_best(a); if (best) @@ -1589,7 +1578,10 @@ alternative_select_choice(struct alternative *a) selection[strlen(selection) - 1] = '\0'; if (strlen(selection) == 0) return current; + errno = 0; idx = strtol(selection, &ret, 10); + if (idx < 0 || errno != 0) + continue; if (*ret == '\0') { /* Look up by index */ if (idx == 0) { @@ -1616,13 +1608,25 @@ alternative_select_choice(struct alternative *a) } } } - free(current); - return NULL; +} + +static void +alternative_config_all(void) +{ + struct dirent **table; + int i, count; + + count = altdb_get_namelist(&table); + for (i = 0; i < count; i++) { + subcall(prog_path, "--config", table[i]->d_name, NULL); + printf("\n"); + } + altdb_free_namelist(table, count); } static void alternative_add_commit_op(struct alternative *a, enum opcode opcode, - const char *arg_a, const char *arg_b) + const char *arg_a, const char *arg_b) { struct commit_operation *op, *cur; @@ -1687,7 +1691,7 @@ alternative_path_classify(const char *linkname) } static bool -alternative_can_replace_path(const char *linkname) +alternative_path_can_remove(const char *linkname) { if (opt_force) return true; @@ -1718,7 +1722,7 @@ alternative_path_needs_update(const char *linkname, const char *filename) return update; case ALT_PATH_OTHER: - warning(_("not replacing %s with a link."), linkname); + warning(_("not replacing %s with a link"), linkname); return false; case ALT_PATH_MISSING: default: @@ -1728,7 +1732,7 @@ alternative_path_needs_update(const char *linkname, const char *filename) static void alternative_prepare_install_single(struct alternative *a, const char *name, - const char *linkname, const char *file) + const char *linkname, const char *file) { char *fntmp, *fn; @@ -1778,16 +1782,16 @@ alternative_prepare_install(struct alternative *a, const char *choice) /* Slave can't be installed */ if (fileset_has_slave(fs, sl->name)) warning(_("skip creation of %s because associated " - "file %s (of link group %s) doesn't exist."), + "file %s (of link group %s) doesn't exist"), sl->link, fileset_get_slave(fs, sl->name), a->master_name); /* Drop unused slave. */ xasprintf(&fn, "%s/%s", altdir, sl->name); - if (alternative_can_replace_path(sl->link)) + if (alternative_path_can_remove(sl->link)) alternative_add_commit_op(a, opcode_rm, sl->link, NULL); else - warning(_("not removing %s since it's not a symlink."), + warning(_("not removing %s since it's not a symlink"), sl->link); alternative_add_commit_op(a, opcode_rm, fn, NULL); free(fn); @@ -1800,7 +1804,7 @@ alternative_remove(struct alternative *a) struct slave_link *sl; checked_rm_args("%s" DPKG_TMP_EXT, a->master_link); - if (alternative_can_replace_path(a->master_link)) + if (alternative_path_can_remove(a->master_link)) checked_rm(a->master_link); checked_rm_args("%s/%s" DPKG_TMP_EXT, altdir, a->master_name); @@ -1808,7 +1812,7 @@ alternative_remove(struct alternative *a) for (sl = a->slaves; sl; sl = sl->next) { checked_rm_args("%s" DPKG_TMP_EXT, sl->link); - if (alternative_can_replace_path(sl->link)) + if (alternative_path_can_remove(sl->link)) checked_rm(sl->link); checked_rm_args("%s/%s" DPKG_TMP_EXT, altdir, sl->name); @@ -1825,9 +1829,6 @@ alternative_is_broken(struct alternative *a) struct fileset *fs; struct slave_link *sl; - if (!alternative_has_current_link(a)) - return true; - /* Check master link */ altlnk = areadlink(a->master_link); if (!altlnk) @@ -1843,6 +1844,9 @@ alternative_is_broken(struct alternative *a) /* Stop if we have an unmanaged alternative */ current = alternative_get_current(a); + if (current == NULL) + return true; + if (!alternative_has_choice(a, current)) { free(current); return false; @@ -1925,7 +1929,8 @@ alternative_map_find(struct alternative_map *am, const char *key) } static void -alternative_map_add(struct alternative_map *am, const char *key, struct alternative *a) +alternative_map_add(struct alternative_map *am, const char *key, + struct alternative *a) { if (am->key == NULL) { am->key = key; @@ -1933,7 +1938,7 @@ alternative_map_add(struct alternative_map *am, const char *key, struct alternat } else { struct alternative_map *new = alternative_map_new(key, a); - while(am->next) + while (am->next) am = am->next; am->next = new; } @@ -1949,7 +1954,7 @@ alternative_map_load_names(struct alternative_map *alt_map_obj) for (i = 0; i < count; i++) { struct alternative *a_new = alternative_new(table[i]->d_name); - if (!alternative_load(a_new, true)) { + if (!alternative_load(a_new, altdb_lax_parser)) { alternative_free(a_new); continue; } @@ -1970,7 +1975,7 @@ alternative_map_load_tree(struct alternative_map *alt_map_links, struct slave_link *sl; struct alternative *a_new = alternative_new(table[i]->d_name); - if (!alternative_load(a_new, true)) { + if (!alternative_load(a_new, altdb_lax_parser)) { alternative_free(a_new); continue; } @@ -2027,13 +2032,36 @@ get_argv_string(int argc, char **argv) } static void +alternative_get_selections(void) +{ + struct alternative_map *alt_map_obj; + struct alternative_map *am; + + alt_map_obj = alternative_map_new(NULL, NULL); + alternative_map_load_names(alt_map_obj); + + for (am = alt_map_obj; am && am->item; am = am->next) { + char *current; + + current = alternative_get_current(am->item); + printf("%-30s %-8s %s\n", am->key, + alternative_status_string(am->item->status), + current ? current : ""); + free(current); + } + + alternative_map_free(alt_map_obj); +} + +static void alternative_set_selection(struct alternative_map *all, const char *name, const char *status, const char *choice) { struct alternative *a; debug("set_selection(%s, %s, %s)", name, status, choice); - if ((a = alternative_map_find(all, name))) { + a = alternative_map_find(all, name); + if (a) { char *cmd; if (strcmp(status, "auto") == 0) { @@ -2057,8 +2085,13 @@ alternative_set_selection(struct alternative_map *all, const char *name, } static void -alternative_set_selections(struct alternative_map *all, FILE* input, const char *desc) +alternative_set_selections(FILE *input, const char *desc) { + struct alternative_map *alt_map_obj; + + alt_map_obj = alternative_map_new(NULL, NULL); + alternative_map_load_names(alt_map_obj); + for (;;) { char line[1024], *res, *name, *status, *choice; size_t len, i; @@ -2116,7 +2149,40 @@ alternative_set_selections(struct alternative_map *all, FILE* input, const char choice = line + i; printf("[%s %s] ", PROGNAME, "--set-selections"); - alternative_set_selection(all, name, status, choice); + alternative_set_selection(alt_map_obj, name, status, choice); + } + + alternative_map_free(alt_map_obj); +} + +static void +alternative_select_mode(struct alternative *a, const char *current_choice) +{ + if (current_choice) { + /* Detect manually modified alternative, switch to manual. */ + if (!alternative_has_choice(a, current_choice)) { + struct stat st; + + errno = 0; + if (stat(current_choice, &st) == -1 && errno != ENOENT) + syserr(_("cannot stat file '%s'"), current_choice); + + if (errno == ENOENT) { + warning(_("%s/%s is dangling; it will be updated " + "with best choice"), altdir, a->master_name); + alternative_set_status(a, ALT_ST_AUTO); + } else if (a->status != ALT_ST_MANUAL) { + warning(_("%s/%s has been changed (manually or by " + "a script); switching to manual " + "updates only"), altdir, a->master_name); + alternative_set_status(a, ALT_ST_MANUAL); + } + } + } else { + /* Lack of alternative link => automatic mode. */ + verbose(_("setting up automatic selection of %s"), + a->master_name); + alternative_set_status(a, ALT_ST_AUTO); } } @@ -2126,10 +2192,11 @@ alternative_evolve(struct alternative *a, struct alternative *b, { struct slave_link *sl; struct stat st; + bool is_link; - bool is_link = (alternative_path_classify(a->master_link) == ALT_PATH_SYMLINK); + is_link = alternative_path_classify(a->master_link) == ALT_PATH_SYMLINK; if (is_link && strcmp(a->master_link, b->master_link) != 0) { - info(_("renaming %s link from %s to %s."), b->master_name, + info(_("renaming %s link from %s to %s"), b->master_name, a->master_link, b->master_link); checked_mv(a->master_link, b->master_link); } @@ -2170,7 +2237,7 @@ alternative_evolve(struct alternative *a, struct alternative *b, } if (rename_link) { - info(_("renaming %s slave link from %s to %s."), + info(_("renaming %s slave link from %s to %s"), sl->name, old, new); checked_mv(old, new); } else { @@ -2183,16 +2250,77 @@ alternative_evolve(struct alternative *a, struct alternative *b, } static void -alternative_check_args(const char *name, const char *linkname, const char *file) +alternative_update(struct alternative *a, + char *current_choice, const char *new_choice) +{ + /* No choice left, remove everything. */ + if (!alternative_choices_count(a)) { + log_msg("link group %s fully removed", a->master_name); + alternative_remove(a); + return; + } + + /* New choice wanted. */ + if (new_choice && + (!current_choice || strcmp(new_choice, current_choice) != 0)) { + log_msg("link group %s updated to point to %s", a->master_name, + new_choice); + info(_("using %s to provide %s (%s) in %s"), new_choice, + a->master_link, a->master_name, + (a->status == ALT_ST_AUTO) ? _("auto mode") : + _("manual mode")); + debug("prepare_install(%s)", new_choice); + alternative_prepare_install(a, new_choice); + } else if (alternative_is_broken(a)) { + log_msg("auto-repair link group %s", a->master_name); + warning(_("forcing reinstallation of alternative %s because " + "link group %s is broken"), current_choice, + a->master_name); + + if (current_choice && !alternative_has_choice(a, current_choice)) { + struct fileset *best = alternative_get_best(a); + + warning(_("current alternative %s is unknown, " + "switching to %s for link group %s"), + current_choice, best->master_file, + a->master_name); + current_choice = best->master_file; + alternative_set_status(a, ALT_ST_AUTO); + } + + if (current_choice) + alternative_prepare_install(a, current_choice); + } + + /* Save administrative file if needed. */ + if (a->modified) { + debug("%s is modified and will be saved", a->master_name); + alternative_save(a); + } + + /* Replace all symlinks in one pass. */ + alternative_commit(a); +} + +static void +alternative_check_name(const char *name) { if (strpbrk(name, "/ \t")) error(_("alternative name (%s) must not contain '/' " - "and spaces."), name); + "and spaces"), name); +} +static void +alternative_check_link(const char *linkname) +{ if (linkname[0] != '/') error(_("alternative link is not absolute as it should be: %s"), linkname); +} +static void +alternative_check_path(const char *file) +{ if (!file || file[0] != '/') error(_("alternative path is not absolute as it should be: %s"), file); @@ -2214,6 +2342,10 @@ alternative_check_install_args(struct alternative *inst_alt, struct slave_link *sl; struct stat st; + alternative_check_name(inst_alt->master_name); + alternative_check_link(inst_alt->master_link); + alternative_check_path(fileset->master_file); + /* Load information about all alternatives to check for mistakes. */ alt_map_links = alternative_map_new(NULL, NULL); alt_map_parent = alternative_map_new(NULL, NULL); @@ -2229,16 +2361,13 @@ alternative_check_install_args(struct alternative *inst_alt, if (found && strcmp(found->master_name, inst_alt->master_name) != 0) { found = alternative_map_find(alt_map_parent, found->master_name); - error(_("alternative link %s is already managed by %s."), + error(_("alternative link %s is already managed by %s"), inst_alt->master_link, found->master_name); } - alternative_check_args(inst_alt->master_name, inst_alt->master_link, - fileset->master_file); - if (stat(fileset->master_file, &st) == -1) { if (errno == ENOENT) - error(_("alternative path %s doesn't exist."), + error(_("alternative path %s doesn't exist"), fileset->master_file); else syserr(_("cannot stat file '%s'"), fileset->master_file); @@ -2247,12 +2376,16 @@ alternative_check_install_args(struct alternative *inst_alt, for (sl = inst_alt->slaves; sl; sl = sl->next) { const char *file = fileset_get_slave(fileset, sl->name); + alternative_check_name(sl->name); + alternative_check_link(sl->link); + alternative_check_path(file); + found = alternative_map_find(alt_map_parent, sl->name); if (found && strcmp(found->master_name, inst_alt->master_name) != 0) { if (strcmp(found->master_name, sl->name) == 0) error(_("alternative %s can't be slave of %s: " - "it is a master alternative."), + "it is a master alternative"), sl->name, inst_alt->master_name); else error(_("alternative %s can't be slave of %s: " @@ -2265,7 +2398,7 @@ alternative_check_install_args(struct alternative *inst_alt, if (found && strcmp(found->master_name, inst_alt->master_name) != 0) { error(_("alternative link %s is already " - "managed by %s."), sl->link, + "managed by %s"), sl->link, found->master_name); } if (found) { @@ -2276,12 +2409,10 @@ alternative_check_install_args(struct alternative *inst_alt, break; if (sl2 && strcmp(sl2->name, sl->name) != 0) error(_("alternative link %s is already " - "managed by %s (slave of %s)."), + "managed by %s (slave of %s)"), sl->link, sl2->name, found->master_name); } - - alternative_check_args(sl->name, sl->link, file); } alternative_map_free(alt_map_links); @@ -2335,24 +2466,35 @@ main(int argc, char **argv) opt_verbose--; PUSH_OPT(argv[i]); } else if (strcmp("--install", argv[i]) == 0) { - long priority; - char *endptr; + char *prio_str, *prio_end; + long prio; set_action("install"); if (MISSING_ARGS(4)) badusage(_("--install needs <link> <name> " "<path> <priority>")); + + prio_str = argv[i + 4]; + if (strcmp(argv[i+1], argv[i+3]) == 0) badusage(_("<link> and <path> can't be the same")); - priority = strtol(argv[i+4], &endptr, 10); - if (*endptr != '\0') + errno = 0; + prio = strtol(prio_str, &prio_end, 10); + if (prio_str == prio_end || *prio_end != '\0') badusage(_("priority must be an integer")); + if (prio < INT_MIN || prio > INT_MAX || errno == ERANGE) { + /* XXX: Switch back to error on 1.17.x. */ + prio = clamp(prio, INT_MIN, INT_MAX); + warning(_("priority is out of range: " + "%s clamped to %ld"), + prio_str, prio); + } a = alternative_new(argv[i + 2]); inst_alt = alternative_new(argv[i + 2]); alternative_set_status(inst_alt, ALT_ST_AUTO); alternative_set_link(inst_alt, xstrdup(argv[i + 1])); - fileset = fileset_new(argv[i + 3], priority); + fileset = fileset_new(argv[i + 3], prio); i += 4; } else if (strcmp("--remove", argv[i]) == 0 || @@ -2364,6 +2506,9 @@ main(int argc, char **argv) a = alternative_new(argv[i + 1]); path = xstrdup(argv[i + 2]); + alternative_check_name(a->master_name); + alternative_check_path(path); + i += 2; } else if (strcmp("--display", argv[i]) == 0 || strcmp("--query", argv[i]) == 0 || @@ -2375,6 +2520,9 @@ main(int argc, char **argv) if (MISSING_ARGS(1)) badusage(_("--%s needs <name>"), argv[i] + 2); a = alternative_new(argv[i + 1]); + + alternative_check_name(a->master_name); + i++; } else if (strcmp("--all", argv[i]) == 0 || strcmp("--get-selections", argv[i]) == 0 || @@ -2457,53 +2605,41 @@ main(int argc, char **argv) if (strcmp(action, "install") == 0) alternative_check_install_args(inst_alt, fileset); + if (strcmp(action, "display") == 0 || + strcmp(action, "query") == 0 || + strcmp(action, "list") == 0 || + strcmp(action, "set") == 0 || + strcmp(action, "auto") == 0 || + strcmp(action, "config") == 0 || + strcmp(action, "remove-all") == 0) { + /* Load the alternative info, stop on failure. */ + if (!alternative_load(a, altdb_warn_parser)) + error(_("no alternatives for %s"), a->master_name); + } else if (strcmp(action, "remove") == 0) { + /* FIXME: Be consistent for now with the case when we + * try to remove a non-existing path from an existing + * link group file. */ + if (!alternative_load(a, altdb_warn_parser)) { + verbose(_("no alternatives for %s"), a->master_name); + exit(0); + } + } else if (strcmp(action, "install") == 0) { + /* Load the alternative info, ignore failures. */ + alternative_load(a, altdb_warn_parser); + } + /* Handle actions. */ if (strcmp(action, "all") == 0) { - config_all(); + alternative_config_all(); exit(0); } else if (strcmp(action, "get-selections") == 0) { - struct alternative_map *alt_map_obj; - struct alternative_map *am; - - alt_map_obj = alternative_map_new(NULL, NULL); - alternative_map_load_names(alt_map_obj); - - for (am = alt_map_obj; am && am->item; am = am->next) { - char *current; - - current = alternative_get_current(am->item); - printf("%-30s %-8s %s\n", am->key, - alternative_status_string(am->item->status), - current ? current : ""); - free(current); - } - - alternative_map_free(alt_map_obj); - + alternative_get_selections(); exit(0); } else if (strcmp(action, "set-selections") == 0) { - struct alternative_map *alt_map_obj; - log_msg("run with %s", get_argv_string(argc, argv)); - alt_map_obj = alternative_map_new(NULL, NULL); - alternative_map_load_names(alt_map_obj); - alternative_set_selections(alt_map_obj, stdin, _("<standard input>")); - alternative_map_free(alt_map_obj); + alternative_set_selections(stdin, _("<standard input>")); exit(0); - } - - /* Load the alternative info, stop on failure except for --install. */ - if (!alternative_load(a, false) && strcmp(action, "install") != 0) { - /* FIXME: Be consistent for now with the case when we try to remove a - * non-existing path from an existing link group file. */ - if (strcmp(action, "remove") == 0) { - verbose(_("no alternatives for %s."), a->master_name); - exit(0); - } - error(_("no alternatives for %s."), a->master_name); - } - - if (strcmp(action, "display") == 0) { + } else if (strcmp(action, "display") == 0) { alternative_display_user(a); exit(0); } else if (strcmp(action, "query") == 0) { @@ -2516,40 +2652,15 @@ main(int argc, char **argv) /* Actions below might modify the system. */ log_msg("run with %s", get_argv_string(argc, argv)); - if (alternative_has_current_link(a)) { - current_choice = alternative_get_current(a); - /* Detect manually modified alternative, switch to manual. */ - if (!alternative_has_choice(a, current_choice)) { - struct stat st; - - errno = 0; - if (stat(current_choice, &st) == -1 && errno != ENOENT) - syserr(_("cannot stat file '%s'"), current_choice); - - if (errno == ENOENT) { - warning(_("%s/%s is dangling, it will be updated " - "with best choice."), altdir, a->master_name); - alternative_set_status(a, ALT_ST_AUTO); - } else if (a->status != ALT_ST_MANUAL) { - warning(_("%s/%s has been changed (manually or by " - "a script). Switching to manual " - "updates only."), altdir, a->master_name); - alternative_set_status(a, ALT_ST_MANUAL); - } - } - } else { - /* Lack of alternative link => automatic mode. */ - verbose(_("setting up automatic selection of %s."), - a->master_name); - alternative_set_status(a, ALT_ST_AUTO); - } + current_choice = alternative_get_current(a); + alternative_select_mode(a, current_choice); if (strcmp(action, "set") == 0) { if (alternative_has_choice(a, path)) new_choice = path; else - error(_("alternative %s for %s not registered, " - "not setting."), path, a->master_name); + error(_("alternative %s for %s not registered; " + "not setting"), path, a->master_name); alternative_set_status(a, ALT_ST_MANUAL); } else if (strcmp(action, "auto") == 0) { alternative_set_status(a, ALT_ST_AUTO); @@ -2567,13 +2678,10 @@ main(int argc, char **argv) alternative_display_user(a); } else if (alternative_choices_count(a) == 1 && a->status == ALT_ST_AUTO && - alternative_has_current_link(a)) { - char *cur = alternative_get_current(a); - - pr(_("There is only one alternative in link group %s: %s"), - a->master_name, cur); + current_choice != NULL) { + pr(_("There is only one alternative in link group %s (providing %s): %s"), + a->master_name, a->master_link, current_choice); pr(_("Nothing to configure.")); - free(cur); } else { new_choice = alternative_select_choice(a); } @@ -2581,8 +2689,8 @@ main(int argc, char **argv) if (alternative_has_choice(a, path)) alternative_remove_choice(a, path); else - verbose(_("alternative %s for %s not registered, not " - "removing."), path, a->master_name); + verbose(_("alternative %s for %s not registered; not " + "removing"), path, a->master_name); if (current_choice && strcmp(current_choice, path) == 0) { struct fileset *best; @@ -2614,60 +2722,14 @@ main(int argc, char **argv) if (a->status == ALT_ST_AUTO) { new_choice = alternative_get_best(a)->master_file; } else { - verbose(_("automatic updates of %s/%s are disabled, " - "leaving it alone."), altdir, a->master_name); + verbose(_("automatic updates of %s/%s are disabled; " + "leaving it alone"), altdir, a->master_name); verbose(_("to return to automatic updates use " - "'%s --auto %s'."), PROGNAME, a->master_name); - } - } - - /* No choice left, remove everything. */ - if (!alternative_choices_count(a)) { - log_msg("link group %s fully removed", a->master_name); - alternative_remove(a); - exit(0); - } - - /* New choice wanted. */ - if (new_choice && (!current_choice || - strcmp(new_choice, current_choice) != 0)) { - log_msg("link group %s updated to point to %s", a->master_name, - new_choice); - info(_("using %s to provide %s (%s) in %s."), new_choice, - a->master_link, a->master_name, - (a->status == ALT_ST_AUTO) ? _("auto mode") : - _("manual mode")); - debug("prepare_install(%s)", new_choice); - alternative_prepare_install(a, new_choice); - } else if (alternative_is_broken(a)) { - log_msg("auto-repair link group %s", a->master_name); - warning(_("forcing reinstallation of alternative %s because " - "link group %s is broken."), current_choice, - a->master_name); - - if (current_choice && !alternative_has_choice(a, current_choice)) { - struct fileset *best = alternative_get_best(a); - - warning(_("current alternative %s is unknown, " - "switching to %s for link group %s."), - current_choice, best->master_file, - a->master_name); - current_choice = best->master_file; - alternative_set_status(a, ALT_ST_AUTO); + "'%s --auto %s'"), PROGNAME, a->master_name); } - - if (current_choice) - alternative_prepare_install(a, current_choice); } - /* Save administrative file if needed. */ - if (a->modified) { - debug("%s is modified and will be saved", a->master_name); - alternative_save(a); - } - - /* Replace all symlinks in one pass. */ - alternative_commit(a); + alternative_update(a, current_choice, new_choice); return 0; } |