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>
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.
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.
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/>.
27 #include <sys/types.h>
47 #include <dpkg/macros.h>
48 #include <dpkg/i18n.h>
50 /* Global variables: */
52 #define PROGNAME "update-alternatives"
54 static const char *altdir
= SYSCONFDIR
"/alternatives";
55 static char *admdir
= NULL
;
56 static const char *instdir
= "";
57 static size_t instdir_len
;
59 static const char *prog_path
= "update-alternatives";
65 ACTION_SET_SELECTIONS
,
66 ACTION_GET_SELECTIONS
,
77 static struct action_name
{
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" },
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;
119 printf(_("Debian %s version %s.\n"), PROGNAME
, VERSION
);
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"));
131 "Usage: %s [<option> ...] <command>\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"
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"
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"
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"
184 static void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(1)
185 error(char const *fmt
, ...)
189 fprintf(stderr
, "%s: %s: ", PROGNAME
, _("error"));
191 vfprintf(stderr
, fmt
, args
);
193 fprintf(stderr
, "\n");
197 static void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(1)
198 syserr(char const *fmt
, ...)
202 fprintf(stderr
, "%s: %s: ", PROGNAME
, _("error"));
204 vfprintf(stderr
, fmt
, args
);
206 fprintf(stderr
, ": %s\n", strerror(errno
));
210 static void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(1)
211 badusage(char const *fmt
, ...)
215 fprintf(stderr
, "%s: ", PROGNAME
);
217 vfprintf(stderr
, fmt
, args
);
219 fprintf(stderr
, "\n\n");
220 fprintf(stderr
, _("Use '%s --help' for program usage information."),
222 fprintf(stderr
, "\n");
226 static void LIBCOMPAT_ATTR_PRINTF(1)
227 warning(char const *fmt
, ...)
231 if (opt_verbose
< OUTPUT_NORMAL
)
234 fprintf(stderr
, "%s: %s: ", PROGNAME
, _("warning"));
236 vfprintf(stderr
, fmt
, args
);
238 fprintf(stderr
, "\n");
241 static void LIBCOMPAT_ATTR_PRINTF(1)
242 debug(char const *fmt
, ...)
246 if (opt_verbose
< OUTPUT_DEBUG
)
249 fprintf(stderr
, "DEBUG: ");
251 vfprintf(stderr
, fmt
, args
);
253 fprintf(stderr
, "\n");
256 static void LIBCOMPAT_ATTR_PRINTF(1)
257 verbose(char const *fmt
, ...)
261 if (opt_verbose
< OUTPUT_VERBOSE
)
264 printf("%s: ", PROGNAME
);
271 static void LIBCOMPAT_ATTR_PRINTF(1)
272 info(char const *fmt
, ...)
276 if (opt_verbose
< OUTPUT_NORMAL
)
279 printf("%s: ", PROGNAME
);
286 static void LIBCOMPAT_ATTR_PRINTF(1)
287 pr(char const *fmt
, ...)
304 error(_("malloc failed (%zu bytes)"), size
);
310 xstrdup(const char *str
)
317 new_str
= strdup(str
);
319 error(_("failed to allocate memory"));
325 xstrndup(const char *str
, size_t n
)
332 new_str
= strndup(str
, n
);
334 error(_("failed to allocate memory"));
339 static char * LIBCOMPAT_ATTR_VPRINTF(1)
340 xvasprintf(const char *fmt
, va_list args
)
344 if (vasprintf(&str
, fmt
, args
) < 0)
345 error(_("failed to allocate memory"));
350 static char * LIBCOMPAT_ATTR_PRINTF(1)
351 xasprintf(const char *fmt
, ...)
357 str
= xvasprintf(fmt
, args
);
364 areadlink(const char *linkname
)
370 /* Allocate required memory to store the value of the symlink */
371 if (lstat(linkname
, &st
))
374 if (!S_ISLNK(st
.st_mode
)) {
379 buf
= xmalloc(st
.st_size
+ 1);
381 /* Read it and terminate the string properly */
382 size
= readlink(linkname
, buf
, st
.st_size
);
384 int saved_errno
= errno
;
397 spawn(const char *prog
, const char *args
[])
404 error(_("fork failed"));
406 execvp(prog
, (char *const *)args
);
407 syserr(_("unable to execute %s (%s)"), prog
, prog
);
409 while ((dead_pid
= waitpid(pid
, &status
, 0)) == -1 && errno
== EINTR
) ;
411 error(_("wait for subprocess %s failed"), prog
);
417 rename_mv(const char *src
, const char *dst
)
419 const char *args
[] = { "mv", src
, dst
, NULL
};
422 if (rename(src
, dst
) == 0)
427 rc
= spawn("mv", args
);
428 if (WIFEXITED(rc
) && WEXITSTATUS(rc
) == 0)
435 xrename(const char *src
, const char *dst
)
437 if (!rename_mv(src
, dst
))
438 syserr(_("unable to install '%.250s' as '%.250s'"), src
, dst
);
441 static void LIBCOMPAT_ATTR_PRINTF(1)
442 xunlink_args(const char *fmt
, ...)
448 path
= xvasprintf(fmt
, args
);
451 if (unlink(path
) < 0 && errno
!= ENOENT
)
452 syserr(_("unable to remove '%s'"), path
);
458 xdirname(const char *pathname
)
460 char *dirname
, *slash
;
462 slash
= strrchr(pathname
, '/');
464 dirname
= xstrndup(pathname
, slash
- pathname
);
466 dirname
= xstrdup(".");
472 make_path(const char *pathname
, mode_t mode
)
474 char *dirname
, *slash
;
476 dirname
= xstrdup(pathname
);
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 slash
= strchr(dirname
, '/');
483 while (slash
!= NULL
) {
484 slash
= strchr(slash
+ 1, '/');
488 if (mkdir(dirname
, mode
) < 0 && errno
!= EEXIST
) {
501 static void LIBCOMPAT_ATTR_PRINTF(1)
502 log_msg(const char *fmt
, ...)
506 if (fh_log
== NULL
) {
507 fh_log
= fopen(log_file
, "a");
508 if (fh_log
== NULL
&& errno
== ENOENT
) {
509 char *log_dir
= xdirname(log_file
);
511 if (make_path(log_dir
, 0755) < 0)
512 syserr(_("cannot create log directory '%s'"),
516 fh_log
= fopen(log_file
, "a");
518 if (fh_log
== NULL
&& errno
!= EACCES
)
519 syserr(_("cannot append to '%s'"), log_file
);
528 if (localtime_r(&now
, &tm
) == NULL
)
529 syserr(_("cannot get local time to log into '%s'"), log_file
);
530 strftime(timestamp
, sizeof(timestamp
), "%Y-%m-%d %H:%M:%S",
532 fprintf(fh_log
, "%s %s: ", PROGNAME
, timestamp
);
534 vfprintf(fh_log
, fmt
, args
);
536 fprintf(fh_log
, "\n");
541 * Filesystem access for alernative handling.
545 fsys_get_path(const char *pathpart
)
547 return xasprintf("%s%s", instdir
, pathpart
);
551 fsys_set_dir(const char *dir
)
554 const char *instdir_env
;
556 instdir_env
= getenv(INSTDIR_ENVVAR
);
563 instdir_len
= strlen(dir
);
569 fsys_gen_admindir(void)
571 return fsys_get_path(ADMINDIR
"/alternatives");
575 fsys_pathname_is_missing(const char *pathname
)
580 root_pathname
= fsys_get_path(pathname
);
583 if (stat(root_pathname
, &st
) < 0 && errno
!= ENOENT
)
584 syserr(_("cannot stat file '%s'"), root_pathname
);
595 fsys_lstat(const char *linkname
, struct stat
*st
)
600 root_linkname
= fsys_get_path(linkname
);
603 rc
= lstat(root_linkname
, st
);
611 fsys_areadlink(const char *linkname
)
616 root_linkname
= fsys_get_path(linkname
);
617 target
= areadlink(root_linkname
);
624 fsys_xreadlink(const char *linkname
)
628 buf
= fsys_areadlink(linkname
);
630 syserr(_("unable to read link '%s%.255s'"), instdir
, linkname
);
636 fsys_set_ref_time(const char *linkname
, const char *target
)
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. */
644 struct timeval tv
[2];
647 if (fsys_lstat(target
, &st
) < 0) {
649 syserr(_("unable to get file '%s%s' metadata"),
654 tv
[0].tv_sec
= st
.st_mtime
;
656 tv
[1].tv_sec
= st
.st_mtime
;
659 root_linkname
= fsys_get_path(linkname
);
660 if (lutimes(root_linkname
, tv
) < 0 && errno
!= ENOSYS
)
661 syserr(_("cannot set symlink '%s' timestamp"), root_linkname
);
667 fsys_symlink(const char *filename
, const char *linkname
)
671 root_linkname
= fsys_get_path(linkname
);
673 if (unlink(root_linkname
) < 0 && errno
!= ENOENT
)
674 syserr(_("unable to remove '%s'"), root_linkname
);
676 if (symlink(filename
, root_linkname
))
677 syserr(_("error creating symbolic link '%.255s'"), root_linkname
);
683 fsys_mv(const char *src
, const char *dst
)
688 root_src
= fsys_get_path(src
);
689 root_dst
= fsys_get_path(dst
);
691 xrename(root_src
, root_dst
);
698 fsys_rm(const char *f
)
702 root_f
= fsys_get_path(f
);
704 if (unlink(root_f
) < 0 && errno
!= ENOENT
)
705 syserr(_("unable to remove '%s'"), root_f
);
710 static void LIBCOMPAT_ATTR_PRINTF(1)
711 fsys_rm_args(const char *fmt
, ...)
717 path
= xvasprintf(fmt
, args
);
729 struct fileset
*next
;
735 struct slave_file
*next
;
741 static struct fileset
*
742 fileset_new(const char *master_file
, int prio
)
746 fs
= xmalloc(sizeof(*fs
));
748 fs
->master_file
= xstrdup(master_file
);
756 fileset_free(struct fileset
*fs
)
758 struct slave_file
*slave
, *next
;
760 free(fs
->master_file
);
761 for (slave
= fs
->slaves
; slave
; slave
= next
) {
771 fileset_add_slave(struct fileset
*fs
, const char *name
, const char *file
)
773 struct slave_file
*sl
, *cur
, *prev
= NULL
;
775 /* Replace existing first */
776 for (cur
= fs
->slaves
; cur
; cur
= cur
->next
) {
777 if (strcmp(cur
->name
, name
) == 0) {
779 cur
->file
= xstrdup(file
);
785 /* Otherwise add new at the end */
786 sl
= xmalloc(sizeof(*sl
));
788 sl
->name
= xstrdup(name
);
789 sl
->file
= xstrdup(file
);
797 fileset_get_slave(struct fileset
*fs
, const char *name
)
799 struct slave_file
*slave
;
801 for (slave
= fs
->slaves
; slave
; slave
= slave
->next
) {
802 if (strcmp(slave
->name
, name
) == 0)
810 fileset_has_slave(struct fileset
*fs
, const char *name
)
812 const char *file
= fileset_get_slave(fs
, name
);
817 return file
[0] != '\0';
821 fileset_can_install_slave(struct fileset
*fs
, const char *slave_name
)
823 /* Decide whether the slave alternative must be setup */
824 if (fileset_has_slave(fs
, slave_name
)) {
825 const char *slave
= fileset_get_slave(fs
, slave_name
);
827 if (!fsys_pathname_is_missing(slave
))
835 struct slave_link
*next
;
841 struct commit_operation
{
842 struct commit_operation
*next
;
855 enum alternative_update_reason
{
857 ALT_UPDATE_SLAVE_CHANGED
,
858 ALT_UPDATE_LINK_BROKEN
,
866 enum alternative_status
{
872 struct slave_link
*slaves
;
873 struct fileset
*choices
;
875 struct commit_operation
*commit_ops
;
883 slave_link_free(struct slave_link
*slave
)
891 commit_operation_free(struct commit_operation
*commit_op
)
893 free(commit_op
->arg_a
);
894 free(commit_op
->arg_b
);
898 static struct alternative
*
899 alternative_new(const char *name
)
901 struct alternative
*alt
;
903 alt
= xmalloc(sizeof(*alt
));
904 alt
->master_name
= xstrdup(name
);
905 alt
->master_link
= NULL
;
907 alt
->status
= ALT_ST_UNKNOWN
;
910 alt
->commit_ops
= NULL
;
911 alt
->modified
= false;
912 alt
->known_current
= false;
919 alternative_ref(struct alternative
*a
)
925 alternative_unref(struct alternative
*a
)
927 return --a
->ref_count
== 0;
931 alternative_choices_free(struct alternative
*a
)
940 a
->choices
= fs
->next
;
946 alternative_commit_operations_free(struct alternative
*a
)
948 struct commit_operation
*op
;
950 while (a
->commit_ops
) {
952 a
->commit_ops
= op
->next
;
953 commit_operation_free(op
);
958 alternative_reset(struct alternative
*alt
)
960 struct slave_link
*slave
;
964 free(alt
->master_link
);
965 alt
->master_link
= NULL
;
966 while (alt
->slaves
) {
968 alt
->slaves
= slave
->next
;
969 slave_link_free(slave
);
971 alternative_choices_free(alt
);
972 alternative_commit_operations_free(alt
);
973 alt
->modified
= false;
974 alt
->known_current
= false;
978 alternative_free(struct alternative
*alt
)
980 if (!alternative_unref(alt
))
983 alternative_reset(alt
);
984 free(alt
->master_name
);
989 alternative_choices_count(struct alternative
*alt
)
994 for (fs
= alt
->choices
; fs
; fs
= fs
->next
)
1001 alternative_slaves_count(struct alternative
*alt
)
1003 struct slave_link
*sl
;
1006 for (sl
= alt
->slaves
; sl
; sl
= sl
->next
)
1013 compare_fileset(const void *va
, const void *vb
)
1015 const struct fileset
*a
= *(const struct fileset
**)va
;
1016 const struct fileset
*b
= *(const struct fileset
**)vb
;
1018 assert(a
&& a
->master_file
);
1019 assert(b
&& b
->master_file
);
1021 return strcmp(a
->master_file
, b
->master_file
);
1025 compare_slave_link(const void *va
, const void *vb
)
1027 const struct slave_link
*a
= *(const struct slave_link
**)va
;
1028 const struct slave_link
*b
= *(const struct slave_link
**)vb
;
1030 assert(a
&& a
->name
);
1031 assert(b
&& b
->name
);
1033 return strcmp(a
->name
, b
->name
);
1037 alternative_sort_choices(struct alternative
*a
)
1040 struct fileset
**table
, *fs
;
1042 count
= alternative_choices_count(a
);
1043 if (count
< 2) /* Nothing to sort */
1046 /* Store objects in a table instead of a linked list */
1047 table
= xmalloc(sizeof(fs
) * count
);
1048 for (fs
= a
->choices
, i
= 0; fs
; fs
= fs
->next
) {
1049 assert(fs
->master_file
);
1053 qsort(table
, count
, sizeof(fs
), compare_fileset
);
1055 /* Rewrite the linked list from the sorted table */
1056 a
->choices
= fs
= table
[0];
1057 table
[count
- 1]->next
= NULL
;
1058 for (i
= 1; i
< count
; fs
= fs
->next
, i
++)
1059 fs
->next
= table
[i
];
1064 alternative_sort_slaves(struct alternative
*a
)
1067 struct slave_link
**table
, *sl
;
1069 count
= alternative_slaves_count(a
);
1070 if (count
< 2) /* Nothing to sort */
1073 /* Store objects in a table instead of a linked list */
1074 table
= xmalloc(sizeof(sl
) * count
);
1075 for (sl
= a
->slaves
, i
= 0; sl
; sl
= sl
->next
, i
++) {
1079 qsort(table
, count
, sizeof(sl
), compare_slave_link
);
1081 /* Rewrite the linked list from the sorted table */
1082 a
->slaves
= sl
= table
[0];
1083 table
[count
- 1]->next
= NULL
;
1084 for (i
= 1; i
< count
; sl
= sl
->next
, i
++)
1085 sl
->next
= table
[i
];
1089 static struct fileset
*
1090 alternative_get_fileset(struct alternative
*a
, const char *file
)
1094 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1095 if (strcmp(fs
->master_file
, file
) == 0)
1101 static struct slave_link
*
1102 alternative_get_slave(struct alternative
*a
, const char *name
)
1104 struct slave_link
*sl
;
1106 for (sl
= a
->slaves
; sl
; sl
= sl
->next
)
1107 if (strcmp(sl
->name
, name
) == 0)
1114 alternative_has_slave(struct alternative
*a
, const char *name
)
1116 return alternative_get_slave(a
, name
) != NULL
;
1120 alternative_has_choice(struct alternative
*a
, const char *file
)
1122 return alternative_get_fileset(a
, file
) != NULL
;
1126 alternative_add_choice(struct alternative
*a
, struct fileset
*fs
)
1128 struct fileset
*cur
, *prev
= NULL
;
1130 /* Replace if already existing */
1131 for (cur
= a
->choices
; cur
; cur
= cur
->next
) {
1132 if (strcmp(cur
->master_file
, fs
->master_file
) == 0) {
1133 fs
->next
= cur
->next
;
1140 /* XXX: Be smarter in detecting change? */
1147 /* Otherwise add at the end */
1156 static struct slave_link
*
1157 alternative_add_slave(struct alternative
*a
,
1158 const char *slave_name
, const char *slave_link
)
1160 struct slave_link
*sl
, *new;
1162 /* Replace if already existing */
1163 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1164 if (strcmp(sl
->name
, slave_name
) == 0) {
1166 sl
->link
= xstrdup(slave_link
);
1169 if (sl
->next
== NULL
)
1173 /* Otherwise create new and add at the end */
1174 new = xmalloc(sizeof(*new));
1175 new->name
= xstrdup(slave_name
);
1176 new->link
= xstrdup(slave_link
);
1177 new->updated
= false;
1188 alternative_copy_slave(struct alternative
*a
, struct slave_link
*sl
)
1190 struct slave_link
*sl_new
;
1192 sl_new
= alternative_add_slave(a
, sl
->name
, sl
->link
);
1193 sl_new
->updated
= sl
->updated
;
1197 alternative_status_string(enum alternative_status status
)
1199 return (status
== ALT_ST_AUTO
) ? "auto" : "manual";
1203 alternative_status_describe(enum alternative_status status
)
1205 return (status
== ALT_ST_AUTO
) ? _("auto mode") : _("manual mode");
1209 alternative_set_status(struct alternative
*a
, enum alternative_status status
)
1211 if (a
->status
== ALT_ST_UNKNOWN
|| status
!= a
->status
)
1214 if (a
->status
!= ALT_ST_UNKNOWN
&& status
!= a
->status
)
1215 log_msg("status of link group %s set to %s", a
->master_link
,
1216 alternative_status_string(status
));
1222 alternative_set_link(struct alternative
*a
, const char *linkname
)
1224 if (a
->master_link
== NULL
|| strcmp(linkname
, a
->master_link
) != 0)
1227 free(a
->master_link
);
1228 a
->master_link
= xstrdup(linkname
);
1232 alternative_remove_choice(struct alternative
*a
, const char *file
)
1234 struct fileset
*fs
, *fs_prev
;
1237 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1238 if (strcmp(fs
->master_file
, file
) != 0) {
1243 fs_prev
->next
= fs
->next
;
1245 a
->choices
= fs
->next
;
1255 * Alternatives Database Load/Store functions.
1258 enum LIBCOMPAT_ATTR_ENUM_FLAGS altdb_flags
{
1259 ALTDB_LAX_PARSER
= 1 << 0,
1260 ALTDB_WARN_PARSER
= 1 << 1,
1263 struct altdb_context
{
1266 enum altdb_flags flags
;
1268 void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(2)
1269 (*bad_format
)(struct altdb_context
*, const char *format
, ...);
1274 altdb_context_free(struct altdb_context
*ctx
)
1278 free(ctx
->filename
);
1282 altdb_filter_namelist(const struct dirent
*entry
)
1284 if (strcmp(entry
->d_name
, ".") == 0 ||
1285 strcmp(entry
->d_name
, "..") == 0 ||
1286 (strlen(entry
->d_name
) > strlen(ALT_TMP_EXT
) &&
1287 strcmp(entry
->d_name
+ strlen(entry
->d_name
) -
1288 strlen(ALT_TMP_EXT
), ALT_TMP_EXT
) == 0))
1294 altdb_get_namelist(struct dirent
***table
)
1298 count
= scandir(admdir
, table
, altdb_filter_namelist
, alphasort
);
1300 if (errno
!= ENOENT
)
1301 syserr(_("cannot scan directory '%.255s'"), admdir
);
1302 /* The directory does not exist, proceed anyway. */
1311 altdb_free_namelist(struct dirent
**table
, int n
)
1319 altdb_get_line(struct altdb_context
*ctx
, const char *name
)
1322 size_t len
, bufsz
, i
;
1325 buf
= xmalloc(bufsz
);
1327 for (i
= 0; true; i
+= strlen(line
)) {
1329 line
= fgets(buf
+ i
, bufsz
- i
, ctx
->fh
);
1331 if (strlen(buf
) < bufsz
- 1 || buf
[bufsz
- 2] == '\n')
1333 /* Need more space */
1335 buf
= realloc(buf
, bufsz
);
1337 error(_("failed to allocate memory"));
1341 ctx
->bad_format(ctx
, _("unexpected end of file while trying "
1342 "to read %s"), name
);
1343 ctx
->bad_format(ctx
, _("while reading %s: %s"),
1344 name
, strerror(errno
));
1348 if (len
== 0 || buf
[len
- 1] != '\n') {
1349 ctx
->bad_format(ctx
, _("line not terminated while trying "
1350 "to read %s"), name
);
1352 line
[len
- 1] = '\0';
1357 static void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(2)
1358 altdb_parse_error(struct altdb_context
*ctx
, const char *format
, ...)
1363 va_start(args
, format
);
1364 msg
= xvasprintf(format
, args
);
1367 error(_("%s corrupt: %s"), ctx
->filename
, msg
);
1370 static void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(2)
1371 altdb_parse_stop(struct altdb_context
*ctx
, const char *format
, ...)
1373 longjmp(ctx
->on_error
, 1);
1377 altdb_print_line(struct altdb_context
*ctx
, const char *line
)
1379 if (strchr(line
, '\n') != NULL
)
1380 error(_("newlines prohibited in update-alternatives files (%s)"),
1383 if (fprintf(ctx
->fh
, "%s\n", line
) < (int) strlen(line
) + 1)
1384 syserr(_("unable to write file '%s'"), ctx
->filename
);
1388 alternative_parse_slave(struct alternative
*a
, struct altdb_context
*ctx
)
1390 char *name
, *linkname
;
1391 struct slave_link
*sl
;
1393 name
= altdb_get_line(ctx
, _("slave name"));
1394 if (!strlen(name
)) { /* End of list */
1398 sl
= alternative_get_slave(a
, name
);
1401 ctx
->bad_format(ctx
, _("duplicate slave name %s"), sl
->name
);
1404 linkname
= altdb_get_line(ctx
, _("slave link"));
1405 if (strcmp(linkname
, a
->master_link
) == 0) {
1408 ctx
->bad_format(ctx
, _("slave link same as main link %s"),
1411 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1412 if (strcmp(linkname
, sl
->link
) == 0) {
1415 ctx
->bad_format(ctx
, _("duplicate slave link %s"),
1420 alternative_add_slave(a
, name
, linkname
);
1428 alternative_parse_fileset(struct alternative
*a
, struct altdb_context
*ctx
)
1431 struct slave_link
*sl
;
1434 master_file
= altdb_get_line(ctx
, _("master file"));
1435 if (!strlen(master_file
)) { /* End of list */
1440 fs
= alternative_get_fileset(a
, master_file
);
1442 ctx
->bad_format(ctx
, _("duplicate path %s"), master_file
);
1444 if (fsys_pathname_is_missing(master_file
)) {
1447 /* File not found - remove. */
1448 if (ctx
->flags
& ALTDB_WARN_PARSER
)
1449 warning(_("alternative %s (part of link group %s) "
1450 "doesn't exist; removing from list of "
1451 "alternatives"), master_file
, a
->master_name
);
1452 junk
= altdb_get_line(ctx
, _("priority"));
1454 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1455 junk
= altdb_get_line(ctx
, _("slave file"));
1458 ctx
->modified
= true;
1460 char *prio_str
, *prio_end
;
1463 prio_str
= altdb_get_line(ctx
, _("priority"));
1465 prio
= strtol(prio_str
, &prio_end
, 10);
1466 /* XXX: Leak master_file/prio_str on non-fatal error */
1467 if (prio_str
== prio_end
|| *prio_end
!= '\0')
1468 ctx
->bad_format(ctx
, _("priority of %s: %s"),
1469 master_file
, prio_str
);
1470 if (prio
< INT_MIN
|| prio
> INT_MAX
|| errno
== ERANGE
)
1471 ctx
->bad_format(ctx
,
1472 _("priority of %s is out of range: %s"),
1473 master_file
, prio_str
);
1476 fs
= fileset_new(master_file
, prio
);
1477 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1478 char *slave_file
= altdb_get_line(ctx
, _("slave file"));
1479 fileset_add_slave(fs
, sl
->name
, slave_file
);
1482 alternative_add_choice(a
, fs
);
1490 alternative_load(struct alternative
*a
, enum altdb_flags flags
)
1492 struct altdb_context ctx
;
1497 /* Initialize parse context */
1498 ctx
.modified
= false;
1500 if (flags
& ALTDB_LAX_PARSER
)
1501 ctx
.bad_format
= altdb_parse_stop
;
1503 ctx
.bad_format
= altdb_parse_error
;
1504 ctx
.filename
= xasprintf("%s/%s", admdir
, a
->master_name
);
1506 /* Open the alternative file. */
1507 ctx
.fh
= fopen(ctx
.filename
, "r");
1508 if (ctx
.fh
== NULL
) {
1509 if (errno
== ENOENT
) {
1510 altdb_context_free(&ctx
);
1514 syserr(_("unable to open file '%s'"), ctx
.filename
);
1517 if (setjmp(ctx
.on_error
)) {
1518 altdb_context_free(&ctx
);
1519 alternative_reset(a
);
1523 /* Verify the alternative is not empty. */
1524 if (fstat(fileno(ctx
.fh
), &st
) == -1)
1525 syserr(_("cannot stat file '%s'"), ctx
.filename
);
1526 if (st
.st_size
== 0) {
1527 altdb_context_free(&ctx
);
1528 alternative_reset(a
);
1532 /* Start parsing mandatory attributes (link+status) of the alternative */
1533 alternative_reset(a
);
1534 status
= altdb_get_line(&ctx
, _("status"));
1535 if (strcmp(status
, "auto") != 0 && strcmp(status
, "manual") != 0)
1536 ctx
.bad_format(&ctx
, _("invalid status"));
1537 alternative_set_status(a
, (strcmp(status
, "auto") == 0) ?
1538 ALT_ST_AUTO
: ALT_ST_MANUAL
);
1541 master_link
= altdb_get_line(&ctx
, _("master link"));
1542 alternative_set_link(a
, master_link
);
1545 /* Parse the description of the slaves links of the alternative */
1546 while (alternative_parse_slave(a
, &ctx
));
1548 /* Parse the available choices in the alternative */
1549 while (alternative_parse_fileset(a
, &ctx
)) ;
1551 /* Close database file */
1553 syserr(_("unable to close file '%s'"), ctx
.filename
);
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 a
->modified
= ctx
.modified
;
1565 alternative_save(struct alternative
*a
)
1567 struct altdb_context ctx
;
1568 struct slave_link
*sl
, *sl_prev
;
1570 char *filenew
, *file
;
1572 /* Cleanup unused slaves before writing admin file. */
1576 bool has_slave
= false;
1578 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1579 if (fileset_has_slave(fs
, sl
->name
)) {
1586 struct slave_link
*sl_rm
;
1588 verbose(_("discarding obsolete slave link %s (%s)"),
1589 sl
->name
, sl
->link
);
1591 sl_prev
->next
= sl
->next
;
1593 a
->slaves
= sl
->next
;
1596 slave_link_free(sl_rm
);
1604 alternative_sort_slaves(a
);
1605 alternative_sort_choices(a
);
1607 /* Write admin file. */
1608 file
= xasprintf("%s/%s", admdir
, a
->master_name
);
1609 filenew
= xasprintf("%s" ALT_TMP_EXT
, file
);
1611 ctx
.filename
= filenew
;
1612 ctx
.fh
= fopen(ctx
.filename
, "w");
1613 if (ctx
.fh
== NULL
&& errno
== ENOENT
) {
1614 if (make_path(admdir
, 0755) < 0)
1615 syserr(_("cannot create administrative directory '%s'"),
1617 ctx
.fh
= fopen(ctx
.filename
, "w");
1620 syserr(_("unable to create file '%s'"), ctx
.filename
);
1622 altdb_print_line(&ctx
, alternative_status_string(a
->status
));
1623 altdb_print_line(&ctx
, a
->master_link
);
1624 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1625 altdb_print_line(&ctx
, sl
->name
);
1626 altdb_print_line(&ctx
, sl
->link
);
1628 altdb_print_line(&ctx
, "");
1630 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1633 altdb_print_line(&ctx
, fs
->master_file
);
1635 prio
= xasprintf("%d", fs
->priority
);
1636 altdb_print_line(&ctx
, prio
);
1639 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1640 if (fileset_has_slave(fs
, sl
->name
))
1641 altdb_print_line(&ctx
,
1642 fileset_get_slave(fs
, sl
->name
));
1644 altdb_print_line(&ctx
, "");
1647 altdb_print_line(&ctx
, "");
1649 /* Close database file */
1651 syserr(_("unable to flush file '%s'"), ctx
.filename
);
1652 if (fsync(fileno(ctx
.fh
)))
1653 syserr(_("unable to sync file '%s'"), ctx
.filename
);
1655 syserr(_("unable to close file '%s'"), ctx
.filename
);
1657 /* Put in place atomically. */
1658 xrename(filenew
, file
);
1665 alternative_set_current(struct alternative
*a
, char *new_choice
)
1667 a
->known_current
= true;
1668 a
->current
= new_choice
;
1674 alternative_get_current(struct alternative
*a
)
1679 if (a
->known_current
)
1682 curlink
= xasprintf("%s/%s", altdir
, a
->master_name
);
1683 file
= fsys_areadlink(curlink
);
1684 if (file
== NULL
&& errno
!= ENOENT
)
1685 syserr(_("cannot stat file '%s%s'"), instdir
, curlink
);
1688 return alternative_set_current(a
, file
);
1691 static struct fileset
*
1692 alternative_get_best(struct alternative
*a
)
1694 struct fileset
*fs
, *best
;
1695 const char *current
;
1697 current
= alternative_get_current(a
);
1699 best
= alternative_get_fileset(a
, current
);
1706 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1707 if (fs
->priority
> best
->priority
)
1714 alternative_display_query(struct alternative
*a
)
1716 struct fileset
*best
, *fs
;
1717 struct slave_link
*sl
;
1718 const char *current
;
1720 pr("Name: %s", a
->master_name
);
1721 pr("Link: %s", a
->master_link
);
1722 if (alternative_slaves_count(a
) > 0) {
1724 for (sl
= a
->slaves
; sl
; sl
= sl
->next
)
1725 pr(" %s %s", sl
->name
, sl
->link
);
1727 pr("Status: %s", alternative_status_string(a
->status
));
1728 best
= alternative_get_best(a
);
1730 pr("Best: %s", best
->master_file
);
1731 current
= alternative_get_current(a
);
1732 pr("Value: %s", current
? current
: "none");
1734 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1736 pr("Alternative: %s", fs
->master_file
);
1737 pr("Priority: %d", fs
->priority
);
1738 if (alternative_slaves_count(a
) == 0)
1741 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1742 if (fileset_has_slave(fs
, sl
->name
))
1743 pr(" %s %s", sl
->name
,
1744 fileset_get_slave(fs
, sl
->name
));
1750 alternative_display_user(struct alternative
*a
)
1752 const char *current
;
1754 struct slave_link
*sl
;
1756 pr("%s - %s", a
->master_name
, alternative_status_describe(a
->status
));
1757 fs
= alternative_get_best(a
);
1759 pr(_(" link best version is %s"), fs
->master_file
);
1761 pr(_(" link best version not available"));
1762 current
= alternative_get_current(a
);
1764 pr(_(" link currently points to %s"), current
);
1766 pr(_(" link currently absent"));
1768 pr(_(" link %s is %s"), a
->master_name
, a
->master_link
);
1769 for (sl
= a
->slaves
; sl
; sl
= sl
->next
)
1770 pr(_(" slave %s is %s"), sl
->name
, sl
->link
);
1772 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1773 pr(_("%s - priority %d"), fs
->master_file
, fs
->priority
);
1774 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1775 if (fileset_has_slave(fs
, sl
->name
))
1776 pr(_(" slave %s: %s"), sl
->name
,
1777 fileset_get_slave(fs
, sl
->name
));
1783 alternative_display_list(struct alternative
*a
)
1787 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1788 pr("%s", fs
->master_file
);
1792 alternative_print_choice(struct alternative
*a
, enum alternative_status status
,
1793 struct fileset
*fs
, int idx
, int len
)
1795 const char *current
= alternative_get_current(a
);
1798 if (a
->status
== status
&&
1799 current
&& strcmp(current
, fs
->master_file
) == 0)
1804 pr("%c %-12d %-*s % -10d %s", mark
, idx
, len
,
1805 fs
->master_file
, fs
->priority
, alternative_status_describe(status
));
1809 alternative_select_choice(struct alternative
*a
)
1811 const char *current
;
1812 char *ret
, selection
[_POSIX_PATH_MAX
];
1813 struct fileset
*best
, *fs
;
1817 n_choices
= alternative_choices_count(a
);
1818 current
= alternative_get_current(a
);
1819 best
= alternative_get_best(a
);
1823 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1824 len
= max(len
, (int)strlen(fs
->master_file
) + 1);
1827 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
);
1832 pr(" %-12.12s %-*.*s %-10.10s %s", _("Selection"), len
, len
,
1833 _("Path"), _("Priority"), _("Status"));
1834 pr("------------------------------------------------------------");
1836 alternative_print_choice(a
, ALT_ST_AUTO
, best
, idx
++, len
);
1837 for (fs
= a
->choices
; fs
; fs
= fs
->next
, idx
++)
1838 alternative_print_choice(a
, ALT_ST_MANUAL
, fs
, idx
, len
);
1840 printf(_("Press <enter> to keep the current choice[*], "
1841 "or type selection number: "));
1842 ret
= fgets(selection
, sizeof(selection
), stdin
);
1843 if (ret
== NULL
|| strlen(selection
) == 0) {
1846 selection
[strlen(selection
) - 1] = '\0';
1847 if (strlen(selection
) == 0)
1848 return xstrdup(current
);
1850 idx
= strtol(selection
, &ret
, 10);
1851 if (idx
>= 0 && errno
== 0 && *ret
== '\0') {
1852 /* Look up by index */
1854 alternative_set_status(a
, ALT_ST_AUTO
);
1855 return xstrdup(best
->master_file
);
1858 for (fs
= a
->choices
; idx
&& fs
; idx
--)
1861 alternative_set_status(a
, ALT_ST_MANUAL
);
1862 return xstrdup(fs
->master_file
);
1865 /* Look up by name */
1866 fs
= alternative_get_fileset(a
, selection
);
1868 alternative_set_status(a
, ALT_ST_MANUAL
);
1869 return xstrdup(selection
);
1876 alternative_config(struct alternative
*a
, const char *current_choice
)
1878 char *new_choice
= NULL
;
1880 if (alternative_choices_count(a
) == 0) {
1881 pr(_("There is no program which provides %s."),
1883 pr(_("Nothing to configure."));
1884 } else if (opt_skip_auto
&& a
->status
== ALT_ST_AUTO
) {
1885 alternative_display_user(a
);
1887 new_choice
= alternative_select_choice(a
);
1894 alternative_add_commit_op(struct alternative
*a
, enum opcode opcode
,
1895 const char *arg_a
, const char *arg_b
)
1897 struct commit_operation
*op
, *cur
;
1899 op
= xmalloc(sizeof(*op
));
1900 op
->opcode
= opcode
;
1901 op
->arg_a
= xstrdup(arg_a
);
1902 op
->arg_b
= xstrdup(arg_b
);
1905 /* Add at the end */
1906 cur
= a
->commit_ops
;
1907 while (cur
&& cur
->next
)
1916 alternative_commit(struct alternative
*a
)
1918 struct commit_operation
*op
;
1920 for (op
= a
->commit_ops
; op
; op
= op
->next
) {
1921 switch (op
->opcode
) {
1928 fsys_mv(op
->arg_a
, op
->arg_b
);
1930 case OPCODE_REF_TIME
:
1931 fsys_set_ref_time(op
->arg_a
, op
->arg_b
);
1936 alternative_commit_operations_free(a
);
1939 enum alternative_path_status
{
1945 static enum alternative_path_status
1946 alternative_path_classify(const char *linkname
)
1950 if (fsys_lstat(linkname
, &st
) == -1) {
1951 if (errno
!= ENOENT
)
1952 syserr(_("cannot stat file '%s%s'"), instdir
, linkname
);
1953 return ALT_PATH_MISSING
;
1954 } else if (S_ISLNK(st
.st_mode
)) {
1955 return ALT_PATH_SYMLINK
;
1957 return ALT_PATH_OTHER
;
1962 alternative_path_can_remove(const char *linkname
)
1967 if (alternative_path_classify(linkname
) == ALT_PATH_OTHER
)
1974 alternative_path_needs_update(const char *linkname
, const char *filename
)
1982 switch (alternative_path_classify(linkname
)) {
1983 case ALT_PATH_SYMLINK
:
1984 linktarget
= fsys_xreadlink(linkname
);
1985 if (strcmp(linktarget
, filename
) == 0)
1992 case ALT_PATH_OTHER
:
1993 warning(_("not replacing %s with a link"), linkname
);
1995 case ALT_PATH_MISSING
:
2002 alternative_prepare_install_single(struct alternative
*a
, const char *name
,
2003 const char *linkname
, const char *file
)
2007 /* Create alternatives directory (/etc/alternatives) if missing. */
2008 if (fsys_pathname_is_missing(altdir
)) {
2009 char *root_altdir
= fsys_get_path(altdir
);
2011 if (make_path(root_altdir
, 0755) < 0)
2012 syserr(_("cannot create alternatives directory '%s'"),
2018 fn
= xasprintf("%s/%s", altdir
, name
);
2020 /* Create link in /etc/alternatives. */
2021 fntmp
= xasprintf("%s/%s" ALT_TMP_EXT
, altdir
, name
);
2022 fsys_symlink(file
, fntmp
);
2023 alternative_add_commit_op(a
, OPCODE_MV
, fntmp
, fn
);
2024 if (fsys_pathname_is_missing(fn
))
2025 alternative_add_commit_op(a
, OPCODE_REF_TIME
, fn
, file
);
2028 if (alternative_path_needs_update(linkname
, fn
)) {
2029 /* Create alternative link. */
2030 fntmp
= xasprintf("%s" ALT_TMP_EXT
, linkname
);
2031 fsys_symlink(fn
, fntmp
);
2032 alternative_add_commit_op(a
, OPCODE_MV
, fntmp
, linkname
);
2033 if (fsys_pathname_is_missing(linkname
))
2034 alternative_add_commit_op(a
, OPCODE_REF_TIME
, linkname
, fn
);
2041 alternative_prepare_install(struct alternative
*a
, const char *choice
)
2043 struct slave_link
*sl
;
2046 fs
= alternative_get_fileset(a
, choice
);
2048 error(_("can't install unknown choice %s"), choice
);
2050 /* Take care of master alternative */
2051 alternative_prepare_install_single(a
, a
->master_name
, a
->master_link
,
2054 /* Take care of slaves alternatives */
2055 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
2058 if (fileset_can_install_slave(fs
, sl
->name
)) {
2059 alternative_prepare_install_single(a
, sl
->name
,
2060 sl
->link
, fileset_get_slave(fs
, sl
->name
));
2064 /* Slave can't be installed */
2065 if (fileset_has_slave(fs
, sl
->name
))
2066 warning(_("skip creation of %s because associated "
2067 "file %s (of link group %s) doesn't exist"),
2068 sl
->link
, fileset_get_slave(fs
, sl
->name
),
2071 /* Drop unused slave. */
2072 fn
= xasprintf("%s/%s", altdir
, sl
->name
);
2073 if (alternative_path_can_remove(sl
->link
))
2074 alternative_add_commit_op(a
, OPCODE_RM
, sl
->link
, NULL
);
2076 warning(_("not removing %s since it's not a symlink"),
2078 alternative_add_commit_op(a
, OPCODE_RM
, fn
, NULL
);
2084 alternative_remove_files(struct alternative
*a
)
2086 struct slave_link
*sl
;
2088 fsys_rm_args("%s" ALT_TMP_EXT
, a
->master_link
);
2089 if (alternative_path_can_remove(a
->master_link
))
2090 fsys_rm(a
->master_link
);
2092 fsys_rm_args("%s/%s" ALT_TMP_EXT
, altdir
, a
->master_name
);
2093 fsys_rm_args("%s/%s", altdir
, a
->master_name
);
2095 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
2096 fsys_rm_args("%s" ALT_TMP_EXT
, sl
->link
);
2097 if (alternative_path_can_remove(sl
->link
))
2100 fsys_rm_args("%s/%s" ALT_TMP_EXT
, altdir
, sl
->name
);
2101 fsys_rm_args("%s/%s", altdir
, sl
->name
);
2103 /* Drop admin file */
2104 xunlink_args("%s/%s", admdir
, a
->master_name
);
2108 alternative_remove(struct alternative
*a
, const char *current_choice
,
2111 char *new_choice
= NULL
;
2113 if (alternative_has_choice(a
, path
))
2114 alternative_remove_choice(a
, path
);
2116 verbose(_("alternative %s for %s not registered; not removing"),
2117 path
, a
->master_name
);
2119 if (current_choice
&& strcmp(current_choice
, path
) == 0) {
2120 struct fileset
*best
;
2122 /* Current choice is removed. */
2123 if (a
->status
== ALT_ST_MANUAL
) {
2124 /* And it was manual, switch to auto. */
2125 info(_("removing manually selected alternative "
2126 "- switching %s to auto mode"),
2128 alternative_set_status(a
, ALT_ST_AUTO
);
2130 best
= alternative_get_best(a
);
2132 new_choice
= xstrdup(best
->master_file
);
2139 alternative_has_broken_symlink(const char *linkname
, const char *ref_target
)
2143 target
= fsys_areadlink(linkname
);
2146 if (strcmp(target
, ref_target
) != 0) {
2155 alternative_has_broken_slave(struct slave_link
*sl
, struct fileset
*fs
)
2157 if (fileset_can_install_slave(fs
, sl
->name
)) {
2159 const char *sl_target
;
2161 /* Verify link -> /etc/alternatives/foo */
2162 wanted
= xasprintf("%s/%s", altdir
, sl
->name
);
2163 if (alternative_has_broken_symlink(sl
->link
, wanted
)) {
2168 /* Verify /etc/alternatives/foo -> file */
2169 sl_target
= fileset_get_slave(fs
, sl
->name
);
2170 if (alternative_has_broken_symlink(wanted
, sl_target
)) {
2179 /* Slave link must not exist. */
2180 if (alternative_path_classify(sl
->link
) != ALT_PATH_MISSING
)
2182 sl_altlnk
= xasprintf("%s/%s", altdir
, sl
->name
);
2183 if (alternative_path_classify(sl_altlnk
) != ALT_PATH_MISSING
) {
2193 static enum alternative_update_reason
2194 alternative_needs_update(struct alternative
*a
)
2196 enum alternative_update_reason reason
= ALT_UPDATE_NO
;
2197 const char *current
;
2200 struct slave_link
*sl
;
2202 /* Check master link */
2203 wanted
= xasprintf("%s/%s", altdir
, a
->master_name
);
2204 if (alternative_has_broken_symlink(a
->master_link
, wanted
)) {
2206 return ALT_UPDATE_LINK_BROKEN
;
2210 /* Stop if we have an unmanaged alternative */
2211 current
= alternative_get_current(a
);
2212 if (current
== NULL
)
2213 return ALT_UPDATE_LINK_BROKEN
;
2215 fs
= alternative_get_fileset(a
, current
);
2217 /* Stop if we do not have the choice. */
2219 return ALT_UPDATE_NO
;
2222 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
2223 if (alternative_has_broken_slave(sl
, fs
)) {
2225 reason
= ALT_UPDATE_SLAVE_CHANGED
;
2227 return ALT_UPDATE_LINK_BROKEN
;
2234 struct alternative_map
{
2235 struct alternative_map
*next
;
2238 struct alternative
*item
;
2241 static struct alternative_map
*
2242 alternative_map_new(const char *key
, struct alternative
*a
)
2244 struct alternative_map
*am
;
2246 am
= xmalloc(sizeof(*am
));
2254 static struct alternative
*
2255 alternative_map_find(struct alternative_map
*am
, const char *key
)
2257 for (; am
; am
= am
->next
)
2258 if (am
->key
&& strcmp(am
->key
, key
) == 0)
2265 alternative_map_add(struct alternative_map
*am
, const char *key
,
2266 struct alternative
*a
)
2270 if (am
->key
== NULL
) {
2274 struct alternative_map
*new = alternative_map_new(key
, a
);
2283 alternative_map_load_names(struct alternative_map
*alt_map_obj
)
2285 struct dirent
**table
;
2288 count
= altdb_get_namelist(&table
);
2289 for (i
= 0; i
< count
; i
++) {
2290 struct alternative
*a_new
= alternative_new(table
[i
]->d_name
);
2292 if (!alternative_load(a_new
, ALTDB_LAX_PARSER
)) {
2293 alternative_free(a_new
);
2296 alternative_map_add(alt_map_obj
, a_new
->master_name
, a_new
);
2298 alternative_unref(a_new
);
2300 altdb_free_namelist(table
, count
);
2304 alternative_map_load_tree(struct alternative_map
*alt_map_links
,
2305 struct alternative_map
*alt_map_parent
)
2307 struct dirent
**table
;
2310 count
= altdb_get_namelist(&table
);
2311 for (i
= 0; i
< count
; i
++) {
2312 struct slave_link
*sl
;
2313 struct alternative
*a_new
= alternative_new(table
[i
]->d_name
);
2315 if (!alternative_load(a_new
, ALTDB_LAX_PARSER
)) {
2316 alternative_free(a_new
);
2319 alternative_map_add(alt_map_links
, a_new
->master_link
, a_new
);
2320 alternative_map_add(alt_map_parent
, a_new
->master_name
, a_new
);
2321 for (sl
= a_new
->slaves
; sl
; sl
= sl
->next
) {
2322 alternative_map_add(alt_map_links
, sl
->link
, a_new
);
2323 alternative_map_add(alt_map_parent
, sl
->name
, a_new
);
2326 alternative_unref(a_new
);
2328 altdb_free_namelist(table
, count
);
2332 alternative_map_free(struct alternative_map
*am
)
2334 struct alternative_map
*am_next
;
2339 alternative_free(am
->item
);
2346 alternative_set_manual(struct alternative
*a
, const char *path
)
2348 char *new_choice
= NULL
;
2350 if (alternative_has_choice(a
, path
))
2351 new_choice
= xstrdup(path
);
2353 error(_("alternative %s for %s not registered; "
2354 "not setting"), path
, a
->master_name
);
2355 alternative_set_status(a
, ALT_ST_MANUAL
);
2361 alternative_set_auto(struct alternative
*a
)
2363 char *new_choice
= NULL
;
2365 alternative_set_status(a
, ALT_ST_AUTO
);
2366 if (alternative_choices_count(a
) == 0)
2367 info(_("there is no program which provides %s"),
2370 new_choice
= xstrdup(alternative_get_best(a
)->master_file
);
2376 get_argv_string(int argc
, char **argv
)
2378 static char string
[2048];
2384 for (i
= 1; i
< argc
; i
++) {
2385 size_t arg_len
= strlen(argv
[i
]);
2387 if (cur_len
+ arg_len
+ 2 > sizeof(string
))
2390 strcpy(string
+ cur_len
, " ");
2393 strcpy(string
+ cur_len
, argv
[i
]);
2401 alternative_select_mode(struct alternative
*a
, const char *current_choice
)
2403 if (current_choice
) {
2404 /* Detect manually modified alternative, switch to manual. */
2405 if (!alternative_has_choice(a
, current_choice
)) {
2406 if (fsys_pathname_is_missing(current_choice
)) {
2407 warning(_("%s%s/%s is dangling; it will be updated "
2408 "with best choice"), instdir
, altdir
,
2410 alternative_set_status(a
, ALT_ST_AUTO
);
2411 } else if (a
->status
!= ALT_ST_MANUAL
) {
2412 warning(_("%s%s/%s has been changed (manually or by "
2413 "a script); switching to manual "
2414 "updates only"), instdir
, altdir
,
2416 alternative_set_status(a
, ALT_ST_MANUAL
);
2420 /* Lack of alternative link => automatic mode. */
2421 verbose(_("setting up automatic selection of %s"),
2423 alternative_set_status(a
, ALT_ST_AUTO
);
2428 alternative_evolve_slave(struct alternative
*a
, const char *cur_choice
,
2429 struct slave_link
*sl
, struct fileset
*fs
)
2431 struct slave_link
*sl_old
;
2432 char *new_file
= NULL
;
2433 const char *old
, *new;
2435 sl_old
= alternative_get_slave(a
, sl
->name
);
2436 if (sl_old
== NULL
) {
2444 if (cur_choice
&& strcmp(cur_choice
, fs
->master_file
) == 0) {
2445 new_file
= xstrdup(fileset_get_slave(fs
, sl
->name
));
2449 lnk
= xasprintf("%s/%s", altdir
, sl
->name
);
2450 new_file
= fsys_areadlink(lnk
);
2453 if (strcmp(old
, new) != 0 &&
2454 alternative_path_classify(old
) == ALT_PATH_SYMLINK
) {
2455 bool rename_link
= false;
2458 rename_link
= !fsys_pathname_is_missing(new_file
);
2461 info(_("renaming %s slave link from %s%s to %s%s"),
2462 sl
->name
, instdir
, old
, instdir
, new);
2474 alternative_evolve(struct alternative
*a
, struct alternative
*b
,
2475 const char *cur_choice
, struct fileset
*fs
)
2477 struct slave_link
*sl
;
2480 is_link
= alternative_path_classify(a
->master_link
) == ALT_PATH_SYMLINK
;
2481 if (is_link
&& strcmp(a
->master_link
, b
->master_link
) != 0) {
2482 info(_("renaming %s link from %s%s to %s%s"), b
->master_name
,
2483 instdir
, a
->master_link
, instdir
, b
->master_link
);
2484 fsys_mv(a
->master_link
, b
->master_link
);
2486 alternative_set_link(a
, b
->master_link
);
2488 /* Check if new slaves have been added, or existing
2490 for (sl
= b
->slaves
; sl
; sl
= sl
->next
) {
2491 alternative_evolve_slave(a
, cur_choice
, sl
, fs
);
2492 alternative_copy_slave(a
, sl
);
2497 alternative_install(struct alternative
**aptr
, struct alternative
*inst_alt
,
2498 const char *current_choice
, struct fileset
*fileset
)
2500 struct alternative
*a
= *aptr
;
2501 char *new_choice
= NULL
;
2503 if (a
->master_link
) {
2504 /* Alternative already exists, check if anything got
2506 alternative_evolve(a
, inst_alt
, current_choice
, fileset
);
2507 alternative_free(inst_alt
);
2509 /* Alternative doesn't exist, create from parameters. */
2510 alternative_free(a
);
2511 *aptr
= a
= inst_alt
;
2513 alternative_add_choice(a
, fileset
);
2514 if (a
->status
== ALT_ST_AUTO
) {
2515 new_choice
= xstrdup(alternative_get_best(a
)->master_file
);
2517 verbose(_("automatic updates of %s/%s are disabled; "
2518 "leaving it alone"), altdir
, a
->master_name
);
2519 verbose(_("to return to automatic updates use "
2520 "'%s --auto %s'"), PROGNAME
, a
->master_name
);
2526 alternative_update(struct alternative
*a
,
2527 const char *current_choice
, const char *new_choice
)
2529 enum alternative_update_reason reason
;
2531 /* No choice left, remove everything. */
2532 if (!alternative_choices_count(a
)) {
2533 log_msg("link group %s fully removed", a
->master_name
);
2534 alternative_remove_files(a
);
2538 /* New choice wanted. */
2540 (!current_choice
|| strcmp(new_choice
, current_choice
) != 0)) {
2541 log_msg("link group %s updated to point to %s", a
->master_name
,
2543 if (a
->status
== ALT_ST_AUTO
)
2544 info(_("using %s to provide %s (%s) in auto mode"),
2545 new_choice
, a
->master_link
, a
->master_name
);
2547 info(_("using %s to provide %s (%s) in manual mode"),
2548 new_choice
, a
->master_link
, a
->master_name
);
2549 debug("prepare_install(%s)", new_choice
);
2550 alternative_prepare_install(a
, new_choice
);
2551 } else if ((reason
= alternative_needs_update(a
))) {
2552 if (reason
== ALT_UPDATE_SLAVE_CHANGED
) {
2553 log_msg("link group %s updated with changed slaves",
2555 info(_("updating alternative %s "
2556 "because link group %s has changed slave links"),
2557 current_choice
, a
->master_name
);
2559 log_msg("auto-repair link group %s", a
->master_name
);
2560 warning(_("forcing reinstallation of alternative %s "
2561 "because link group %s is broken"),
2562 current_choice
, a
->master_name
);
2565 if (current_choice
&& !alternative_has_choice(a
, current_choice
)) {
2566 struct fileset
*best
= alternative_get_best(a
);
2568 warning(_("current alternative %s is unknown, "
2569 "switching to %s for link group %s"),
2570 current_choice
, best
->master_file
,
2572 current_choice
= best
->master_file
;
2573 alternative_set_status(a
, ALT_ST_AUTO
);
2577 alternative_prepare_install(a
, current_choice
);
2580 /* Save administrative file if needed. */
2582 debug("%s is modified and will be saved", a
->master_name
);
2583 alternative_save(a
);
2586 /* Replace all symlinks in one pass. */
2587 alternative_commit(a
);
2591 alternative_config_all(void)
2593 struct alternative_map
*alt_map_obj
;
2594 struct alternative_map
*am
;
2596 alt_map_obj
= alternative_map_new(NULL
, NULL
);
2597 alternative_map_load_names(alt_map_obj
);
2599 for (am
= alt_map_obj
; am
&& am
->item
; am
= am
->next
) {
2600 const char *current_choice
;
2603 current_choice
= alternative_get_current(am
->item
);
2604 alternative_select_mode(am
->item
, current_choice
);
2606 new_choice
= alternative_config(am
->item
, current_choice
);
2608 alternative_update(am
->item
, current_choice
, new_choice
);
2613 alternative_map_free(alt_map_obj
);
2617 alternative_get_selections(void)
2619 struct alternative_map
*alt_map_obj
;
2620 struct alternative_map
*am
;
2622 alt_map_obj
= alternative_map_new(NULL
, NULL
);
2623 alternative_map_load_names(alt_map_obj
);
2625 for (am
= alt_map_obj
; am
&& am
->item
; am
= am
->next
) {
2626 const char *current
;
2628 current
= alternative_get_current(am
->item
);
2629 printf("%-30s %-8s %s\n", am
->key
,
2630 alternative_status_string(am
->item
->status
),
2631 current
? current
: "");
2634 alternative_map_free(alt_map_obj
);
2638 alternative_set_selection(struct alternative_map
*all
, const char *name
,
2639 const char *status
, const char *choice
)
2641 struct alternative
*a
;
2643 debug("set_selection(%s, %s, %s)", name
, status
, choice
);
2644 a
= alternative_map_find(all
, name
);
2646 char *new_choice
= NULL
;
2648 if (strcmp(status
, "auto") == 0) {
2649 info(_("selecting alternative %s as auto"), name
);
2650 new_choice
= alternative_set_auto(a
);
2651 } else if (alternative_has_choice(a
, choice
)) {
2652 info(_("selecting alternative %s as choice %s"), name
,
2654 new_choice
= alternative_set_manual(a
, choice
);
2656 info(_("alternative %s unchanged because choice "
2657 "%s is not available"), name
, choice
);
2661 const char *current_choice
;
2663 current_choice
= alternative_get_current(a
);
2664 alternative_select_mode(a
, current_choice
);
2666 alternative_update(a
, current_choice
, new_choice
);
2671 info(_("skip unknown alternative %s"), name
);
2676 alternative_set_selections(FILE *input
, const char *desc
)
2678 struct alternative_map
*alt_map_obj
;
2680 alt_map_obj
= alternative_map_new(NULL
, NULL
);
2681 alternative_map_load_names(alt_map_obj
);
2684 char line
[1024], *res
, *name
, *status
, *choice
;
2688 /* Can't use scanf("%s %s %s") because choice can
2689 * contain a space */
2690 res
= fgets(line
, sizeof(line
), input
);
2691 if (res
== NULL
&& errno
) {
2692 syserr(_("read error in %.250s"), desc
);
2693 } else if (res
== NULL
) {
2697 if (len
== 0 || line
[len
- 1] != '\n') {
2698 error(_("line too long or not terminated while "
2699 "trying to read %s"), desc
);
2701 line
[len
- 1] = '\0';
2704 /* Delimit name string in line */
2707 while (i
< len
&& !isblank(line
[i
]))
2710 info(_("skip invalid selection line: %s"), line
);
2714 while (i
< len
&& isblank(line
[i
]))
2717 /* Delimit status string in line */
2719 while (i
< len
&& !isblank(line
[i
]))
2722 info(_("skip invalid selection line: %s"), line
);
2726 while (i
< len
&& isblank(line
[i
]))
2729 /* Delimit choice string in the line */
2731 info(_("skip invalid selection line: %s"), line
);
2736 alternative_set_selection(alt_map_obj
, name
, status
, choice
);
2739 alternative_map_free(alt_map_obj
);
2743 alternative_check_name(const char *name
)
2745 if (strpbrk(name
, "/ \t"))
2746 error(_("alternative name (%s) must not contain '/' "
2747 "and spaces"), name
);
2751 alternative_check_link(const char *linkname
)
2753 if (linkname
[0] != '/')
2754 error(_("alternative link is not absolute as it should be: %s"),
2759 alternative_check_path(const char *file
)
2761 if (!file
|| file
[0] != '/')
2762 error(_("alternative path is not absolute as it should be: %s"),
2767 * Check the alternative installation arguments.
2769 * That the caller doesn't mix links between alternatives, doesn't mix
2770 * alternatives between slave/master, and that the various parameters
2774 alternative_check_install_args(struct alternative
*inst_alt
,
2775 struct fileset
*fileset
)
2777 struct alternative_map
*alt_map_links
, *alt_map_parent
;
2778 struct alternative
*found
;
2779 struct slave_link
*sl
;
2781 alternative_check_name(inst_alt
->master_name
);
2782 alternative_check_link(inst_alt
->master_link
);
2783 alternative_check_path(fileset
->master_file
);
2785 /* Load information about all alternatives to check for mistakes. */
2786 alt_map_links
= alternative_map_new(NULL
, NULL
);
2787 alt_map_parent
= alternative_map_new(NULL
, NULL
);
2788 alternative_map_load_tree(alt_map_links
, alt_map_parent
);
2790 found
= alternative_map_find(alt_map_parent
, inst_alt
->master_name
);
2791 if (found
&& strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2792 error(_("alternative %s can't be master: it is a slave of %s"),
2793 inst_alt
->master_name
, found
->master_name
);
2796 found
= alternative_map_find(alt_map_links
, inst_alt
->master_link
);
2797 if (found
&& strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2798 found
= alternative_map_find(alt_map_parent
,
2799 found
->master_name
);
2800 error(_("alternative link %s is already managed by %s"),
2801 inst_alt
->master_link
, found
->master_name
);
2804 if (fsys_pathname_is_missing(fileset
->master_file
))
2805 error(_("alternative path %s%s doesn't exist"),
2806 instdir
, fileset
->master_file
);
2808 for (sl
= inst_alt
->slaves
; sl
; sl
= sl
->next
) {
2809 const char *file
= fileset_get_slave(fileset
, sl
->name
);
2811 alternative_check_name(sl
->name
);
2812 alternative_check_link(sl
->link
);
2813 alternative_check_path(file
);
2815 found
= alternative_map_find(alt_map_parent
, sl
->name
);
2817 strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2818 if (strcmp(found
->master_name
, sl
->name
) == 0)
2819 error(_("alternative %s can't be slave of %s: "
2820 "it is a master alternative"),
2821 sl
->name
, inst_alt
->master_name
);
2823 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
);
2829 found
= alternative_map_find(alt_map_links
, sl
->link
);
2831 strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2832 error(_("alternative link %s is already "
2833 "managed by %s"), sl
->link
,
2834 found
->master_name
);
2837 struct slave_link
*sl2
;
2839 for (sl2
= found
->slaves
; sl2
; sl2
= sl2
->next
)
2840 if (strcmp(sl2
->link
, sl
->link
) == 0)
2842 if (sl2
&& strcmp(sl2
->name
, sl
->name
) != 0)
2843 error(_("alternative link %s is already "
2844 "managed by %s (slave of %s)"),
2845 sl
->link
, sl2
->name
,
2846 found
->master_name
);
2850 alternative_map_free(alt_map_links
);
2851 alternative_map_free(alt_map_parent
);
2859 set_action(enum action new_action
)
2862 badusage(_("two commands specified: --%s and --%s"),
2863 action_names
[action
].name
, action_names
[new_action
].name
);
2864 action
= new_action
;
2868 set_action_from_name(const char *new_action
)
2872 for (i
= 0; i
< array_count(action_names
); i
++) {
2873 if (strcmp(new_action
, action_names
[i
].name
) == 0) {
2874 set_action(action_names
[i
].action
);
2879 assert(!"unknown action name");
2883 set_rootdir(const char *dir
)
2885 instdir
= fsys_set_dir(dir
);
2887 log_file
= fsys_get_path(LOGDIR
"/alternatives.log");
2888 altdir
= SYSCONFDIR
"/alternatives";
2890 admdir
= fsys_gen_admindir();
2898 const char *basedir_env
;
2900 /* Try to get the admindir from an environment variable, usually set
2901 * by the system package manager. */
2902 basedir_env
= getenv(ADMINDIR_ENVVAR
);
2904 return xasprintf("%s%s", basedir_env
, "/alternatives");
2906 return fsys_gen_admindir();
2909 #define MISSING_ARGS(nb) (argc < i + nb + 1)
2912 main(int argc
, char **argv
)
2914 /* Alternative worked on. */
2915 struct alternative
*a
= NULL
;
2916 /* Alternative to install. */
2917 struct alternative
*inst_alt
= NULL
;
2918 /* Set of files to install in the alternative. */
2919 struct fileset
*fileset
= NULL
;
2920 /* Path of alternative we are offering. */
2921 const char *path
= NULL
;
2922 const char *current_choice
= NULL
;
2923 char *new_choice
= NULL
;
2924 bool modifies_alt
= false;
2925 bool modifies_sys
= false;
2928 setlocale(LC_ALL
, "");
2929 bindtextdomain(PACKAGE
, LOCALEDIR
);
2930 textdomain(PACKAGE
);
2935 instdir
= fsys_set_dir(NULL
);
2936 admdir
= admindir_init();
2937 log_file
= fsys_get_path(LOGDIR
"/alternatives.log");
2939 if (setvbuf(stdout
, NULL
, _IONBF
, 0))
2940 syserr("setvbuf failed");
2942 prog_path
= argv
[0];
2944 for (i
= 1; i
< argc
; i
++) {
2945 if (strstr(argv
[i
], "--") != argv
[i
]) {
2946 error(_("unknown argument '%s'"), argv
[i
]);
2947 } else if (strcmp("--help", argv
[i
]) == 0) {
2950 } else if (strcmp("--version", argv
[i
]) == 0) {
2953 } else if (strcmp("--quiet", argv
[i
]) == 0) {
2954 opt_verbose
= OUTPUT_QUIET
;
2955 } else if (strcmp("--verbose", argv
[i
]) == 0) {
2956 opt_verbose
= OUTPUT_VERBOSE
;
2957 } else if (strcmp("--debug", argv
[i
]) == 0) {
2958 opt_verbose
= OUTPUT_DEBUG
;
2959 } else if (strcmp("--install", argv
[i
]) == 0) {
2960 const char *alink
, *aname
, *apath
;
2961 char *prio_str
, *prio_end
;
2964 set_action(ACTION_INSTALL
);
2965 if (MISSING_ARGS(4))
2966 badusage(_("--%s needs <link> <name> <path> "
2967 "<priority>"), argv
[i
] + 2);
2969 alink
= argv
[i
+ 1];
2970 aname
= argv
[i
+ 2];
2971 apath
= argv
[i
+ 3];
2972 prio_str
= argv
[i
+ 4];
2974 if (strcmp(alink
, apath
) == 0)
2975 badusage(_("<link> '%s' is the same as <path>"),
2978 prio
= strtol(prio_str
, &prio_end
, 10);
2979 if (prio_str
== prio_end
|| *prio_end
!= '\0')
2980 badusage(_("priority '%s' must be an integer"),
2982 if (prio
< INT_MIN
|| prio
> INT_MAX
|| errno
== ERANGE
)
2983 badusage(_("priority '%s' is out of range"),
2986 a
= alternative_new(aname
);
2987 inst_alt
= alternative_new(aname
);
2988 alternative_set_status(inst_alt
, ALT_ST_AUTO
);
2989 alternative_set_link(inst_alt
, alink
);
2990 fileset
= fileset_new(apath
, prio
);
2993 } else if (strcmp("--remove", argv
[i
]) == 0 ||
2994 strcmp("--set", argv
[i
]) == 0) {
2995 set_action_from_name(argv
[i
] + 2);
2996 if (MISSING_ARGS(2))
2997 badusage(_("--%s needs <name> <path>"), argv
[i
] + 2);
2999 a
= alternative_new(argv
[i
+ 1]);
3002 alternative_check_name(a
->master_name
);
3003 alternative_check_path(path
);
3006 } else if (strcmp("--display", argv
[i
]) == 0 ||
3007 strcmp("--query", argv
[i
]) == 0 ||
3008 strcmp("--auto", argv
[i
]) == 0 ||
3009 strcmp("--config", argv
[i
]) == 0 ||
3010 strcmp("--list", argv
[i
]) == 0 ||
3011 strcmp("--remove-all", argv
[i
]) == 0) {
3012 set_action_from_name(argv
[i
] + 2);
3013 if (MISSING_ARGS(1))
3014 badusage(_("--%s needs <name>"), argv
[i
] + 2);
3015 a
= alternative_new(argv
[i
+ 1]);
3017 alternative_check_name(a
->master_name
);
3020 } else if (strcmp("--all", argv
[i
]) == 0 ||
3021 strcmp("--get-selections", argv
[i
]) == 0 ||
3022 strcmp("--set-selections", argv
[i
]) == 0) {
3023 set_action_from_name(argv
[i
] + 2);
3024 } else if (strcmp("--slave", argv
[i
]) == 0) {
3025 const char *slink
, *sname
, *spath
;
3026 struct slave_link
*sl
;
3028 if (action
!= ACTION_INSTALL
)
3029 badusage(_("--%s only allowed with --%s"),
3030 argv
[i
] + 2, "install");
3031 if (MISSING_ARGS(3))
3032 badusage(_("--%s needs <link> <name> <path>"),
3035 slink
= argv
[i
+ 1];
3036 sname
= argv
[i
+ 2];
3037 spath
= argv
[i
+ 3];
3039 if (strcmp(slink
, spath
) == 0)
3040 badusage(_("<link> '%s' is the same as <path>"),
3042 if (strcmp(inst_alt
->master_name
, sname
) == 0)
3043 badusage(_("<name> '%s' is both primary and slave"),
3045 if (strcmp(slink
, inst_alt
->master_link
) == 0)
3046 badusage(_("<link> '%s' is both primary and slave"),
3048 if (alternative_has_slave(inst_alt
, sname
))
3049 badusage(_("duplicate slave <name> '%s'"), sname
);
3051 for (sl
= inst_alt
->slaves
; sl
; sl
= sl
->next
) {
3052 const char *linkname
= sl
->link
;
3053 if (linkname
== NULL
)
3055 if (strcmp(linkname
, slink
) == 0)
3056 badusage(_("duplicate slave <link> '%s'"),
3060 alternative_add_slave(inst_alt
, sname
, slink
);
3061 fileset_add_slave(fileset
, sname
, spath
);
3064 } else if (strcmp("--log", argv
[i
]) == 0) {
3065 if (MISSING_ARGS(1))
3066 badusage(_("--%s needs a <file> argument"),
3069 log_file
= fsys_get_path(argv
[i
+ 1]);
3071 } else if (strcmp("--altdir", argv
[i
]) == 0) {
3072 if (MISSING_ARGS(1))
3073 badusage(_("--%s needs a <directory> argument"),
3075 altdir
= argv
[i
+ 1];
3078 /* If altdir is below instdir, convert it to a relative
3079 * path, as we will prepend instdir as needed. */
3080 if (strncmp(altdir
, instdir
, instdir_len
) == 0)
3081 altdir
+= instdir_len
;
3082 } else if (strcmp("--admindir", argv
[i
]) == 0) {
3083 if (MISSING_ARGS(1))
3084 badusage(_("--%s needs a <directory> argument"),
3087 admdir
= xstrdup(argv
[i
+ 1]);
3089 } else if (strcmp("--instdir", argv
[i
]) == 0) {
3090 if (MISSING_ARGS(1))
3091 badusage(_("--%s needs a <directory> argument"),
3093 fsys_set_dir(argv
[i
+ 1]);
3096 /* If altdir is below instdir, convert it to a relative
3097 * path, as we will prepend instdir as needed. */
3098 if (strncmp(altdir
, instdir
, instdir_len
) == 0)
3099 altdir
+= instdir_len
;
3100 } else if (strcmp("--root", argv
[i
]) == 0) {
3101 if (MISSING_ARGS(1))
3102 badusage(_("--%s needs a <directory> argument"),
3104 set_rootdir(argv
[i
+ 1]);
3106 } else if (strcmp("--skip-auto", argv
[i
]) == 0) {
3108 } else if (strcmp("--force", argv
[i
]) == 0) {
3111 badusage(_("unknown option '%s'"), argv
[i
]);
3115 if (action
== ACTION_NONE
)
3116 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");
3122 debug("root=%s admdir=%s altdir=%s", instdir
, admdir
, altdir
);
3124 /* The following actions might modify the current alternative. */
3125 if (action
== ACTION_SET
||
3126 action
== ACTION_AUTO
||
3127 action
== ACTION_CONFIG
||
3128 action
== ACTION_REMOVE
||
3129 action
== ACTION_REMOVE_ALL
||
3130 action
== ACTION_INSTALL
)
3131 modifies_alt
= true;
3133 /* The following actions might modify the system somehow. */
3135 action
== ACTION_CONFIG_ALL
||
3136 action
== ACTION_SET_SELECTIONS
)
3137 modifies_sys
= true;
3139 if (action
== ACTION_INSTALL
)
3140 alternative_check_install_args(inst_alt
, fileset
);
3142 if (action
== ACTION_DISPLAY
||
3143 action
== ACTION_QUERY
||
3144 action
== ACTION_LIST
||
3145 action
== ACTION_SET
||
3146 action
== ACTION_AUTO
||
3147 action
== ACTION_CONFIG
||
3148 action
== ACTION_REMOVE_ALL
) {
3149 /* Load the alternative info, stop on failure. */
3150 if (!alternative_load(a
, ALTDB_WARN_PARSER
))
3151 error(_("no alternatives for %s"), a
->master_name
);
3152 } 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 if (!alternative_load(a
, ALTDB_WARN_PARSER
)) {
3157 verbose(_("no alternatives for %s"), a
->master_name
);
3158 alternative_free(a
);
3163 } else if (action
== ACTION_INSTALL
) {
3164 /* Load the alternative info, ignore failures. */
3165 alternative_load(a
, ALTDB_WARN_PARSER
);
3169 log_msg("run with %s", get_argv_string(argc
, argv
));
3172 current_choice
= alternative_get_current(a
);
3173 alternative_select_mode(a
, current_choice
);
3176 /* Handle actions. */
3177 if (action
== ACTION_CONFIG_ALL
) {
3178 alternative_config_all();
3179 } else if (action
== ACTION_GET_SELECTIONS
) {
3180 alternative_get_selections();
3181 } else if (action
== ACTION_SET_SELECTIONS
) {
3182 alternative_set_selections(stdin
, _("<standard input>"));
3183 } else if (action
== ACTION_DISPLAY
) {
3184 alternative_display_user(a
);
3185 } else if (action
== ACTION_QUERY
) {
3186 alternative_display_query(a
);
3187 } else if (action
== ACTION_LIST
) {
3188 alternative_display_list(a
);
3189 } else if (action
== ACTION_SET
) {
3190 new_choice
= alternative_set_manual(a
, path
);
3191 } else if (action
== ACTION_AUTO
) {
3192 new_choice
= alternative_set_auto(a
);
3193 } else if (action
== ACTION_CONFIG
) {
3194 new_choice
= alternative_config(a
, current_choice
);
3195 } else if (action
== ACTION_REMOVE
) {
3196 new_choice
= alternative_remove(a
, current_choice
, path
);
3197 } else if (action
== ACTION_REMOVE_ALL
) {
3198 alternative_choices_free(a
);
3199 } else if (action
== ACTION_INSTALL
) {
3200 new_choice
= alternative_install(&a
, inst_alt
, current_choice
,
3205 alternative_update(a
, current_choice
, new_choice
);
3208 alternative_free(a
);