[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH v3 2/2] update-alternatives: Implement offline mode



Lets update-alternatives manage symlinks inside a cross-arch root
filesystem in a directory specified by DPKG_INSTDIR.

Signed-off-by: Andreas Oberritter <obi@opendreambox.org>
---
Changes since v2:
 rebased
Changes since v1:
 altdb_get_namelist() didn't respect DPKG_INSTDIR.

 utils/update-alternatives.c | 171 ++++++++++++++++++++++++++++----------------
 1 file changed, 109 insertions(+), 62 deletions(-)

diff --git a/utils/update-alternatives.c b/utils/update-alternatives.c
index d5f9af5..dc7fc61 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";
@@ -71,6 +72,27 @@ static int nb_opts = 0;
 #define ALT_TMP_EXT ".dpkg-tmp"
 
 /*
+ * Types.
+ */
+
+enum alternative_path_status {
+	ALT_PATH_SYMLINK,
+	ALT_PATH_MISSING,
+	ALT_PATH_OTHER,
+};
+
+
+/*
+ * Predeclarations.
+ */
+
+static int DPKG_ATTR_PRINTF(2)
+xasprintf(char **strp, const char *fmt, ...);
+
+static enum alternative_path_status
+alternative_path_classify(const char *linkname);
+
+/*
  * Functions.
  */
 
@@ -277,7 +299,7 @@ xstrdup(const char *str)
 }
 
 static char *
-areadlink(const char *linkname)
+_areadlink(const char *linkname)
 {
 	struct stat st;
 	char *buf;
@@ -310,6 +332,22 @@ areadlink(const char *linkname)
 }
 
 static char *
+areadlink(const char *linkname)
+{
+	char *instdir_linkname;
+	int saved_errno;
+	char *ret;
+
+	xasprintf(&instdir_linkname, "%s%s", instdir, linkname);
+	ret = _areadlink(instdir_linkname);
+	saved_errno = errno;
+	free(instdir_linkname);
+	errno = saved_errno;
+
+	return ret;
+}
+
+static char *
 xreadlink(const char *linkname)
 {
 	char *buf;
@@ -356,6 +394,18 @@ set_action(const char *new_action)
 }
 
 static const char *
+instdir_init(void)
+{
+	const char *dpkg_instdir;
+
+	dpkg_instdir = getenv("DPKG_INSTDIR");
+	if (dpkg_instdir)
+		return dpkg_instdir;
+
+	return "";
+}
+
+static const char *
 admindir_init(void)
 {
 	const char *basedir, *dpkg_basedir;
@@ -477,25 +527,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;
+
+	xasprintf(&instdir_linkname, "%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;
+
+	xasprintf(&instdir_src, "%s%s", instdir, src);
+	xasprintf(&instdir_dst, "%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;
+
+	xasprintf(&instdir_f, "%s%s", instdir, f);
 
-	if (errno != ENOENT)
+	if (unlink(instdir_f) && errno != ENOENT)
 		syserr(_("unable to remove '%s'"), f);
+
+	free(instdir_f);
 }
 
 static void DPKG_ATTR_PRINTF(1)
@@ -612,16 +680,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;
 	}
 
@@ -1084,10 +1147,15 @@ static int
 altdb_get_namelist(struct dirent ***table)
 {
 	int count;
+	char *instdir_admdir;
+
+	xasprintf(&instdir_admdir, "%s%s", instdir, admdir);
 
-	count = scandir(admdir, table, altdb_filter_namelist, alphasort);
+	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;
 }
@@ -1212,7 +1280,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"));
@@ -1225,12 +1292,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) "
@@ -1293,7 +1357,7 @@ alternative_load(struct alternative *a, enum altdb_flags flags)
 		ctx.bad_format = altdb_parse_stop;
 	else
 		ctx.bad_format = altdb_parse_error;
-	xasprintf(&fn, "%s/%s", admdir, a->master_name);
+	xasprintf(&fn, "%s%s/%s", instdir, admdir, a->master_name);
 	ctx.filename = fn;
 
 	/* Open the alternative file. */
@@ -1386,7 +1450,7 @@ alternative_save(struct alternative *a)
 	xasprintf(&file, "%s/%s", admdir, a->master_name);
 	xasprintf(&filenew, "%s" ALT_TMP_EXT, file);
 
-	ctx.filename = filenew;
+	xasprintf(&ctx.filename, "%s%s", instdir, filenew);
 	ctx.fh = fopen(ctx.filename, "w");
 	if (ctx.fh == NULL)
 		syserr(_("unable to create file '%s'"), ctx.filename);
@@ -1425,6 +1489,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);
@@ -1445,7 +1510,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;
 
@@ -1453,12 +1517,9 @@ alternative_get_current(struct alternative *a)
 		return a->current;
 
 	xasprintf(&curlink, "%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);
@@ -1708,14 +1769,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;
 
@@ -1731,6 +1786,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;
+
+	xasprintf(&instdir_linkname, "%s%s", instdir, linkname);
+	ret = _alternative_path_classify(instdir_linkname);
+	free(instdir_linkname);
+
+	return ret;
+}
+
 static bool
 alternative_path_can_remove(const char *linkname)
 {
@@ -2216,13 +2284,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);
@@ -2245,7 +2307,6 @@ static void
 alternative_evolve_slave(struct alternative *a, const char *cur_choice,
                          struct slave_link *sl, struct fileset *fs)
 {
-	struct stat st;
 	char *new_file = NULL;
 	const char *old, *new;
 
@@ -2263,17 +2324,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);
@@ -2416,7 +2467,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);
@@ -2441,13 +2491,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);
@@ -2520,6 +2566,7 @@ main(int argc, char **argv)
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
 
+	instdir = instdir_init();
 	admdir = admindir_init();
 
 	if (setvbuf(stdout, NULL, _IONBF, 0))
-- 
1.9.1


Reply to: