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(_("%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
, ...)
504 if (fh_log
== NULL
) {
505 fh_log
= fopen(log_file
, "a");
506 if (fh_log
== NULL
&& errno
== ENOENT
) {
507 char *log_dir
= xdirname(log_file
);
509 if (make_path(log_dir
, 0755) < 0)
510 syserr(_("cannot create log directory '%s'"),
514 fh_log
= fopen(log_file
, "a");
516 if (fh_log
== NULL
&& errno
!= EACCES
)
517 syserr(_("cannot append to '%s'"), log_file
);
527 if (localtime_r(&now
, &tm
) == NULL
)
528 syserr(_("cannot get local time to log into '%s'"), log_file
);
529 strftime(timestamp
, sizeof(timestamp
), "%Y-%m-%d %H:%M:%S",
531 fprintf(fh_log
, "%s %s: ", PROGNAME
, timestamp
);
533 vfprintf(fh_log
, fmt
, args
);
535 fprintf(fh_log
, "\n");
540 * Filesystem access for alternative handling.
544 fsys_get_path(const char *pathpart
)
546 return xasprintf("%s%s", instdir
, pathpart
);
550 fsys_set_dir(const char *dir
)
553 const char *instdir_env
;
555 instdir_env
= getenv(INSTDIR_ENVVAR
);
562 instdir_len
= strlen(dir
);
568 fsys_gen_admindir(void)
570 return fsys_get_path(ADMINDIR
"/alternatives");
574 fsys_pathname_is_missing(const char *pathname
)
579 root_pathname
= fsys_get_path(pathname
);
582 if (stat(root_pathname
, &st
) < 0 && errno
!= ENOENT
)
583 syserr(_("cannot stat file '%s'"), root_pathname
);
594 fsys_lstat(const char *linkname
, struct stat
*st
)
599 root_linkname
= fsys_get_path(linkname
);
602 rc
= lstat(root_linkname
, st
);
610 fsys_areadlink(const char *linkname
)
615 root_linkname
= fsys_get_path(linkname
);
616 target
= areadlink(root_linkname
);
623 fsys_xreadlink(const char *linkname
)
627 buf
= fsys_areadlink(linkname
);
629 syserr(_("unable to read link '%s%.255s'"), instdir
, linkname
);
635 fsys_set_ref_time(const char *linkname
, const char *target
)
638 /* If the symlink did not exist, then copy the timestamps
639 * from the target. This is needed so we can get reproducible
640 * installations, for programs that track these timestamps on
641 * their databases. */
643 struct timeval tv
[2];
646 if (fsys_lstat(target
, &st
) < 0) {
648 syserr(_("unable to get file '%s%s' metadata"),
653 tv
[0].tv_sec
= st
.st_mtime
;
655 tv
[1].tv_sec
= st
.st_mtime
;
658 root_linkname
= fsys_get_path(linkname
);
659 if (lutimes(root_linkname
, tv
) < 0 && errno
!= ENOSYS
)
660 syserr(_("cannot set symlink '%s' timestamp"), root_linkname
);
666 fsys_symlink(const char *filename
, const char *linkname
)
670 root_linkname
= fsys_get_path(linkname
);
672 if (unlink(root_linkname
) < 0 && errno
!= ENOENT
)
673 syserr(_("unable to remove '%s'"), root_linkname
);
675 if (symlink(filename
, root_linkname
))
676 syserr(_("error creating symbolic link '%.255s'"), root_linkname
);
682 fsys_mv(const char *src
, const char *dst
)
687 root_src
= fsys_get_path(src
);
688 root_dst
= fsys_get_path(dst
);
690 xrename(root_src
, root_dst
);
697 fsys_rm(const char *f
)
701 root_f
= fsys_get_path(f
);
703 if (unlink(root_f
) < 0 && errno
!= ENOENT
)
704 syserr(_("unable to remove '%s'"), root_f
);
709 static void LIBCOMPAT_ATTR_PRINTF(1)
710 fsys_rm_args(const char *fmt
, ...)
716 path
= xvasprintf(fmt
, args
);
728 struct fileset
*next
;
734 struct slave_file
*next
;
740 static struct fileset
*
741 fileset_new(const char *master_file
, int prio
)
745 fs
= xmalloc(sizeof(*fs
));
747 fs
->master_file
= xstrdup(master_file
);
755 fileset_free(struct fileset
*fs
)
757 struct slave_file
*slave
, *next
;
759 free(fs
->master_file
);
760 for (slave
= fs
->slaves
; slave
; slave
= next
) {
770 fileset_add_slave(struct fileset
*fs
, const char *name
, const char *file
)
772 struct slave_file
*sl
, *cur
, *prev
= NULL
;
774 /* Replace existing first */
775 for (cur
= fs
->slaves
; cur
; cur
= cur
->next
) {
776 if (strcmp(cur
->name
, name
) == 0) {
778 cur
->file
= xstrdup(file
);
784 /* Otherwise add new at the end */
785 sl
= xmalloc(sizeof(*sl
));
787 sl
->name
= xstrdup(name
);
788 sl
->file
= xstrdup(file
);
796 fileset_get_slave(struct fileset
*fs
, const char *name
)
798 struct slave_file
*slave
;
800 for (slave
= fs
->slaves
; slave
; slave
= slave
->next
) {
801 if (strcmp(slave
->name
, name
) == 0)
809 fileset_has_slave(struct fileset
*fs
, const char *name
)
811 const char *file
= fileset_get_slave(fs
, name
);
816 return file
[0] != '\0';
820 fileset_can_install_slave(struct fileset
*fs
, const char *slave_name
)
822 /* Decide whether the slave alternative must be setup */
823 if (fileset_has_slave(fs
, slave_name
)) {
824 const char *slave
= fileset_get_slave(fs
, slave_name
);
826 if (!fsys_pathname_is_missing(slave
))
834 struct slave_link
*next
;
840 struct commit_operation
{
841 struct commit_operation
*next
;
854 enum alternative_update_reason
{
856 ALT_UPDATE_SLAVE_CHANGED
,
857 ALT_UPDATE_LINK_BROKEN
,
865 enum alternative_status
{
871 struct slave_link
*slaves
;
872 struct fileset
*choices
;
874 struct commit_operation
*commit_ops
;
882 slave_link_free(struct slave_link
*slave
)
890 commit_operation_free(struct commit_operation
*commit_op
)
892 free(commit_op
->arg_a
);
893 free(commit_op
->arg_b
);
897 static struct alternative
*
898 alternative_new(const char *name
)
900 struct alternative
*alt
;
902 alt
= xmalloc(sizeof(*alt
));
903 alt
->master_name
= xstrdup(name
);
904 alt
->master_link
= NULL
;
906 alt
->status
= ALT_ST_UNKNOWN
;
909 alt
->commit_ops
= NULL
;
910 alt
->modified
= false;
911 alt
->known_current
= false;
918 alternative_ref(struct alternative
*a
)
924 alternative_unref(struct alternative
*a
)
926 return --a
->ref_count
== 0;
930 alternative_choices_free(struct alternative
*a
)
939 a
->choices
= fs
->next
;
945 alternative_commit_operations_free(struct alternative
*a
)
947 struct commit_operation
*op
;
949 while (a
->commit_ops
) {
951 a
->commit_ops
= op
->next
;
952 commit_operation_free(op
);
957 alternative_reset(struct alternative
*alt
)
959 struct slave_link
*slave
;
963 free(alt
->master_link
);
964 alt
->master_link
= NULL
;
965 while (alt
->slaves
) {
967 alt
->slaves
= slave
->next
;
968 slave_link_free(slave
);
970 alternative_choices_free(alt
);
971 alternative_commit_operations_free(alt
);
972 alt
->modified
= false;
973 alt
->known_current
= false;
977 alternative_free(struct alternative
*alt
)
979 if (!alternative_unref(alt
))
982 alternative_reset(alt
);
983 free(alt
->master_name
);
988 alternative_choices_count(struct alternative
*alt
)
993 for (fs
= alt
->choices
; fs
; fs
= fs
->next
)
1000 alternative_slaves_count(struct alternative
*alt
)
1002 struct slave_link
*sl
;
1005 for (sl
= alt
->slaves
; sl
; sl
= sl
->next
)
1012 compare_fileset(const void *va
, const void *vb
)
1014 const struct fileset
*a
= *(const struct fileset
**)va
;
1015 const struct fileset
*b
= *(const struct fileset
**)vb
;
1017 assert(a
&& a
->master_file
);
1018 assert(b
&& b
->master_file
);
1020 return strcmp(a
->master_file
, b
->master_file
);
1024 compare_slave_link(const void *va
, const void *vb
)
1026 const struct slave_link
*a
= *(const struct slave_link
**)va
;
1027 const struct slave_link
*b
= *(const struct slave_link
**)vb
;
1029 assert(a
&& a
->name
);
1030 assert(b
&& b
->name
);
1032 return strcmp(a
->name
, b
->name
);
1036 alternative_sort_choices(struct alternative
*a
)
1039 struct fileset
**table
, *fs
;
1041 count
= alternative_choices_count(a
);
1042 if (count
< 2) /* Nothing to sort */
1045 /* Store objects in a table instead of a linked list */
1046 table
= xmalloc(sizeof(fs
) * count
);
1047 for (fs
= a
->choices
, i
= 0; fs
; fs
= fs
->next
) {
1048 assert(fs
->master_file
);
1052 qsort(table
, count
, sizeof(fs
), compare_fileset
);
1054 /* Rewrite the linked list from the sorted table */
1055 a
->choices
= fs
= table
[0];
1056 table
[count
- 1]->next
= NULL
;
1057 for (i
= 1; i
< count
; fs
= fs
->next
, i
++)
1058 fs
->next
= table
[i
];
1063 alternative_sort_slaves(struct alternative
*a
)
1066 struct slave_link
**table
, *sl
;
1068 count
= alternative_slaves_count(a
);
1069 if (count
< 2) /* Nothing to sort */
1072 /* Store objects in a table instead of a linked list */
1073 table
= xmalloc(sizeof(sl
) * count
);
1074 for (sl
= a
->slaves
, i
= 0; sl
; sl
= sl
->next
, i
++) {
1078 qsort(table
, count
, sizeof(sl
), compare_slave_link
);
1080 /* Rewrite the linked list from the sorted table */
1081 a
->slaves
= sl
= table
[0];
1082 table
[count
- 1]->next
= NULL
;
1083 for (i
= 1; i
< count
; sl
= sl
->next
, i
++)
1084 sl
->next
= table
[i
];
1088 static struct fileset
*
1089 alternative_get_fileset(struct alternative
*a
, const char *file
)
1093 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1094 if (strcmp(fs
->master_file
, file
) == 0)
1100 static struct slave_link
*
1101 alternative_get_slave(struct alternative
*a
, const char *name
)
1103 struct slave_link
*sl
;
1105 for (sl
= a
->slaves
; sl
; sl
= sl
->next
)
1106 if (strcmp(sl
->name
, name
) == 0)
1113 alternative_has_slave(struct alternative
*a
, const char *name
)
1115 return alternative_get_slave(a
, name
) != NULL
;
1119 alternative_has_choice(struct alternative
*a
, const char *file
)
1121 return alternative_get_fileset(a
, file
) != NULL
;
1125 alternative_add_choice(struct alternative
*a
, struct fileset
*fs
)
1127 struct fileset
*cur
, *prev
= NULL
;
1129 /* Replace if already existing */
1130 for (cur
= a
->choices
; cur
; cur
= cur
->next
) {
1131 if (strcmp(cur
->master_file
, fs
->master_file
) == 0) {
1132 fs
->next
= cur
->next
;
1139 /* XXX: Be smarter in detecting change? */
1146 /* Otherwise add at the end */
1155 static struct slave_link
*
1156 alternative_add_slave(struct alternative
*a
,
1157 const char *slave_name
, const char *slave_link
)
1159 struct slave_link
*sl
, *new;
1161 /* Replace if already existing */
1162 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1163 if (strcmp(sl
->name
, slave_name
) == 0) {
1165 sl
->link
= xstrdup(slave_link
);
1168 if (sl
->next
== NULL
)
1172 /* Otherwise create new and add at the end */
1173 new = xmalloc(sizeof(*new));
1174 new->name
= xstrdup(slave_name
);
1175 new->link
= xstrdup(slave_link
);
1176 new->updated
= false;
1187 alternative_copy_slave(struct alternative
*a
, struct slave_link
*sl
)
1189 struct slave_link
*sl_new
;
1191 sl_new
= alternative_add_slave(a
, sl
->name
, sl
->link
);
1192 sl_new
->updated
= sl
->updated
;
1196 alternative_status_string(enum alternative_status status
)
1198 return (status
== ALT_ST_AUTO
) ? "auto" : "manual";
1202 alternative_status_describe(enum alternative_status status
)
1204 return (status
== ALT_ST_AUTO
) ? _("auto mode") : _("manual mode");
1208 alternative_set_status(struct alternative
*a
, enum alternative_status status
)
1210 if (a
->status
== ALT_ST_UNKNOWN
|| status
!= a
->status
)
1213 if (a
->status
!= ALT_ST_UNKNOWN
&& status
!= a
->status
)
1214 log_msg("status of link group %s set to %s", a
->master_link
,
1215 alternative_status_string(status
));
1221 alternative_set_link(struct alternative
*a
, const char *linkname
)
1223 if (a
->master_link
== NULL
|| strcmp(linkname
, a
->master_link
) != 0)
1226 free(a
->master_link
);
1227 a
->master_link
= xstrdup(linkname
);
1231 alternative_remove_choice(struct alternative
*a
, const char *file
)
1233 struct fileset
*fs
, *fs_prev
;
1236 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1237 if (strcmp(fs
->master_file
, file
) != 0) {
1242 fs_prev
->next
= fs
->next
;
1244 a
->choices
= fs
->next
;
1254 * Alternatives Database Load/Store functions.
1257 enum LIBCOMPAT_ATTR_ENUM_FLAGS altdb_flags
{
1258 ALTDB_LAX_PARSER
= 1 << 0,
1259 ALTDB_WARN_PARSER
= 1 << 1,
1262 struct altdb_context
{
1265 enum altdb_flags flags
;
1267 void LIBCOMPAT_ATTR_NORET
1268 (*bad_format
)(struct altdb_context
*, const char *msg
);
1273 altdb_context_free(struct altdb_context
*ctx
)
1277 free(ctx
->filename
);
1280 static void LIBCOMPAT_ATTR_NORET
LIBCOMPAT_ATTR_PRINTF(2)
1281 altdb_bad_format(struct altdb_context
*ctx
, const char *format
, ...)
1286 va_start(args
, format
);
1287 msg
= xvasprintf(format
, args
);
1290 ctx
->bad_format(ctx
, msg
);
1292 /* This cannot happen, but just to make sure the bad_format() function
1293 * pointer is well implemented. */
1294 error("(internal) non returning bad-format function returned");
1298 altdb_filter_namelist(const struct dirent
*entry
)
1300 if (strcmp(entry
->d_name
, ".") == 0 ||
1301 strcmp(entry
->d_name
, "..") == 0 ||
1302 (strlen(entry
->d_name
) > strlen(ALT_TMP_EXT
) &&
1303 strcmp(entry
->d_name
+ strlen(entry
->d_name
) -
1304 strlen(ALT_TMP_EXT
), ALT_TMP_EXT
) == 0))
1310 altdb_get_namelist(struct dirent
***table
)
1314 count
= scandir(admdir
, table
, altdb_filter_namelist
, alphasort
);
1316 if (errno
!= ENOENT
)
1317 syserr(_("cannot scan directory '%.255s'"), admdir
);
1318 /* The directory does not exist, proceed anyway. */
1327 altdb_free_namelist(struct dirent
**table
, int n
)
1335 altdb_get_line(struct altdb_context
*ctx
, const char *name
)
1338 size_t len
, bufsz
, i
;
1341 buf
= xmalloc(bufsz
);
1343 for (i
= 0; true; i
+= strlen(line
)) {
1345 line
= fgets(buf
+ i
, bufsz
- i
, ctx
->fh
);
1347 if (strlen(buf
) < bufsz
- 1 || buf
[bufsz
- 2] == '\n')
1349 /* Need more space */
1351 buf
= realloc(buf
, bufsz
);
1353 error(_("failed to allocate memory"));
1357 altdb_bad_format(ctx
, _("unexpected end of file while trying "
1358 "to read %s"), name
);
1359 altdb_bad_format(ctx
, _("while reading %s: %s"),
1360 name
, strerror(errno
));
1364 if (len
== 0 || buf
[len
- 1] != '\n') {
1365 altdb_bad_format(ctx
, _("line not terminated while trying "
1366 "to read %s"), name
);
1368 line
[len
- 1] = '\0';
1373 static void LIBCOMPAT_ATTR_NORET
1374 altdb_parse_error(struct altdb_context
*ctx
, const char *msg
)
1376 error(_("%s corrupt: %s"), ctx
->filename
, msg
);
1379 static void LIBCOMPAT_ATTR_NORET
1380 altdb_parse_stop(struct altdb_context
*ctx
, const char *msg
)
1382 longjmp(ctx
->on_error
, 1);
1386 altdb_print_line(struct altdb_context
*ctx
, const char *line
)
1388 if (strchr(line
, '\n') != NULL
)
1389 error(_("newlines prohibited in update-alternatives files (%s)"),
1392 if (fprintf(ctx
->fh
, "%s\n", line
) < (int) strlen(line
) + 1)
1393 syserr(_("unable to write file '%s'"), ctx
->filename
);
1397 alternative_parse_slave(struct alternative
*a
, struct altdb_context
*ctx
)
1399 char *name
, *linkname
;
1400 struct slave_link
*sl
;
1402 name
= altdb_get_line(ctx
, _("slave name"));
1403 if (!strlen(name
)) { /* End of list */
1407 sl
= alternative_get_slave(a
, name
);
1410 altdb_bad_format(ctx
, _("duplicate slave name %s"), sl
->name
);
1413 linkname
= altdb_get_line(ctx
, _("slave link"));
1414 if (strcmp(linkname
, a
->master_link
) == 0) {
1417 altdb_bad_format(ctx
, _("slave link same as main link %s"),
1420 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1421 if (strcmp(linkname
, sl
->link
) == 0) {
1424 altdb_bad_format(ctx
, _("duplicate slave link %s"),
1429 alternative_add_slave(a
, name
, linkname
);
1437 alternative_parse_fileset(struct alternative
*a
, struct altdb_context
*ctx
)
1440 struct slave_link
*sl
;
1443 master_file
= altdb_get_line(ctx
, _("master file"));
1444 if (!strlen(master_file
)) { /* End of list */
1449 fs
= alternative_get_fileset(a
, master_file
);
1451 altdb_bad_format(ctx
, _("duplicate path %s"), master_file
);
1453 if (fsys_pathname_is_missing(master_file
)) {
1456 /* File not found - remove. */
1457 if (ctx
->flags
& ALTDB_WARN_PARSER
)
1458 warning(_("alternative %s (part of link group %s) "
1459 "doesn't exist; removing from list of "
1460 "alternatives"), master_file
, a
->master_name
);
1461 junk
= altdb_get_line(ctx
, _("priority"));
1463 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1464 junk
= altdb_get_line(ctx
, _("slave file"));
1467 ctx
->modified
= true;
1469 char *prio_str
, *prio_end
;
1472 prio_str
= altdb_get_line(ctx
, _("priority"));
1474 prio
= strtol(prio_str
, &prio_end
, 10);
1475 /* XXX: Leak master_file/prio_str on non-fatal error */
1476 if (prio_str
== prio_end
|| *prio_end
!= '\0')
1477 altdb_bad_format(ctx
, _("priority of %s: %s"),
1478 master_file
, prio_str
);
1479 if (prio
< INT_MIN
|| prio
> INT_MAX
|| errno
== ERANGE
)
1480 altdb_bad_format(ctx
,
1481 _("priority of %s is out of range: %s"),
1482 master_file
, prio_str
);
1485 fs
= fileset_new(master_file
, prio
);
1486 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1487 char *slave_file
= altdb_get_line(ctx
, _("slave file"));
1488 fileset_add_slave(fs
, sl
->name
, slave_file
);
1491 alternative_add_choice(a
, fs
);
1499 alternative_load(struct alternative
*a
, enum altdb_flags flags
)
1501 struct altdb_context ctx
;
1506 /* Initialize parse context */
1507 ctx
.modified
= false;
1509 if (flags
& ALTDB_LAX_PARSER
)
1510 ctx
.bad_format
= altdb_parse_stop
;
1512 ctx
.bad_format
= altdb_parse_error
;
1513 ctx
.filename
= xasprintf("%s/%s", admdir
, a
->master_name
);
1515 /* Open the alternative file. */
1516 ctx
.fh
= fopen(ctx
.filename
, "r");
1517 if (ctx
.fh
== NULL
) {
1518 if (errno
== ENOENT
) {
1519 altdb_context_free(&ctx
);
1523 syserr(_("unable to open file '%s'"), ctx
.filename
);
1526 if (setjmp(ctx
.on_error
)) {
1527 altdb_context_free(&ctx
);
1528 alternative_reset(a
);
1532 /* Verify the alternative is not empty. */
1533 if (fstat(fileno(ctx
.fh
), &st
) == -1)
1534 syserr(_("cannot stat file '%s'"), ctx
.filename
);
1535 if (st
.st_size
== 0) {
1536 altdb_context_free(&ctx
);
1537 alternative_reset(a
);
1541 /* Start parsing mandatory attributes (link+status) of the alternative */
1542 alternative_reset(a
);
1543 status
= altdb_get_line(&ctx
, _("status"));
1544 if (strcmp(status
, "auto") != 0 && strcmp(status
, "manual") != 0)
1545 altdb_bad_format(&ctx
, _("invalid status"));
1546 alternative_set_status(a
, (strcmp(status
, "auto") == 0) ?
1547 ALT_ST_AUTO
: ALT_ST_MANUAL
);
1550 master_link
= altdb_get_line(&ctx
, _("master link"));
1551 alternative_set_link(a
, master_link
);
1554 /* Parse the description of the slaves links of the alternative */
1555 while (alternative_parse_slave(a
, &ctx
));
1557 /* Parse the available choices in the alternative */
1558 while (alternative_parse_fileset(a
, &ctx
)) ;
1560 /* Close database file */
1562 syserr(_("unable to close file '%s'"), ctx
.filename
);
1565 /* Initialize the modified field which has been erroneously changed
1566 * by the various alternative_(add|set)_* calls:
1567 * false unless a choice has been auto-cleaned */
1568 a
->modified
= ctx
.modified
;
1574 alternative_save(struct alternative
*a
)
1576 struct altdb_context ctx
;
1577 struct slave_link
*sl
, *sl_prev
;
1579 char *filenew
, *file
;
1581 /* Cleanup unused slaves before writing admin file. */
1585 bool has_slave
= false;
1587 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1588 if (fileset_has_slave(fs
, sl
->name
)) {
1595 struct slave_link
*sl_rm
;
1597 verbose(_("discarding obsolete slave link %s (%s)"),
1598 sl
->name
, sl
->link
);
1600 sl_prev
->next
= sl
->next
;
1602 a
->slaves
= sl
->next
;
1605 slave_link_free(sl_rm
);
1613 alternative_sort_slaves(a
);
1614 alternative_sort_choices(a
);
1616 /* Write admin file. */
1617 file
= xasprintf("%s/%s", admdir
, a
->master_name
);
1618 filenew
= xasprintf("%s" ALT_TMP_EXT
, file
);
1620 ctx
.filename
= filenew
;
1621 ctx
.fh
= fopen(ctx
.filename
, "w");
1622 if (ctx
.fh
== NULL
&& errno
== ENOENT
) {
1623 if (make_path(admdir
, 0755) < 0)
1624 syserr(_("cannot create administrative directory '%s'"),
1626 ctx
.fh
= fopen(ctx
.filename
, "w");
1629 syserr(_("unable to create file '%s'"), ctx
.filename
);
1631 altdb_print_line(&ctx
, alternative_status_string(a
->status
));
1632 altdb_print_line(&ctx
, a
->master_link
);
1633 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1634 altdb_print_line(&ctx
, sl
->name
);
1635 altdb_print_line(&ctx
, sl
->link
);
1637 altdb_print_line(&ctx
, "");
1639 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1642 altdb_print_line(&ctx
, fs
->master_file
);
1644 prio
= xasprintf("%d", fs
->priority
);
1645 altdb_print_line(&ctx
, prio
);
1648 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1649 if (fileset_has_slave(fs
, sl
->name
))
1650 altdb_print_line(&ctx
,
1651 fileset_get_slave(fs
, sl
->name
));
1653 altdb_print_line(&ctx
, "");
1656 altdb_print_line(&ctx
, "");
1658 /* Close database file */
1660 syserr(_("unable to flush file '%s'"), ctx
.filename
);
1661 if (fsync(fileno(ctx
.fh
)))
1662 syserr(_("unable to sync file '%s'"), ctx
.filename
);
1664 syserr(_("unable to close file '%s'"), ctx
.filename
);
1666 /* Put in place atomically. */
1667 xrename(filenew
, file
);
1674 alternative_set_current(struct alternative
*a
, char *new_choice
)
1676 a
->known_current
= true;
1677 a
->current
= new_choice
;
1683 alternative_get_current(struct alternative
*a
)
1688 if (a
->known_current
)
1691 curlink
= xasprintf("%s/%s", altdir
, a
->master_name
);
1692 file
= fsys_areadlink(curlink
);
1693 if (file
== NULL
&& errno
!= ENOENT
)
1694 syserr(_("cannot stat file '%s%s'"), instdir
, curlink
);
1697 return alternative_set_current(a
, file
);
1700 static struct fileset
*
1701 alternative_get_best(struct alternative
*a
)
1703 struct fileset
*fs
, *best
;
1704 const char *current
;
1706 current
= alternative_get_current(a
);
1708 best
= alternative_get_fileset(a
, current
);
1715 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1716 if (fs
->priority
> best
->priority
)
1723 alternative_display_query(struct alternative
*a
)
1725 struct fileset
*best
, *fs
;
1726 struct slave_link
*sl
;
1727 const char *current
;
1729 pr("Name: %s", a
->master_name
);
1730 pr("Link: %s", a
->master_link
);
1731 if (alternative_slaves_count(a
) > 0) {
1733 for (sl
= a
->slaves
; sl
; sl
= sl
->next
)
1734 pr(" %s %s", sl
->name
, sl
->link
);
1736 pr("Status: %s", alternative_status_string(a
->status
));
1737 best
= alternative_get_best(a
);
1739 pr("Best: %s", best
->master_file
);
1740 current
= alternative_get_current(a
);
1741 pr("Value: %s", current
? current
: "none");
1743 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1745 pr("Alternative: %s", fs
->master_file
);
1746 pr("Priority: %d", fs
->priority
);
1747 if (alternative_slaves_count(a
) == 0)
1750 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1751 if (fileset_has_slave(fs
, sl
->name
))
1752 pr(" %s %s", sl
->name
,
1753 fileset_get_slave(fs
, sl
->name
));
1759 alternative_display_user(struct alternative
*a
)
1761 const char *current
;
1763 struct slave_link
*sl
;
1765 pr("%s - %s", a
->master_name
, alternative_status_describe(a
->status
));
1766 fs
= alternative_get_best(a
);
1768 pr(_(" link best version is %s"), fs
->master_file
);
1770 pr(_(" link best version not available"));
1771 current
= alternative_get_current(a
);
1773 pr(_(" link currently points to %s"), current
);
1775 pr(_(" link currently absent"));
1777 pr(_(" link %s is %s"), a
->master_name
, a
->master_link
);
1778 for (sl
= a
->slaves
; sl
; sl
= sl
->next
)
1779 pr(_(" slave %s is %s"), sl
->name
, sl
->link
);
1781 for (fs
= a
->choices
; fs
; fs
= fs
->next
) {
1782 pr(_("%s - priority %d"), fs
->master_file
, fs
->priority
);
1783 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
1784 if (fileset_has_slave(fs
, sl
->name
))
1785 pr(_(" slave %s: %s"), sl
->name
,
1786 fileset_get_slave(fs
, sl
->name
));
1792 alternative_display_list(struct alternative
*a
)
1796 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1797 pr("%s", fs
->master_file
);
1801 alternative_print_choice(struct alternative
*a
, enum alternative_status status
,
1802 struct fileset
*fs
, int idx
, int len
)
1804 const char *current
= alternative_get_current(a
);
1807 if (a
->status
== status
&&
1808 current
&& strcmp(current
, fs
->master_file
) == 0)
1813 pr("%c %-12d %-*s % -10d %s", mark
, idx
, len
,
1814 fs
->master_file
, fs
->priority
, alternative_status_describe(status
));
1818 alternative_select_choice(struct alternative
*a
)
1820 const char *current
;
1821 char *ret
, selection
[_POSIX_PATH_MAX
];
1822 struct fileset
*best
, *fs
;
1826 n_choices
= alternative_choices_count(a
);
1827 current
= alternative_get_current(a
);
1828 best
= alternative_get_best(a
);
1832 for (fs
= a
->choices
; fs
; fs
= fs
->next
)
1833 len
= max(len
, (int)strlen(fs
->master_file
) + 1);
1838 pr(P_("There is %d choice for the alternative %s (providing %s).",
1839 "There are %d choices for the alternative %s (providing %s).",
1840 n_choices
), n_choices
, a
->master_name
, a
->master_link
);
1843 pr(" %-12.12s %-*.*s %-10.10s %s", _("Selection"), len
, len
,
1844 _("Path"), _("Priority"), _("Status"));
1845 pr("------------------------------------------------------------");
1847 alternative_print_choice(a
, ALT_ST_AUTO
, best
, idx
++, len
);
1848 for (fs
= a
->choices
; fs
; fs
= fs
->next
, idx
++)
1849 alternative_print_choice(a
, ALT_ST_MANUAL
, fs
, idx
, len
);
1851 printf(_("Press <enter> to keep the current choice[*], "
1852 "or type selection number: "));
1853 ret
= fgets(selection
, sizeof(selection
), stdin
);
1854 if (ret
== NULL
|| strlen(selection
) == 0) {
1857 selection
[strlen(selection
) - 1] = '\0';
1858 if (strlen(selection
) == 0)
1859 return xstrdup(current
);
1861 idx
= strtol(selection
, &ret
, 10);
1862 if (idx
>= 0 && errno
== 0 && *ret
== '\0') {
1863 /* Look up by index */
1865 alternative_set_status(a
, ALT_ST_AUTO
);
1866 return xstrdup(best
->master_file
);
1869 for (fs
= a
->choices
; idx
&& fs
; idx
--)
1872 alternative_set_status(a
, ALT_ST_MANUAL
);
1873 return xstrdup(fs
->master_file
);
1876 /* Look up by name */
1877 fs
= alternative_get_fileset(a
, selection
);
1879 alternative_set_status(a
, ALT_ST_MANUAL
);
1880 return xstrdup(selection
);
1887 alternative_config(struct alternative
*a
, const char *current_choice
)
1889 char *new_choice
= NULL
;
1891 if (alternative_choices_count(a
) == 0) {
1892 pr(_("There is no program which provides %s."),
1894 pr(_("Nothing to configure."));
1895 } else if (opt_skip_auto
&& a
->status
== ALT_ST_AUTO
) {
1896 alternative_display_user(a
);
1898 new_choice
= alternative_select_choice(a
);
1905 alternative_add_commit_op(struct alternative
*a
, enum opcode opcode
,
1906 const char *arg_a
, const char *arg_b
)
1908 struct commit_operation
*op
, *cur
;
1910 op
= xmalloc(sizeof(*op
));
1911 op
->opcode
= opcode
;
1912 op
->arg_a
= xstrdup(arg_a
);
1913 op
->arg_b
= xstrdup(arg_b
);
1916 /* Add at the end */
1917 cur
= a
->commit_ops
;
1918 while (cur
&& cur
->next
)
1927 alternative_commit(struct alternative
*a
)
1929 struct commit_operation
*op
;
1931 for (op
= a
->commit_ops
; op
; op
= op
->next
) {
1932 switch (op
->opcode
) {
1939 fsys_mv(op
->arg_a
, op
->arg_b
);
1941 case OPCODE_REF_TIME
:
1942 fsys_set_ref_time(op
->arg_a
, op
->arg_b
);
1947 alternative_commit_operations_free(a
);
1950 enum alternative_path_status
{
1956 static enum alternative_path_status
1957 alternative_path_classify(const char *linkname
)
1961 if (fsys_lstat(linkname
, &st
) == -1) {
1962 if (errno
!= ENOENT
)
1963 syserr(_("cannot stat file '%s%s'"), instdir
, linkname
);
1964 return ALT_PATH_MISSING
;
1965 } else if (S_ISLNK(st
.st_mode
)) {
1966 return ALT_PATH_SYMLINK
;
1968 return ALT_PATH_OTHER
;
1973 alternative_path_can_remove(const char *linkname
)
1978 if (alternative_path_classify(linkname
) == ALT_PATH_OTHER
)
1985 alternative_path_needs_update(const char *linkname
, const char *filename
)
1993 switch (alternative_path_classify(linkname
)) {
1994 case ALT_PATH_SYMLINK
:
1995 linktarget
= fsys_xreadlink(linkname
);
1996 if (strcmp(linktarget
, filename
) == 0)
2003 case ALT_PATH_OTHER
:
2004 warning(_("not replacing %s with a link"), linkname
);
2006 case ALT_PATH_MISSING
:
2013 alternative_prepare_install_single(struct alternative
*a
, const char *name
,
2014 const char *linkname
, const char *file
)
2018 /* Create alternatives directory (/etc/alternatives) if missing. */
2019 if (fsys_pathname_is_missing(altdir
)) {
2020 char *root_altdir
= fsys_get_path(altdir
);
2022 if (make_path(root_altdir
, 0755) < 0)
2023 syserr(_("cannot create alternatives directory '%s'"),
2029 fn
= xasprintf("%s/%s", altdir
, name
);
2031 /* Create link in /etc/alternatives. */
2032 fntmp
= xasprintf("%s/%s" ALT_TMP_EXT
, altdir
, name
);
2033 fsys_symlink(file
, fntmp
);
2034 alternative_add_commit_op(a
, OPCODE_MV
, fntmp
, fn
);
2035 if (fsys_pathname_is_missing(fn
))
2036 alternative_add_commit_op(a
, OPCODE_REF_TIME
, fn
, file
);
2039 if (alternative_path_needs_update(linkname
, fn
)) {
2040 /* Create alternative link. */
2041 fntmp
= xasprintf("%s" ALT_TMP_EXT
, linkname
);
2042 fsys_symlink(fn
, fntmp
);
2043 alternative_add_commit_op(a
, OPCODE_MV
, fntmp
, linkname
);
2044 if (fsys_pathname_is_missing(linkname
))
2045 alternative_add_commit_op(a
, OPCODE_REF_TIME
, linkname
, fn
);
2052 alternative_prepare_install(struct alternative
*a
, const char *choice
)
2054 struct slave_link
*sl
;
2057 fs
= alternative_get_fileset(a
, choice
);
2059 error(_("can't install unknown choice %s"), choice
);
2061 /* Take care of master alternative */
2062 alternative_prepare_install_single(a
, a
->master_name
, a
->master_link
,
2065 /* Take care of slaves alternatives */
2066 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
2069 if (fileset_can_install_slave(fs
, sl
->name
)) {
2070 alternative_prepare_install_single(a
, sl
->name
,
2071 sl
->link
, fileset_get_slave(fs
, sl
->name
));
2075 /* Slave can't be installed */
2076 if (fileset_has_slave(fs
, sl
->name
))
2077 warning(_("skip creation of %s because associated "
2078 "file %s (of link group %s) doesn't exist"),
2079 sl
->link
, fileset_get_slave(fs
, sl
->name
),
2082 /* Drop unused slave. */
2083 fn
= xasprintf("%s/%s", altdir
, sl
->name
);
2084 if (alternative_path_can_remove(sl
->link
))
2085 alternative_add_commit_op(a
, OPCODE_RM
, sl
->link
, NULL
);
2087 warning(_("not removing %s since it's not a symlink"),
2089 alternative_add_commit_op(a
, OPCODE_RM
, fn
, NULL
);
2095 alternative_remove_files(struct alternative
*a
)
2097 struct slave_link
*sl
;
2099 fsys_rm_args("%s" ALT_TMP_EXT
, a
->master_link
);
2100 if (alternative_path_can_remove(a
->master_link
))
2101 fsys_rm(a
->master_link
);
2103 fsys_rm_args("%s/%s" ALT_TMP_EXT
, altdir
, a
->master_name
);
2104 fsys_rm_args("%s/%s", altdir
, a
->master_name
);
2106 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
2107 fsys_rm_args("%s" ALT_TMP_EXT
, sl
->link
);
2108 if (alternative_path_can_remove(sl
->link
))
2111 fsys_rm_args("%s/%s" ALT_TMP_EXT
, altdir
, sl
->name
);
2112 fsys_rm_args("%s/%s", altdir
, sl
->name
);
2114 /* Drop admin file */
2115 xunlink_args("%s/%s", admdir
, a
->master_name
);
2119 alternative_remove(struct alternative
*a
, const char *current_choice
,
2122 char *new_choice
= NULL
;
2124 if (alternative_has_choice(a
, path
))
2125 alternative_remove_choice(a
, path
);
2127 verbose(_("alternative %s for %s not registered; not removing"),
2128 path
, a
->master_name
);
2130 if (current_choice
&& strcmp(current_choice
, path
) == 0) {
2131 struct fileset
*best
;
2133 /* Current choice is removed. */
2134 if (a
->status
== ALT_ST_MANUAL
) {
2135 /* And it was manual, switch to auto. */
2136 info(_("removing manually selected alternative "
2137 "- switching %s to auto mode"),
2139 alternative_set_status(a
, ALT_ST_AUTO
);
2141 best
= alternative_get_best(a
);
2143 new_choice
= xstrdup(best
->master_file
);
2150 alternative_has_broken_symlink(const char *linkname
, const char *ref_target
)
2154 target
= fsys_areadlink(linkname
);
2157 if (strcmp(target
, ref_target
) != 0) {
2166 alternative_has_broken_slave(struct slave_link
*sl
, struct fileset
*fs
)
2168 if (fileset_can_install_slave(fs
, sl
->name
)) {
2170 const char *sl_target
;
2172 /* Verify link -> /etc/alternatives/foo */
2173 wanted
= xasprintf("%s/%s", altdir
, sl
->name
);
2174 if (alternative_has_broken_symlink(sl
->link
, wanted
)) {
2179 /* Verify /etc/alternatives/foo -> file */
2180 sl_target
= fileset_get_slave(fs
, sl
->name
);
2181 if (alternative_has_broken_symlink(wanted
, sl_target
)) {
2190 /* Slave link must not exist. */
2191 if (alternative_path_classify(sl
->link
) != ALT_PATH_MISSING
)
2193 sl_altlnk
= xasprintf("%s/%s", altdir
, sl
->name
);
2194 if (alternative_path_classify(sl_altlnk
) != ALT_PATH_MISSING
) {
2204 static enum alternative_update_reason
2205 alternative_needs_update(struct alternative
*a
)
2207 enum alternative_update_reason reason
= ALT_UPDATE_NO
;
2208 const char *current
;
2211 struct slave_link
*sl
;
2213 /* Check master link */
2214 wanted
= xasprintf("%s/%s", altdir
, a
->master_name
);
2215 if (alternative_has_broken_symlink(a
->master_link
, wanted
)) {
2217 return ALT_UPDATE_LINK_BROKEN
;
2221 /* Stop if we have an unmanaged alternative */
2222 current
= alternative_get_current(a
);
2223 if (current
== NULL
)
2224 return ALT_UPDATE_LINK_BROKEN
;
2226 fs
= alternative_get_fileset(a
, current
);
2228 /* Stop if we do not have the choice. */
2230 return ALT_UPDATE_NO
;
2233 for (sl
= a
->slaves
; sl
; sl
= sl
->next
) {
2234 if (alternative_has_broken_slave(sl
, fs
)) {
2236 reason
= ALT_UPDATE_SLAVE_CHANGED
;
2238 return ALT_UPDATE_LINK_BROKEN
;
2245 struct alternative_map
{
2246 struct alternative_map
*next
;
2249 struct alternative
*item
;
2252 static struct alternative_map
*
2253 alternative_map_new(const char *key
, struct alternative
*a
)
2255 struct alternative_map
*am
;
2257 am
= xmalloc(sizeof(*am
));
2265 static struct alternative
*
2266 alternative_map_find(struct alternative_map
*am
, const char *key
)
2268 for (; am
; am
= am
->next
)
2269 if (am
->key
&& strcmp(am
->key
, key
) == 0)
2276 alternative_map_add(struct alternative_map
*am
, const char *key
,
2277 struct alternative
*a
)
2281 if (am
->key
== NULL
) {
2285 struct alternative_map
*new = alternative_map_new(key
, a
);
2294 alternative_map_load_names(struct alternative_map
*alt_map_obj
)
2296 struct dirent
**table
;
2299 count
= altdb_get_namelist(&table
);
2300 for (i
= 0; i
< count
; i
++) {
2301 struct alternative
*a_new
= alternative_new(table
[i
]->d_name
);
2303 if (!alternative_load(a_new
, ALTDB_LAX_PARSER
)) {
2304 alternative_free(a_new
);
2307 alternative_map_add(alt_map_obj
, a_new
->master_name
, a_new
);
2309 alternative_unref(a_new
);
2311 altdb_free_namelist(table
, count
);
2315 alternative_map_load_tree(struct alternative_map
*alt_map_links
,
2316 struct alternative_map
*alt_map_parent
)
2318 struct dirent
**table
;
2321 count
= altdb_get_namelist(&table
);
2322 for (i
= 0; i
< count
; i
++) {
2323 struct slave_link
*sl
;
2324 struct alternative
*a_new
= alternative_new(table
[i
]->d_name
);
2326 if (!alternative_load(a_new
, ALTDB_LAX_PARSER
)) {
2327 alternative_free(a_new
);
2330 alternative_map_add(alt_map_links
, a_new
->master_link
, a_new
);
2331 alternative_map_add(alt_map_parent
, a_new
->master_name
, a_new
);
2332 for (sl
= a_new
->slaves
; sl
; sl
= sl
->next
) {
2333 alternative_map_add(alt_map_links
, sl
->link
, a_new
);
2334 alternative_map_add(alt_map_parent
, sl
->name
, a_new
);
2337 alternative_unref(a_new
);
2339 altdb_free_namelist(table
, count
);
2343 alternative_map_free(struct alternative_map
*am
)
2345 struct alternative_map
*am_next
;
2350 alternative_free(am
->item
);
2357 alternative_set_manual(struct alternative
*a
, const char *path
)
2359 char *new_choice
= NULL
;
2361 if (alternative_has_choice(a
, path
))
2362 new_choice
= xstrdup(path
);
2364 error(_("alternative %s for %s not registered; "
2365 "not setting"), path
, a
->master_name
);
2366 alternative_set_status(a
, ALT_ST_MANUAL
);
2372 alternative_set_auto(struct alternative
*a
)
2374 char *new_choice
= NULL
;
2376 alternative_set_status(a
, ALT_ST_AUTO
);
2377 if (alternative_choices_count(a
) == 0)
2378 info(_("there is no program which provides %s"),
2381 new_choice
= xstrdup(alternative_get_best(a
)->master_file
);
2387 get_argv_string(int argc
, char **argv
)
2389 static char string
[2048];
2395 for (i
= 1; i
< argc
; i
++) {
2396 size_t arg_len
= strlen(argv
[i
]);
2398 if (cur_len
+ arg_len
+ 2 > sizeof(string
))
2401 strcpy(string
+ cur_len
, " ");
2404 strcpy(string
+ cur_len
, argv
[i
]);
2412 alternative_select_mode(struct alternative
*a
, const char *current_choice
)
2414 if (current_choice
) {
2415 /* Detect manually modified alternative, switch to manual. */
2416 if (!alternative_has_choice(a
, current_choice
)) {
2417 if (fsys_pathname_is_missing(current_choice
)) {
2418 warning(_("%s%s/%s is dangling; it will be updated "
2419 "with best choice"), instdir
, altdir
,
2421 alternative_set_status(a
, ALT_ST_AUTO
);
2422 } else if (a
->status
!= ALT_ST_MANUAL
) {
2423 warning(_("%s%s/%s has been changed (manually or by "
2424 "a script); switching to manual "
2425 "updates only"), instdir
, altdir
,
2427 alternative_set_status(a
, ALT_ST_MANUAL
);
2431 /* Lack of alternative link => automatic mode. */
2432 verbose(_("setting up automatic selection of %s"),
2434 alternative_set_status(a
, ALT_ST_AUTO
);
2439 alternative_evolve_slave(struct alternative
*a
, const char *cur_choice
,
2440 struct slave_link
*sl
, struct fileset
*fs
)
2442 struct slave_link
*sl_old
;
2443 char *new_file
= NULL
;
2444 const char *old
, *new;
2446 sl_old
= alternative_get_slave(a
, sl
->name
);
2447 if (sl_old
== NULL
) {
2455 if (cur_choice
&& strcmp(cur_choice
, fs
->master_file
) == 0) {
2456 new_file
= xstrdup(fileset_get_slave(fs
, sl
->name
));
2460 lnk
= xasprintf("%s/%s", altdir
, sl
->name
);
2461 new_file
= fsys_areadlink(lnk
);
2464 if (strcmp(old
, new) != 0 &&
2465 alternative_path_classify(old
) == ALT_PATH_SYMLINK
) {
2466 bool rename_link
= false;
2469 rename_link
= !fsys_pathname_is_missing(new_file
);
2472 info(_("renaming %s slave link from %s%s to %s%s"),
2473 sl
->name
, instdir
, old
, instdir
, new);
2485 alternative_evolve(struct alternative
*a
, struct alternative
*b
,
2486 const char *cur_choice
, struct fileset
*fs
)
2488 struct slave_link
*sl
;
2491 is_link
= alternative_path_classify(a
->master_link
) == ALT_PATH_SYMLINK
;
2492 if (is_link
&& strcmp(a
->master_link
, b
->master_link
) != 0) {
2493 info(_("renaming %s link from %s%s to %s%s"), b
->master_name
,
2494 instdir
, a
->master_link
, instdir
, b
->master_link
);
2495 fsys_mv(a
->master_link
, b
->master_link
);
2497 alternative_set_link(a
, b
->master_link
);
2499 /* Check if new slaves have been added, or existing
2501 for (sl
= b
->slaves
; sl
; sl
= sl
->next
) {
2502 alternative_evolve_slave(a
, cur_choice
, sl
, fs
);
2503 alternative_copy_slave(a
, sl
);
2508 alternative_install(struct alternative
**aptr
, struct alternative
*inst_alt
,
2509 const char *current_choice
, struct fileset
*fileset
)
2511 struct alternative
*a
= *aptr
;
2512 char *new_choice
= NULL
;
2514 if (a
->master_link
) {
2515 /* Alternative already exists, check if anything got
2517 alternative_evolve(a
, inst_alt
, current_choice
, fileset
);
2518 alternative_free(inst_alt
);
2520 /* Alternative doesn't exist, create from parameters. */
2521 alternative_free(a
);
2522 *aptr
= a
= inst_alt
;
2524 alternative_add_choice(a
, fileset
);
2525 if (a
->status
== ALT_ST_AUTO
) {
2526 new_choice
= xstrdup(alternative_get_best(a
)->master_file
);
2528 verbose(_("automatic updates of %s/%s are disabled; "
2529 "leaving it alone"), altdir
, a
->master_name
);
2530 verbose(_("to return to automatic updates use "
2531 "'%s --auto %s'"), PROGNAME
, a
->master_name
);
2537 alternative_update(struct alternative
*a
,
2538 const char *current_choice
, const char *new_choice
)
2540 enum alternative_update_reason reason
;
2542 /* No choice left, remove everything. */
2543 if (!alternative_choices_count(a
)) {
2544 log_msg("link group %s fully removed", a
->master_name
);
2545 alternative_remove_files(a
);
2549 /* New choice wanted. */
2551 (!current_choice
|| strcmp(new_choice
, current_choice
) != 0)) {
2552 log_msg("link group %s updated to point to %s", a
->master_name
,
2554 if (a
->status
== ALT_ST_AUTO
)
2555 info(_("using %s to provide %s (%s) in auto mode"),
2556 new_choice
, a
->master_link
, a
->master_name
);
2558 info(_("using %s to provide %s (%s) in manual mode"),
2559 new_choice
, a
->master_link
, a
->master_name
);
2560 debug("prepare_install(%s)", new_choice
);
2561 alternative_prepare_install(a
, new_choice
);
2562 } else if ((reason
= alternative_needs_update(a
))) {
2563 if (reason
== ALT_UPDATE_SLAVE_CHANGED
) {
2564 log_msg("link group %s updated with changed slaves",
2566 info(_("updating alternative %s "
2567 "because link group %s has changed slave links"),
2568 current_choice
, a
->master_name
);
2570 log_msg("auto-repair link group %s", a
->master_name
);
2571 warning(_("forcing reinstallation of alternative %s "
2572 "because link group %s is broken"),
2573 current_choice
, a
->master_name
);
2576 if (current_choice
&& !alternative_has_choice(a
, current_choice
)) {
2577 struct fileset
*best
= alternative_get_best(a
);
2579 warning(_("current alternative %s is unknown, "
2580 "switching to %s for link group %s"),
2581 current_choice
, best
->master_file
,
2583 current_choice
= best
->master_file
;
2584 alternative_set_status(a
, ALT_ST_AUTO
);
2588 alternative_prepare_install(a
, current_choice
);
2591 /* Save administrative file if needed. */
2593 debug("%s is modified and will be saved", a
->master_name
);
2594 alternative_save(a
);
2597 /* Replace all symlinks in one pass. */
2598 alternative_commit(a
);
2602 alternative_config_all(void)
2604 struct alternative_map
*alt_map_obj
;
2605 struct alternative_map
*am
;
2607 alt_map_obj
= alternative_map_new(NULL
, NULL
);
2608 alternative_map_load_names(alt_map_obj
);
2610 for (am
= alt_map_obj
; am
&& am
->item
; am
= am
->next
) {
2611 const char *current_choice
;
2614 current_choice
= alternative_get_current(am
->item
);
2615 alternative_select_mode(am
->item
, current_choice
);
2617 new_choice
= alternative_config(am
->item
, current_choice
);
2619 alternative_update(am
->item
, current_choice
, new_choice
);
2624 alternative_map_free(alt_map_obj
);
2628 alternative_get_selections(void)
2630 struct alternative_map
*alt_map_obj
;
2631 struct alternative_map
*am
;
2633 alt_map_obj
= alternative_map_new(NULL
, NULL
);
2634 alternative_map_load_names(alt_map_obj
);
2636 for (am
= alt_map_obj
; am
&& am
->item
; am
= am
->next
) {
2637 const char *current
;
2639 current
= alternative_get_current(am
->item
);
2640 printf("%-30s %-8s %s\n", am
->key
,
2641 alternative_status_string(am
->item
->status
),
2642 current
? current
: "");
2645 alternative_map_free(alt_map_obj
);
2649 alternative_set_selection(struct alternative_map
*all
, const char *name
,
2650 const char *status
, const char *choice
)
2652 struct alternative
*a
;
2654 debug("set_selection(%s, %s, %s)", name
, status
, choice
);
2655 a
= alternative_map_find(all
, name
);
2657 char *new_choice
= NULL
;
2659 if (strcmp(status
, "auto") == 0) {
2660 info(_("selecting alternative %s as auto"), name
);
2661 new_choice
= alternative_set_auto(a
);
2662 } else if (alternative_has_choice(a
, choice
)) {
2663 info(_("selecting alternative %s as choice %s"), name
,
2665 new_choice
= alternative_set_manual(a
, choice
);
2667 info(_("alternative %s unchanged because choice "
2668 "%s is not available"), name
, choice
);
2672 const char *current_choice
;
2674 current_choice
= alternative_get_current(a
);
2675 alternative_select_mode(a
, current_choice
);
2677 alternative_update(a
, current_choice
, new_choice
);
2682 info(_("skip unknown alternative %s"), name
);
2687 alternative_set_selections(FILE *input
, const char *desc
)
2689 struct alternative_map
*alt_map_obj
;
2691 alt_map_obj
= alternative_map_new(NULL
, NULL
);
2692 alternative_map_load_names(alt_map_obj
);
2695 char line
[1024], *res
, *name
, *status
, *choice
;
2699 /* Can't use scanf("%s %s %s") because choice can
2700 * contain a space */
2701 res
= fgets(line
, sizeof(line
), input
);
2702 if (res
== NULL
&& errno
) {
2703 syserr(_("read error in %.250s"), desc
);
2704 } else if (res
== NULL
) {
2708 if (len
== 0 || line
[len
- 1] != '\n') {
2709 error(_("line too long or not terminated while "
2710 "trying to read %s"), desc
);
2712 line
[len
- 1] = '\0';
2715 /* Delimit name string in line */
2718 while (i
< len
&& !isblank(line
[i
]))
2721 info(_("skip invalid selection line: %s"), line
);
2725 while (i
< len
&& isblank(line
[i
]))
2728 /* Delimit status string in line */
2730 while (i
< len
&& !isblank(line
[i
]))
2733 info(_("skip invalid selection line: %s"), line
);
2737 while (i
< len
&& isblank(line
[i
]))
2740 /* Delimit choice string in the line */
2742 info(_("skip invalid selection line: %s"), line
);
2747 alternative_set_selection(alt_map_obj
, name
, status
, choice
);
2750 alternative_map_free(alt_map_obj
);
2754 alternative_check_name(const char *name
)
2756 if (strpbrk(name
, "/ \t"))
2757 error(_("alternative name (%s) must not contain '/' "
2758 "and spaces"), name
);
2762 alternative_check_link(const char *linkname
)
2764 if (linkname
[0] != '/')
2765 error(_("alternative link is not absolute as it should be: %s"),
2770 alternative_check_path(const char *file
)
2772 if (!file
|| file
[0] != '/')
2773 error(_("alternative path is not absolute as it should be: %s"),
2778 * Check the alternative installation arguments.
2780 * That the caller doesn't mix links between alternatives, doesn't mix
2781 * alternatives between slave/master, and that the various parameters
2785 alternative_check_install_args(struct alternative
*inst_alt
,
2786 struct fileset
*fileset
)
2788 struct alternative_map
*alt_map_links
, *alt_map_parent
;
2789 struct alternative
*found
;
2790 struct slave_link
*sl
;
2792 alternative_check_name(inst_alt
->master_name
);
2793 alternative_check_link(inst_alt
->master_link
);
2794 alternative_check_path(fileset
->master_file
);
2796 /* Load information about all alternatives to check for mistakes. */
2797 alt_map_links
= alternative_map_new(NULL
, NULL
);
2798 alt_map_parent
= alternative_map_new(NULL
, NULL
);
2799 alternative_map_load_tree(alt_map_links
, alt_map_parent
);
2801 found
= alternative_map_find(alt_map_parent
, inst_alt
->master_name
);
2802 if (found
&& strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2803 error(_("alternative %s can't be master: it is a slave of %s"),
2804 inst_alt
->master_name
, found
->master_name
);
2807 found
= alternative_map_find(alt_map_links
, inst_alt
->master_link
);
2808 if (found
&& strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2809 found
= alternative_map_find(alt_map_parent
,
2810 found
->master_name
);
2811 error(_("alternative link %s is already managed by %s"),
2812 inst_alt
->master_link
, found
->master_name
);
2815 if (fsys_pathname_is_missing(fileset
->master_file
))
2816 error(_("alternative path %s%s doesn't exist"),
2817 instdir
, fileset
->master_file
);
2819 for (sl
= inst_alt
->slaves
; sl
; sl
= sl
->next
) {
2820 const char *file
= fileset_get_slave(fileset
, sl
->name
);
2822 alternative_check_name(sl
->name
);
2823 alternative_check_link(sl
->link
);
2824 alternative_check_path(file
);
2826 found
= alternative_map_find(alt_map_parent
, sl
->name
);
2828 strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2829 if (strcmp(found
->master_name
, sl
->name
) == 0)
2830 error(_("alternative %s can't be slave of %s: "
2831 "it is a master alternative"),
2832 sl
->name
, inst_alt
->master_name
);
2834 error(_("alternative %s can't be slave of %s: "
2835 "it is a slave of %s"),
2836 sl
->name
, inst_alt
->master_name
,
2837 found
->master_name
);
2840 found
= alternative_map_find(alt_map_links
, sl
->link
);
2842 strcmp(found
->master_name
, inst_alt
->master_name
) != 0) {
2843 error(_("alternative link %s is already "
2844 "managed by %s"), sl
->link
,
2845 found
->master_name
);
2848 struct slave_link
*sl2
;
2850 for (sl2
= found
->slaves
; sl2
; sl2
= sl2
->next
)
2851 if (strcmp(sl2
->link
, sl
->link
) == 0)
2853 if (sl2
&& strcmp(sl2
->name
, sl
->name
) != 0)
2854 error(_("alternative link %s is already "
2855 "managed by %s (slave of %s)"),
2856 sl
->link
, sl2
->name
,
2857 found
->master_name
);
2861 alternative_map_free(alt_map_links
);
2862 alternative_map_free(alt_map_parent
);
2870 set_action(enum action new_action
)
2873 badusage(_("two commands specified: --%s and --%s"),
2874 action_names
[action
].name
, action_names
[new_action
].name
);
2875 action
= new_action
;
2879 set_action_from_name(const char *new_action
)
2883 for (i
= 0; i
< array_count(action_names
); i
++) {
2884 if (strcmp(new_action
, action_names
[i
].name
) == 0) {
2885 set_action(action_names
[i
].action
);
2890 assert(!"unknown action name");
2894 set_rootdir(const char *dir
)
2896 instdir
= fsys_set_dir(dir
);
2898 log_file
= fsys_get_path(LOGDIR
"/alternatives.log");
2899 altdir
= SYSCONFDIR
"/alternatives";
2901 admdir
= fsys_gen_admindir();
2909 const char *basedir_env
;
2911 /* Try to get the admindir from an environment variable, usually set
2912 * by the system package manager. */
2913 basedir_env
= getenv(ADMINDIR_ENVVAR
);
2915 return xasprintf("%s%s", basedir_env
, "/alternatives");
2917 return fsys_gen_admindir();
2920 #define MISSING_ARGS(nb) (argc < i + nb + 1)
2923 main(int argc
, char **argv
)
2925 /* Alternative worked on. */
2926 struct alternative
*a
= NULL
;
2927 /* Alternative to install. */
2928 struct alternative
*inst_alt
= NULL
;
2929 /* Set of files to install in the alternative. */
2930 struct fileset
*fileset
= NULL
;
2931 /* Path of alternative we are offering. */
2932 const char *path
= NULL
;
2933 const char *current_choice
= NULL
;
2934 char *new_choice
= NULL
;
2935 bool modifies_alt
= false;
2936 bool modifies_sys
= false;
2939 setlocale(LC_ALL
, "");
2940 bindtextdomain(PACKAGE
, LOCALEDIR
);
2941 textdomain(PACKAGE
);
2946 instdir
= fsys_set_dir(NULL
);
2947 admdir
= admindir_init();
2948 log_file
= fsys_get_path(LOGDIR
"/alternatives.log");
2950 if (setvbuf(stdout
, NULL
, _IONBF
, 0))
2951 syserr("setvbuf failed");
2953 prog_path
= argv
[0];
2955 for (i
= 1; i
< argc
; i
++) {
2956 if (strstr(argv
[i
], "--") != argv
[i
]) {
2957 error(_("unknown argument '%s'"), argv
[i
]);
2958 } else if (strcmp("--help", argv
[i
]) == 0) {
2961 } else if (strcmp("--version", argv
[i
]) == 0) {
2964 } else if (strcmp("--quiet", argv
[i
]) == 0) {
2965 opt_verbose
= OUTPUT_QUIET
;
2966 } else if (strcmp("--verbose", argv
[i
]) == 0) {
2967 opt_verbose
= OUTPUT_VERBOSE
;
2968 } else if (strcmp("--debug", argv
[i
]) == 0) {
2969 opt_verbose
= OUTPUT_DEBUG
;
2970 } else if (strcmp("--install", argv
[i
]) == 0) {
2971 const char *alink
, *aname
, *apath
;
2972 char *prio_str
, *prio_end
;
2975 set_action(ACTION_INSTALL
);
2976 if (MISSING_ARGS(4))
2977 badusage(_("--%s needs <link> <name> <path> "
2978 "<priority>"), argv
[i
] + 2);
2980 alink
= argv
[i
+ 1];
2981 aname
= argv
[i
+ 2];
2982 apath
= argv
[i
+ 3];
2983 prio_str
= argv
[i
+ 4];
2985 if (strcmp(alink
, apath
) == 0)
2986 badusage(_("<link> '%s' is the same as <path>"),
2989 prio
= strtol(prio_str
, &prio_end
, 10);
2990 if (prio_str
== prio_end
|| *prio_end
!= '\0')
2991 badusage(_("priority '%s' must be an integer"),
2993 if (prio
< INT_MIN
|| prio
> INT_MAX
|| errno
== ERANGE
)
2994 badusage(_("priority '%s' is out of range"),
2997 a
= alternative_new(aname
);
2998 inst_alt
= alternative_new(aname
);
2999 alternative_set_status(inst_alt
, ALT_ST_AUTO
);
3000 alternative_set_link(inst_alt
, alink
);
3001 fileset
= fileset_new(apath
, prio
);
3004 } else if (strcmp("--remove", argv
[i
]) == 0 ||
3005 strcmp("--set", argv
[i
]) == 0) {
3006 set_action_from_name(argv
[i
] + 2);
3007 if (MISSING_ARGS(2))
3008 badusage(_("--%s needs <name> <path>"), argv
[i
] + 2);
3010 a
= alternative_new(argv
[i
+ 1]);
3013 alternative_check_name(a
->master_name
);
3014 alternative_check_path(path
);
3017 } else if (strcmp("--display", argv
[i
]) == 0 ||
3018 strcmp("--query", argv
[i
]) == 0 ||
3019 strcmp("--auto", argv
[i
]) == 0 ||
3020 strcmp("--config", argv
[i
]) == 0 ||
3021 strcmp("--list", argv
[i
]) == 0 ||
3022 strcmp("--remove-all", argv
[i
]) == 0) {
3023 set_action_from_name(argv
[i
] + 2);
3024 if (MISSING_ARGS(1))
3025 badusage(_("--%s needs <name>"), argv
[i
] + 2);
3026 a
= alternative_new(argv
[i
+ 1]);
3028 alternative_check_name(a
->master_name
);
3031 } else if (strcmp("--all", argv
[i
]) == 0 ||
3032 strcmp("--get-selections", argv
[i
]) == 0 ||
3033 strcmp("--set-selections", argv
[i
]) == 0) {
3034 set_action_from_name(argv
[i
] + 2);
3035 } else if (strcmp("--slave", argv
[i
]) == 0) {
3036 const char *slink
, *sname
, *spath
;
3037 struct slave_link
*sl
;
3039 if (action
!= ACTION_INSTALL
)
3040 badusage(_("--%s only allowed with --%s"),
3041 argv
[i
] + 2, "install");
3042 if (MISSING_ARGS(3))
3043 badusage(_("--%s needs <link> <name> <path>"),
3046 slink
= argv
[i
+ 1];
3047 sname
= argv
[i
+ 2];
3048 spath
= argv
[i
+ 3];
3050 if (strcmp(slink
, spath
) == 0)
3051 badusage(_("<link> '%s' is the same as <path>"),
3053 if (strcmp(inst_alt
->master_name
, sname
) == 0)
3054 badusage(_("<name> '%s' is both primary and slave"),
3056 if (strcmp(slink
, inst_alt
->master_link
) == 0)
3057 badusage(_("<link> '%s' is both primary and slave"),
3059 if (alternative_has_slave(inst_alt
, sname
))
3060 badusage(_("duplicate slave <name> '%s'"), sname
);
3062 for (sl
= inst_alt
->slaves
; sl
; sl
= sl
->next
) {
3063 const char *linkname
= sl
->link
;
3064 if (linkname
== NULL
)
3066 if (strcmp(linkname
, slink
) == 0)
3067 badusage(_("duplicate slave <link> '%s'"),
3071 alternative_add_slave(inst_alt
, sname
, slink
);
3072 fileset_add_slave(fileset
, sname
, spath
);
3075 } else if (strcmp("--log", argv
[i
]) == 0) {
3076 if (MISSING_ARGS(1))
3077 badusage(_("--%s needs a <file> argument"),
3080 log_file
= fsys_get_path(argv
[i
+ 1]);
3082 } else if (strcmp("--altdir", argv
[i
]) == 0) {
3083 if (MISSING_ARGS(1))
3084 badusage(_("--%s needs a <directory> argument"),
3086 altdir
= argv
[i
+ 1];
3089 /* If altdir is below instdir, convert it to a relative
3090 * path, as we will prepend instdir as needed. */
3091 if (strncmp(altdir
, instdir
, instdir_len
) == 0)
3092 altdir
+= instdir_len
;
3093 } else if (strcmp("--admindir", argv
[i
]) == 0) {
3094 if (MISSING_ARGS(1))
3095 badusage(_("--%s needs a <directory> argument"),
3098 admdir
= xstrdup(argv
[i
+ 1]);
3100 } else if (strcmp("--instdir", argv
[i
]) == 0) {
3101 if (MISSING_ARGS(1))
3102 badusage(_("--%s needs a <directory> argument"),
3104 fsys_set_dir(argv
[i
+ 1]);
3107 /* If altdir is below instdir, convert it to a relative
3108 * path, as we will prepend instdir as needed. */
3109 if (strncmp(altdir
, instdir
, instdir_len
) == 0)
3110 altdir
+= instdir_len
;
3111 } else if (strcmp("--root", argv
[i
]) == 0) {
3112 if (MISSING_ARGS(1))
3113 badusage(_("--%s needs a <directory> argument"),
3115 set_rootdir(argv
[i
+ 1]);
3117 } else if (strcmp("--skip-auto", argv
[i
]) == 0) {
3119 } else if (strcmp("--force", argv
[i
]) == 0) {
3122 badusage(_("unknown option '%s'"), argv
[i
]);
3126 if (action
== ACTION_NONE
)
3127 badusage(_("need --%s, --%s, --%s, --%s, --%s, --%s, --%s, "
3128 "--%s, --%s, --%s, --%s or --%s"),
3129 "display", "query", "list", "get-selections",
3130 "config", "set", "set-selections", "install",
3131 "remove", "all", "remove-all", "auto");
3133 debug("root=%s admdir=%s altdir=%s", instdir
, admdir
, altdir
);
3135 /* The following actions might modify the current alternative. */
3136 if (action
== ACTION_SET
||
3137 action
== ACTION_AUTO
||
3138 action
== ACTION_CONFIG
||
3139 action
== ACTION_REMOVE
||
3140 action
== ACTION_REMOVE_ALL
||
3141 action
== ACTION_INSTALL
)
3142 modifies_alt
= true;
3144 /* The following actions might modify the system somehow. */
3146 action
== ACTION_CONFIG_ALL
||
3147 action
== ACTION_SET_SELECTIONS
)
3148 modifies_sys
= true;
3150 if (action
== ACTION_INSTALL
)
3151 alternative_check_install_args(inst_alt
, fileset
);
3153 if (action
== ACTION_DISPLAY
||
3154 action
== ACTION_QUERY
||
3155 action
== ACTION_LIST
||
3156 action
== ACTION_SET
||
3157 action
== ACTION_AUTO
||
3158 action
== ACTION_CONFIG
||
3159 action
== ACTION_REMOVE_ALL
) {
3160 /* Load the alternative info, stop on failure. */
3161 if (!alternative_load(a
, ALTDB_WARN_PARSER
))
3162 error(_("no alternatives for %s"), a
->master_name
);
3163 } else if (action
== ACTION_REMOVE
) {
3164 /* XXX: Be consistent for now with the case when we
3165 * try to remove a non-existing path from an existing
3166 * link group file. */
3167 if (!alternative_load(a
, ALTDB_WARN_PARSER
)) {
3168 verbose(_("no alternatives for %s"), a
->master_name
);
3169 alternative_free(a
);
3174 } else if (action
== ACTION_INSTALL
) {
3175 /* Load the alternative info, ignore failures. */
3176 alternative_load(a
, ALTDB_WARN_PARSER
);
3180 log_msg("run with %s", get_argv_string(argc
, argv
));
3183 current_choice
= alternative_get_current(a
);
3184 alternative_select_mode(a
, current_choice
);
3187 /* Handle actions. */
3188 if (action
== ACTION_CONFIG_ALL
) {
3189 alternative_config_all();
3190 } else if (action
== ACTION_GET_SELECTIONS
) {
3191 alternative_get_selections();
3192 } else if (action
== ACTION_SET_SELECTIONS
) {
3193 alternative_set_selections(stdin
, _("<standard input>"));
3194 } else if (action
== ACTION_DISPLAY
) {
3195 alternative_display_user(a
);
3196 } else if (action
== ACTION_QUERY
) {
3197 alternative_display_query(a
);
3198 } else if (action
== ACTION_LIST
) {
3199 alternative_display_list(a
);
3200 } else if (action
== ACTION_SET
) {
3201 new_choice
= alternative_set_manual(a
, path
);
3202 } else if (action
== ACTION_AUTO
) {
3203 new_choice
= alternative_set_auto(a
);
3204 } else if (action
== ACTION_CONFIG
) {
3205 new_choice
= alternative_config(a
, current_choice
);
3206 } else if (action
== ACTION_REMOVE
) {
3207 new_choice
= alternative_remove(a
, current_choice
, path
);
3208 } else if (action
== ACTION_REMOVE_ALL
) {
3209 alternative_choices_free(a
);
3210 } else if (action
== ACTION_INSTALL
) {
3211 new_choice
= alternative_install(&a
, inst_alt
, current_choice
,
3216 alternative_update(a
, current_choice
, new_choice
);
3219 alternative_free(a
);