[PATCH v4] update-alternatives: Implement offline mode
Lets update-alternatives manage symlinks inside a cross-arch root
filesystem in a directory specified by DPKG_ROOT.
Note: SYSCONFDIR has to be set to a value relative to DPKG_ROOT.
Signed-off-by: Andreas Oberritter <obi@opendreambox.org>
---
v4:
Now using DPKG_ROOT variable (--force-script-chrootless option).
Rebased onto master.
v3 [https://lists.debian.org/debian-dpkg/2015/03/msg00004.html]:
Rebased onto master.
v2 [https://lists.debian.org/debian-dpkg/2014/09/msg00012.html]:
altdb_get_namelist() didn't respect DPKG_INSTDIR.
v1 [https://lists.debian.org/debian-dpkg/2014/08/msg00052.html]:
utils/Makefile.am | 1 +
utils/update-alternatives.c | 175 ++++++++++++++++++++++++++++----------------
2 files changed, 114 insertions(+), 62 deletions(-)
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 83378ad..1f821c5 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -27,6 +27,7 @@ update_alternatives_SOURCES = \
update_alternatives_CPPFLAGS = \
-DALT_TMP_EXT=\".dpkg-tmp\" \
-DADMINDIR_ENVVAR=\"DPKG_ADMINDIR\" \
+ -DINSTDIR_ENVVAR=\"DPKG_ROOT\" \
$(AM_CPPFLAGS)
update_alternatives_LDADD = \
diff --git a/utils/update-alternatives.c b/utils/update-alternatives.c
index 5dc3213..2b49819 100644
--- a/utils/update-alternatives.c
+++ b/utils/update-alternatives.c
@@ -51,6 +51,7 @@
#define PROGNAME "update-alternatives"
static const char *altdir = SYSCONFDIR "/alternatives";
+static const char *instdir;
static const char *admdir;
static const char *prog_path = "update-alternatives";
@@ -64,6 +65,27 @@ static int opt_verbose = 0;
static int opt_force = 0;
/*
+ * Types.
+ */
+
+enum alternative_path_status {
+ ALT_PATH_SYMLINK,
+ ALT_PATH_MISSING,
+ ALT_PATH_OTHER,
+};
+
+
+/*
+ * Predeclarations.
+ */
+
+static char * DPKG_ATTR_PRINTF(1)
+xasprintf(const char *fmt, ...);
+
+static enum alternative_path_status
+alternative_path_classify(const char *linkname);
+
+/*
* Functions.
*/
@@ -270,7 +292,7 @@ xstrdup(const char *str)
}
static char *
-areadlink(const char *linkname)
+_areadlink(const char *linkname)
{
struct stat st;
char *buf;
@@ -303,6 +325,19 @@ areadlink(const char *linkname)
}
static char *
+areadlink(const char *linkname)
+{
+ char *instdir_linkname;
+ char *ret;
+
+ instdir_linkname = xasprintf("%s%s", instdir, linkname);
+ ret = _areadlink(instdir_linkname);
+ free(instdir_linkname);
+
+ return ret;
+}
+
+static char *
xreadlink(const char *linkname)
{
char *buf;
@@ -348,9 +383,22 @@ set_action(const char *new_action)
}
static const char *
+instdir_init(void)
+{
+ const char *dpkg_instdir;
+
+ dpkg_instdir = getenv(INSTDIR_ENVVAR);
+ if (dpkg_instdir)
+ return dpkg_instdir;
+
+ return "";
+}
+
+static const char *
admindir_init(void)
{
const char *basedir, *basedir_env;
+ size_t length;
/* Try to get the admindir from an environment variable, usually set
* by the system package manager. */
@@ -360,6 +408,12 @@ admindir_init(void)
else
basedir = ADMINDIR;
+ /* If instdir is set and admindir is below instdir, treat admindir
+ * as relative. */
+ length = strlen(instdir);
+ if (strncmp(basedir, instdir, length) == 0)
+ basedir += length;
+
return xasprintf("%s/%s", basedir, "alternatives");
}
@@ -432,25 +486,43 @@ rename_mv(const char *src, const char *dst)
static void
checked_symlink(const char *filename, const char *linkname)
{
- if (symlink(filename, linkname))
+ char *instdir_linkname;
+
+ instdir_linkname = xasprintf("%s%s", instdir, linkname);
+
+ if (symlink(filename, instdir_linkname))
syserr(_("error creating symbolic link '%.255s'"), linkname);
+
+ free(instdir_linkname);
}
static void
checked_mv(const char *src, const char *dst)
{
- if (!rename_mv(src, dst))
+ char *instdir_src;
+ char *instdir_dst;
+
+ instdir_src = xasprintf("%s%s", instdir, src);
+ instdir_dst = xasprintf("%s%s", instdir, dst);
+
+ if (!rename_mv(instdir_src, instdir_dst))
syserr(_("unable to install '%.250s' as '%.250s'"), src, dst);
+
+ free(instdir_src);
+ free(instdir_dst);
}
static void
checked_rm(const char *f)
{
- if (!unlink(f))
- return;
+ char *instdir_f;
- if (errno != ENOENT)
+ instdir_f = xasprintf("%s%s", instdir, f);
+
+ if (unlink(instdir_f) && errno != ENOENT)
syserr(_("unable to remove '%s'"), f);
+
+ free(instdir_f);
}
static void DPKG_ATTR_PRINTF(1)
@@ -567,16 +639,11 @@ fileset_has_slave(struct fileset *fs, const char *name)
static bool
fileset_can_install_slave(struct fileset *fs, const char *slave_name)
{
- struct stat st;
-
/* Decide whether the slave alternative must be setup */
if (fileset_has_slave(fs, slave_name)) {
const char *slave = fileset_get_slave(fs, slave_name);
- errno = 0;
- if (stat(slave, &st) == -1 && errno != ENOENT)
- syserr(_("cannot stat file '%s'"), slave);
- if (errno == 0)
+ if (alternative_path_classify(slave) != ALT_PATH_MISSING)
return true;
}
@@ -1039,10 +1106,15 @@ static int
altdb_get_namelist(struct dirent ***table)
{
int count;
+ char *instdir_admdir;
- count = scandir(admdir, table, altdb_filter_namelist, alphasort);
+ instdir_admdir = xasprintf("%s%s", instdir, admdir);
+
+ count = scandir(instdir_admdir, table, altdb_filter_namelist, alphasort);
if (count < 0)
- syserr(_("cannot scan directory '%.255s'"), admdir);
+ syserr(_("cannot scan directory '%.255s'"), instdir_admdir);
+
+ free(instdir_admdir);
return count;
}
@@ -1167,7 +1239,6 @@ alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx)
{
struct fileset *fs;
struct slave_link *sl;
- struct stat st;
char *master_file;
master_file = altdb_get_line(ctx, _("master file"));
@@ -1180,12 +1251,9 @@ alternative_parse_fileset(struct alternative *a, struct altdb_context *ctx)
if (fs)
ctx->bad_format(ctx, _("duplicate path %s"), master_file);
- if (stat(master_file, &st)) {
+ if (alternative_path_classify(master_file) == ALT_PATH_MISSING) {
char *junk;
- if (errno != ENOENT)
- syserr(_("cannot stat file '%s'"), master_file);
-
/* File not found - remove. */
if (ctx->flags & ALTDB_WARN_PARSER)
warning(_("alternative %s (part of link group %s) "
@@ -1248,7 +1316,7 @@ alternative_load(struct alternative *a, enum altdb_flags flags)
ctx.bad_format = altdb_parse_stop;
else
ctx.bad_format = altdb_parse_error;
- ctx.filename = xasprintf("%s/%s", admdir, a->master_name);
+ ctx.filename = xasprintf("%s%s/%s", instdir, admdir, a->master_name);
/* Open the alternative file. */
ctx.fh = fopen(ctx.filename, "r");
@@ -1340,7 +1408,7 @@ alternative_save(struct alternative *a)
file = xasprintf("%s/%s", admdir, a->master_name);
filenew = xasprintf("%s" ALT_TMP_EXT, file);
- ctx.filename = filenew;
+ ctx.filename = xasprintf("%s%s", instdir, filenew);
ctx.fh = fopen(ctx.filename, "w");
if (ctx.fh == NULL)
syserr(_("unable to create file '%s'"), ctx.filename);
@@ -1379,6 +1447,7 @@ alternative_save(struct alternative *a)
syserr(_("unable to sync file '%s'"), ctx.filename);
if (fclose(ctx.fh))
syserr(_("unable to close file '%s'"), ctx.filename);
+ free(ctx.filename);
/* Put in place atomically. */
checked_mv(filenew, file);
@@ -1399,7 +1468,6 @@ alternative_set_current(struct alternative *a, char *new_choice)
static const char *
alternative_get_current(struct alternative *a)
{
- struct stat st;
char *curlink;
char *file;
@@ -1407,12 +1475,9 @@ alternative_get_current(struct alternative *a)
return a->current;
curlink = xasprintf("%s/%s", altdir, a->master_name);
- if (lstat(curlink, &st)) {
- if (errno == ENOENT) {
- free(curlink);
- return alternative_set_current(a, NULL);
- }
- syserr(_("cannot stat file '%s'"), curlink);
+ if (alternative_path_classify(curlink) == ALT_PATH_MISSING) {
+ free(curlink);
+ return alternative_set_current(a, NULL);
}
file = xreadlink(curlink);
@@ -1674,14 +1739,8 @@ alternative_commit(struct alternative *a)
alternative_commit_operations_free(a);
}
-enum alternative_path_status {
- ALT_PATH_SYMLINK,
- ALT_PATH_MISSING,
- ALT_PATH_OTHER,
-};
-
static enum alternative_path_status
-alternative_path_classify(const char *linkname)
+_alternative_path_classify(const char *linkname)
{
struct stat st;
@@ -1697,6 +1756,19 @@ alternative_path_classify(const char *linkname)
}
}
+static enum alternative_path_status
+alternative_path_classify(const char *linkname)
+{
+ enum alternative_path_status ret;
+ char *instdir_linkname;
+
+ instdir_linkname = xasprintf("%s%s", instdir, linkname);
+ ret = _alternative_path_classify(instdir_linkname);
+ free(instdir_linkname);
+
+ return ret;
+}
+
static bool
alternative_path_can_remove(const char *linkname)
{
@@ -2120,13 +2192,7 @@ alternative_select_mode(struct alternative *a, const char *current_choice)
if (current_choice) {
/* Detect manually modified alternative, switch to manual. */
if (!alternative_has_choice(a, current_choice)) {
- struct stat st;
-
- errno = 0;
- if (stat(current_choice, &st) == -1 && errno != ENOENT)
- syserr(_("cannot stat file '%s'"), current_choice);
-
- if (errno == ENOENT) {
+ if (alternative_path_classify(current_choice) == ALT_PATH_MISSING) {
warning(_("%s/%s is dangling; it will be updated "
"with best choice"), altdir, a->master_name);
alternative_set_status(a, ALT_ST_AUTO);
@@ -2150,7 +2216,6 @@ alternative_evolve_slave(struct alternative *a, const char *cur_choice,
struct slave_link *sl, struct fileset *fs)
{
struct slave_link *sl_old;
- struct stat st;
char *new_file = NULL;
const char *old, *new;
@@ -2174,17 +2239,7 @@ alternative_evolve_slave(struct alternative *a, const char *cur_choice,
}
if (strcmp(old, new) != 0 &&
alternative_path_classify(old) == ALT_PATH_SYMLINK) {
- bool rename_link = false;
-
- if (new_file) {
- errno = 0;
- if (stat(new_file, &st) == -1 && errno != ENOENT)
- syserr(_("cannot stat file '%s'"),
- new_file);
- rename_link = (errno == 0);
- }
-
- if (rename_link) {
+ if (new_file && alternative_path_classify(new_file) != ALT_PATH_MISSING) {
info(_("renaming %s slave link from %s to %s"),
sl->name, old, new);
checked_mv(old, new);
@@ -2474,7 +2529,6 @@ alternative_check_install_args(struct alternative *inst_alt,
struct alternative_map *alt_map_links, *alt_map_parent;
struct alternative *found;
struct slave_link *sl;
- struct stat st;
alternative_check_name(inst_alt->master_name);
alternative_check_link(inst_alt->master_link);
@@ -2499,13 +2553,9 @@ alternative_check_install_args(struct alternative *inst_alt,
inst_alt->master_link, found->master_name);
}
- if (stat(fileset->master_file, &st) == -1) {
- if (errno == ENOENT)
- error(_("alternative path %s doesn't exist"),
- fileset->master_file);
- else
- syserr(_("cannot stat file '%s'"), fileset->master_file);
- }
+ if (alternative_path_classify(fileset->master_file) == ALT_PATH_MISSING)
+ error(_("alternative path %s doesn't exist"),
+ fileset->master_file);
for (sl = inst_alt->slaves; sl; sl = sl->next) {
const char *file = fileset_get_slave(fileset, sl->name);
@@ -2580,6 +2630,7 @@ main(int argc, char **argv)
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
+ instdir = instdir_init();
admdir = admindir_init();
if (setvbuf(stdout, NULL, _IONBF, 0))
Reply to: