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

[PATCH 4/6] conffile-database handling functions



conffiles.{c,h} defines a set of routines for adding and removing
stored versions of the pristine conffiles as shipped in their respective
packages.

such an implementation allows for some neat features in the future,
such as:
 - 3-way merging of conffile changes
 - showing the old->new delta for the "dist" conffiles
 - dynamic registering of conffiles (à la ucf)
 - a general dpkg/conffile cmdline interface for all of this

the layout pattern for the conffile database is:

	<admindir>/conffiles[-new]/<pkg>/<hash>

where "-new" signifies a temporary copy of an unpacked/unconfigured
package's configuration files.  <pkg> is the package owning the conffiles
in question, and <hash> is an md5sum of the conffile's path.  this ensures
a flat and normalized layout.

it's assumed that this database is not meant for direct user consumption,
and instead that there will be a provided utility (similar in features
and function to ucf(1) utility) and/or exported library routines for
such interaction.
---
 lib/dpkg/dpkg.h |    1 +
 src/Makefile.am |    1 +
 src/conffiles.c |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/conffiles.h |   50 +++++++++++++++
 4 files changed, 230 insertions(+), 0 deletions(-)
 create mode 100644 src/conffiles.c
 create mode 100644 src/conffiles.h

diff --git a/lib/dpkg/dpkg.h b/lib/dpkg/dpkg.h
index afe650f..335a826 100644
--- a/lib/dpkg/dpkg.h
+++ b/lib/dpkg/dpkg.h
@@ -74,6 +74,7 @@ DPKG_BEGIN_DECLS
 #define STATOVERRIDEFILE  "statoverride"
 #define UPDATESDIR        "updates/"
 #define INFODIR           "info/"
+#define CONFFILESDIR      "conffiles"
 #define TRIGGERSDIR       "triggers/"
 #define TRIGGERSFILEFILE  "File"
 #define TRIGGERSDEFERREDFILE "Unincorp"
diff --git a/src/Makefile.am b/src/Makefile.am
index 820cf1c..0877c4a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,6 +16,7 @@ bin_PROGRAMS = dpkg dpkg-query dpkg-trigger
 dpkg_SOURCES = \
 	archives.c archives.h \
 	cleanup.c \
+	conffiles.c conffiles.h \
 	configure.c \
 	depcon.c \
 	enquiry.c \
diff --git a/src/conffiles.c b/src/conffiles.c
new file mode 100644
index 0000000..fe426d7
--- /dev/null
+++ b/src/conffiles.c
@@ -0,0 +1,178 @@
+#include "conffiles.h"
+
+#include <config.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <dpkg/dpkg.h>
+#include <dpkg/i18n.h>
+#include <dpkg/buffer.h>
+#include <dpkg/path.h>
+#include <dpkg/dpkg-db.h>
+#include "main.h"
+
+/* MD5 string length, minus null byte.  mostly here for readability below */
+#define MD5SUM_LEN 32
+
+/* return a pointer to a char* which has the full on-disk location of
+ * the file in the "conffile db" registered for the file at path.  pass
+ * flags f to indicate whether you want the "new", "old", or "current" version.
+ *
+ * no checking is done that the file actually exists, this is just where it
+ * ought to be.  your job to free() the result.
+ */
+static
+char* conff_db_path(const char *pkg, const char *path, conff_flag f)
+{
+	char *p = NULL, *hash = NULL;
+	const char *ext = "";
+	size_t root_sz = 0, path_sz = 0;
+
+	/* <admindir>/<conffiles[-ext]>/<pkg> + '\0' */
+	root_sz = strlen(admindir) + 1 + strlen(CONFFILESDIR) + 1 + 
+	          strlen(pkg) + 1;
+
+	/* in the case of new/old, we add extra space for the [-ext] */
+	switch (f) {
+	case conff_db_new:
+		ext = NEWDBEXT;
+		root_sz+=strlen(NEWDBEXT);
+		break;
+	case conff_db_cur:
+		ext = "";
+		break;
+	default:
+		ohshit("conff_db_path called with unsupported flags %x", f);
+		break;
+	}
+
+	path_sz = root_sz;
+	/* and if a conffile is being requested (not just the db root)... */
+	if (path != NULL) {
+		/* / + hash */
+		path_sz += (1 + MD5SUM_LEN);
+	} 
+
+	/* this is the path to the conffile db root for pkg */
+	p = m_malloc(path_sz);
+	snprintf(p, root_sz, "%s/%s%s/%s", admindir, CONFFILESDIR, ext, pkg);
+
+	/* append the pathname's hash if relevant */
+	if (path != NULL) {
+		p[root_sz-1] = '/';
+		hash = p + root_sz;
+		buffer_md5(path, hash, strlen(path));
+	}
+
+	debug(dbg_conffdetail, "conff_db_path(%s, %s, %x) = %s\n", pkg, path, 
+	      f, p);
+
+	return p;
+}
+
+/* ensure that the <admindir>/<conffilesdb/><pkg> exists */
+void
+conff_ensure_db(const char *pkg, conff_flag f)
+{
+	struct stat s;
+	char *dbdir = NULL;
+	short dirsmade = 0;
+	size_t base_sz = 0; /* offset of <pkg> in <conffilesdb>/<pkg> */
+
+	dbdir = conff_db_path(pkg, NULL, f);
+	/* to avoid an extra malloc() we reuse the same string twice */
+	base_sz = strlen(dbdir) - (strlen(pkg) + 1);
+	/* and use a creative for loop to prevent a need for copy/paste :) */
+	for (dbdir[base_sz] = '\0'; dirsmade != 2; dirsmade++) {
+		if (stat(dbdir, &s)) {
+			debug(dbg_conffdetail, "conff_ensure_db: creating %s\n",
+			      dbdir);
+			if (errno != ENOENT) 
+				ohshite("conff_ensure_db: stat(%s)", dbdir);
+			if(mkdir(dbdir, S_IRWXU)) 
+				ohshite("conff_ensure_db: mkdir(%s)", dbdir);
+		}
+		dbdir[base_sz] = '/';
+	}
+
+	free(dbdir);
+}
+
+void
+conff_reg_fd(const char *pkg, const char *path, int fd, size_t sz)
+{
+	int cfgfd;
+	/* get the path to where the registered file goes */
+	char *p = conff_db_path(pkg, path, conff_db_new);
+	char fnamebuf[256];
+
+	conff_ensure_db(pkg, conff_db_new);
+	debug(dbg_conff, "conffile_reg_fd: %s, %s, %s\n", pkg, path, p);
+	/* make a mode 600 copy of file to p */
+	cfgfd = open(p, (O_CREAT|O_EXCL|O_WRONLY), (S_IRUSR|S_IWUSR));
+	if (cfgfd < 0) 
+		ohshite(_("unable to create `%.255s'"),p);
+	fd_fd_copy(fd, cfgfd, sz, _("backend dpkg-deb during `%.255s'"),
+	           path_quote_filename(fnamebuf,256,p));
+	if (close(cfgfd))
+		ohshite("can't close %s", p);
+
+	free(p);
+}
+
+int
+conff_get_fd(const char *pkg, const char *path, conff_flag f)
+{
+	int fd = -1;
+	char *p = conff_db_path(pkg, path, f);
+
+	fd = open(p, O_RDONLY);
+	if (fd == -1)
+		ohshite("error opening conffile registered at %s", p);
+
+	free(p);
+	return fd;
+}
+
+void
+conff_commit_new(const char *pkg)
+{
+	/* update the conffiles "db" dir.  this consists of the following steps:
+	 * - nuke the existing current db dir
+	 * - move the new db into place
+	 */
+	char *cfgdb = NULL, *cfgdbnew = NULL;
+	debug(dbg_conff, "conff_commit_new(%s)", pkg);
+
+	cfgdb = conff_db_path(pkg, NULL, conff_db_cur);
+	cfgdbnew = conff_db_path(pkg, NULL, conff_db_new);
+
+	/* make sure <admindir>/<conffdb/>/<pkg> exists, and then nuke <pkg> */
+	conff_ensure_db(pkg, conff_db_cur);
+	ensure_pathname_nonexisting(cfgdb);
+	if (rename(cfgdbnew, cfgdb))
+		ohshite("conff_commit_new: rename(%s,%s)", cfgdbnew, cfgdb);
+
+	free(cfgdb);
+	free(cfgdbnew);
+}
+
+void
+conff_db_remove(const char *pkg, conff_flag f)
+{
+	char *cfgdb = conff_db_path(pkg, NULL, f);
+
+	debug(dbg_conff, "conff_db_remove(%s): removing %s\n", pkg, cfgdb);
+	ensure_pathname_nonexisting(cfgdb);
+
+	free(cfgdb);
+}
+
+/*
+ * vim: noet ts=8 sw=8
+ */
diff --git a/src/conffiles.h b/src/conffiles.h
new file mode 100644
index 0000000..9cf6e5b
--- /dev/null
+++ b/src/conffiles.h
@@ -0,0 +1,50 @@
+/*
+ * conffiles.h
+ *
+ * module for registering, deregistering, and otherwise handling conffiles
+ *
+ */
+#ifndef CONFFILES_H
+#define CONFFILES_H
+
+#include <sys/types.h>
+
+/* conff_flag is used in various conffile db functions to determine whether
+ * to reference the installed package's conffile db or the to-be-installed
+ * package's conffile db
+ */
+typedef enum {
+	conff_db_cur=0x1,
+	conff_db_new=0x2,
+} conff_flag;
+
+/* register a new conffile for package pkg at path... um... path, reading
+ * the contents of the distributed conffile from fd, which is expected to
+ * be sz bytes
+ *
+ * after calling this function it can be expected that the conffile is
+ * "registered" as a new conffile, which will be later processed in the
+ * package configuration stage, where it will be checked for stuff like
+ * new/changed/obsoleted/etc.
+ */
+void conff_reg_fd(const char *pkg, const char *path, int fd, size_t sz);
+
+/* return an fd opened for reading the contents of the "registered" conffile 
+ * which is expected to be installed at path... path from package pkg.  the 
+ * flag f is used to determine which version of the conffile is requested 
+ * (i.e. old/new/current)
+ */
+int conff_get_fd(const char *pkg, const char *path, conff_flag f);
+
+/* remove the on-disk conffile database for package pkg, using the flag f
+ * to determine which version of the database to remove (new/cur/old)
+ */
+void conff_db_remove(const char *pkg, conff_flag f);
+
+/* update the conffiles db so that the latest "new" files are now recorded
+ * as "current" for package pkg.
+ */
+void conff_commit_new(const char *pkg);
+
+#endif /* CONFFILES_H */
+
-- 
1.6.4.3


Reply to: