]> pd.if.org Git - zpackage/commitdiff
improve config file handling
authorNathan Wagner <nw@hydaspes.if.org>
Wed, 5 Dec 2018 10:57:47 +0000 (10:57 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Wed, 5 Dec 2018 10:57:47 +0000 (10:57 +0000)
t/update.t
zpm-syncfs.c

index 2606f70f9046f9c9947f100f8a29ea533427bc09..e97835d151e879859b9e1359fde84c321ed47fd9 100755 (executable)
@@ -95,12 +95,21 @@ require zpm install -R. -f itest-2.0-1.zpm itest-2.0-1
 conf=$(cat etc/conf)
 okstreq "$conf" bar new conf is bar
 
+zpm note -f var/lib/zpm/local.db -l | diagstdin
+
 echo quux > etc/conf
+qhash=$(printf '%.8s' $(zpm hash etc/conf))
 
 require zpm install -R. -f itest-3.0-1.zpm itest-3.0-1
 conf=$(cat etc/conf)
-okstreq "$conf" quux kept conf as quux
+okstreq "$conf" quux kept conf $qhash as quux
+
+zpm note -f var/lib/zpm/local.db -l | diagstdin
+
+ZPMDB=$PWD/var/lib/zpm/local.db
+export ZPMDB
 
+require zpm uninstall -R. itest
 zpm note -f var/lib/zpm/local.db -l | diagstdin
 
 cd ..
index e3d9dbbef73469195e5def7ec6215e4fb47cce72..beff5608ef331083626a1468e7c424de7ea7477e 100644 (file)
@@ -5,6 +5,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <dirent.h>
 #include <limits.h>
 #include <errno.h>
 #include <ctype.h>
@@ -60,6 +61,15 @@ static void usage() {
        printf("usage: zpm $scriptname [-fncC] args ...\n");
 }
 
+static void warn(char *fmt, ...) {
+       va_list args;
+
+       va_start(args, fmt);
+       vfprintf(stderr, fmt, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+}
+
 static void pdots(int len, int ch, int was, int now, int total) {
        was = len * was / total;
        if (now > total) {
@@ -431,6 +441,8 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols,
  * the the hash of the file on disk
  */
 #define D_OHASH 0x400
+/* file exists, and is a directory, and is empty */
+#define D_ISEMPTY 0x800
 /* an error occurred trying to compare the file (other than it doesn't exist */
 #define D_ERROR 0x1000
 /* there was a stat error */
@@ -438,6 +450,27 @@ static int read_item(struct config *conf, int ncols, char **vals, char **cols,
 /* there was an error calling readlink */
 #define D_RLERROR 0x4000
 
+static int dir_is_empty(char *path) {
+       DIR *dir;
+       struct dirent *dp;
+       int empty;
+
+       dir = opendir(path);
+       if (!dir) {
+               return -1;
+       }
+
+       dp = readdir(dir);
+       if (dp) {
+               empty = 0;
+       } else {
+               empty = 1;
+       }
+       closedir(dir);
+
+       return empty;
+}
+
 /* 1 = file doesn't exist, 2 = file is a directory, target isn't */
 /* 4 == ftype different */
 /* 8 = hash different when both are regular files */
@@ -464,6 +497,9 @@ static unsigned int file_compare(struct nitem *n, struct stat *st) {
                }
                if (stat_type == S_IFDIR) {
                        diff |= D_EISDIR;
+                       if (dir_is_empty(n->dest)) {
+                               diff |= D_ISEMPTY;
+                       }
                }
 
                if (n->hash && etype == S_IFREG && stat_type == S_IFREG) {
@@ -590,10 +626,16 @@ static int check_existing(void *f, int ncols, char **vals, char **cols) {
        }
 
        unsigned int diffs = file_compare(&nitem, &st);
+       int sametype = (!(diffs & D_TYPE));
+
        if (diffs >= D_ERROR) {
                return seterror(conf, "can't check %s", nitem.dest);
        }
 
+       if (sametype && nitem.configuration) {
+               return 0;
+       }
+
        int action = acceptable(conf, diffs, nitem.op);
        if (!action) {
                if (conf->accept) {
@@ -660,7 +702,6 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) {
        }
        /* TODO check that expected filetype matches actual filetype */
 
-
        errno = 0;
 
        if (unlinkat(AT_FDCWD, dest, flags) == -1) {
@@ -681,7 +722,6 @@ static int remove_files(void *f, int ncols, char **vals, char **cols) {
 
 #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__)
 
-
 static int remove_dir(struct config *conf, char *path) {
        int rv;
 
@@ -775,7 +815,7 @@ static int install(struct config *conf, struct nitem *item, unsigned int flags)
        int failure = conf->errabort;
        int success = 0;
 
-       if (flags & 16) {
+       if (flags & INS_RTF) {
                failure = 0;
                success = 1;
        }
@@ -854,6 +894,24 @@ static int install(struct config *conf, struct nitem *item, unsigned int flags)
        return success;
 }
 
+static int save_config_file(struct config *conf, struct nitem *n, char *msgfmt) {
+       char hash[ZPM_HASH_STRLEN+1];
+
+       if (!msgfmt) {
+               msgfmt = "saved config file %.8s";
+       }
+
+       if (zpm_import(conf->log, n->dest, 0, hash)) {
+               zpm_note_add(conf->log, n->pkglist, n->path, hash, msgfmt, hash);
+       } else {
+               warn("unable to import existing config file %s", n->dest);
+               conf->errors++;
+               return 0;
+       }
+       return 1;
+}
+
+#if 0
 /*
  * figure out what the difference is for a config file, only called
  * for an update of a configuration file
@@ -861,67 +919,33 @@ static int install(struct config *conf, struct nitem *item, unsigned int flags)
  * return 1 if the new file should not be installed
  * return 0 if the new file should be installed
  */
-static int adjust_for_config(struct config *conf, struct nitem *n, unsigned int
-               diffs) {
+static int adjust_for_config(struct nitem *n, unsigned int diffs) {
 
        if (!n->oldwasconf) {
                return 0;
        }
+
+       int sametype = (!(diffs & D_TYPE));
+       int isdir = (diffs & D_ISDIR);
+       int eisdir = (diffs & D_EISDIR);
        
        /* TODO what if old was a directory? */
        if (!n->configuration) {
                /* replacing conf with non-conf */
                /* absorb file, mark todo */
-               char hash[ZPM_HASH_STRLEN+1];
-               if (!conf->dryrun) {
-                       if (conf->verbose) {
-                               fprintf(stderr, "importing old conf file\n");
-                       }
-                       if (zpm_import(conf->log, n->dest, 0, hash)) {
-                               zpm_note_add(conf->log, n->pkglist, n->dest, hash,
-                                               "replaced config file with non-config.  zpm-cat %.8s", hash);
-                       } else {
-                               fprintf(stderr, "unable to import existing config file %s\n", n->dest);
-                               return -1;
-                       }
-               } else {
-                       fprintf(stderr, "dry-run: would replace config file %s with non-config file\n", n->dest);
-               }
                return 0;
        }
 
-       int sametype = (!(diffs & D_TYPE));
-       int isdir = (diffs & D_ISDIR);
-       int eisdir = (diffs & D_EISDIR);
-
        /* both old and new are config files */
        if (isdir && sametype) {
                /* both config directories, can only be changing
                 * metadata, so no adjustment needed
                 */
-                       if (conf->verbose) {
-                               fprintf(stderr, "both config dirs, ok to update\n");
-                       }
                return 0;
        }
 
        if (isdir) {
-               char hash[ZPM_HASH_STRLEN+1];
-
-               /* replacing old file with new directory */
-               /* absorb, make note */
-               if (!conf->dryrun) {
-                       if (zpm_import(conf->log, n->dest, 0, hash)) {
-                               zpm_note_add(conf->log, n->pkglist, n->dest, hash,
-                                               "replaced config file with config directory.  zpm-cat %.8s", hash);
-                       } else {
-                               fprintf(stderr, "unable to import existing config file %s\n", n->dest);
-                               return -1;
-                       }
-               } else {
-                       fprintf(stderr, "dry-run: would replace config file %s with config directory\n", n->dest);
-               }
-                       return 0;
+               return 0;
        }
 
        if (eisdir) {
@@ -936,37 +960,185 @@ static int adjust_for_config(struct config *conf, struct nitem *n, unsigned int
        /* replacing old file with new file */
        /* new is same as on disk */
        if (!(diffs & D_HASH)) {
-                       if (conf->verbose) {
-                               fprintf(stderr, "new config file is already on disk, probably shouldn't happen\n");
-                       }
                return 0;
        }
 
        /* new is different than on disk, but on disk is same as old */
        if (!(diffs & D_OHASH)) {
-                       if (conf->verbose) {
-                               fprintf(stderr, "old config file not changed from default, replacing with new default\n");
-                       }
-               /* ok to do the update, since same as default */
-               fprintf(stderr, "updating default config %s\n", n->dest);
                return 0;
        }
 
-       /* new is different than on disk, and disk different than old */
-       /* log */
-       if (conf->verbose) {
-               fprintf(stderr, "new default config file is different than on disk, and old default was changed, should keep on-disk config\n");
+       return 1;
+
+}
+#endif
+
+static int config_handler(void *f, int ncols, char **vals, char **cols) {
+       struct config *conf = f;
+       struct nitem nitem;
+       struct stat existing;
+       char *save = 0;
+       char *note = 0;
+       char *notehash = 0;
+       int update = 0;
+
+       if (!read_item(conf, ncols, vals, cols, &nitem)) {
+               fprintf(stderr, "can't read item\n");
+               conf->errors++;
+               return conf->errabort;
        }
+
+       unsigned int diffs = file_compare(&nitem, &existing);
+       if (diffs >= D_ERROR) {
+               return seterror(conf, "can't check %s", nitem.dest);
+       }
+
+       int exist = (!(diffs & D_NOEXIST));
+       int sametype = (!(diffs & D_TYPE));
+       //int mdsame = (!(diffs & D_MD));
+       int hashsame = (!(diffs & D_HASH));
+       int oldhashsame = (!(diffs & D_OHASH));
+       int isdir = (diffs & D_ISDIR);
+       int eisdir = (diffs & D_EISDIR);
+       update = (nitem.op == OP_UPDATE);
+
+       notehash = nitem.hash;
+
+       /* if the file doesn't exist in the system, nothing to do */
+       /* could possibly note if we expected it, but the regular handling
+        * should do that
+        */
+       if (!exist) {
+               return 0;
+       }
+
+       if (nitem.op == OP_UPDATE && !nitem.oldwasconf) {
+               /* possibly save anyway */
+               return 0;
+       }
+
+       /* so, old was conf, and something exists in the filesystem */
+
+       if (!sametype) {
+               warn("won't %s %s%s, %s exists",
+                               nitem.op == OP_NEW ? "install" : nitem.opstr,
+                               nitem.path,
+                               isdir ? "/" : "",
+                               eisdir ? "directory" : "file"
+                   );
+               conf->errors++;
+               return conf->errabort;
+       }
+
+       /* all below are same type of file */
+       /* what about sametype, but old was different type */
+
+       if (isdir) {
+               return 0;
+       }
+
+       /* save or note cases */
+
+       if (nitem.op == OP_REMOVE) {
+               save ="saved removed config file %.8s";
+       } else 
+
+       if (nitem.op == OP_UPDATE) {
+               if (!nitem.configuration) {
+                       /* replacing config with non-config */
+                       save = "replacing configuration file %.8s with non-configuration file";
+               } else if (oldhashsame) {
+                       /* config file hasn't changed from old default,
+                        * so go ahead and install the new one
+                        */
+                       save = "replaced old default config (%.8s) with new one.";
+                       notehash = nitem.ohash;
+               } else {
+                       note = "kept existing config file.  new default version is %.8s";
+                       save = 0;
+               }
+       } else
+
+       if (nitem.op == OP_NEW && !hashsame) {
+               note = "config file already existed.  would have installed %.8s";
+               save = 0;
+       }
+
+       /*
+        * save files, add notes
+        */
        if (!conf->dryrun) {
-               zpm_note_add(conf->log, n->pkglist, n->dest, n->hash,
-                               "default config file update.  zpm-cat %.8s", n->hash);
-               /* TODO check for note error */
+               if (save) {
+                       warn("saving config file: %s (root %s)", nitem.path, conf->rootdir ? conf->rootdir : "/");
+                       save_config_file(conf, &nitem, save);
+               }
+               if (note) {
+                       zpm_note_add(conf->log, nitem.pkglist, nitem.path, nitem.hash,
+                                       note, nitem.hash);
+               }
        } else {
-               fprintf(stderr, "dry-run: default config file %s update\n",
-                               n->dest);
+               if (save) {
+                       fprintf(stderr, "dry run: %s %s: ", nitem.pkglist,
+                                       nitem.path);
+                       warn(save, notehash);
+               }
+               if (note) {
+                       fprintf(stderr, "dry run: %s %s: ", nitem.pkglist,
+                                       nitem.path);
+                       warn(note, notehash);
+               }
+
        }
-       return 1;
 
+       return 0;
+}
+
+static void handle_config_files(struct config *conf) {
+       int rv;
+       char *errmsg;
+       sqlite3_str *s;
+       char *sql;
+
+       s = sqlite3_str_new(conf->log->db);
+       sqlite3_str_appendall(s, "select *, ");
+       if (conf->rootdir) {
+               sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
+       } else {
+               sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
+       }
+       sqlite3_str_appendall(s, " as dest from syncinfo");
+
+       sqlite3_str_appendall(s," where configuration > 0");
+
+       if (conf->reverse) {
+               sqlite3_str_appendall(s," order by length(path) desc, path desc");
+       }
+
+       sql = sqlite3_str_value(s);
+
+       rv = zpm_exec(conf->log, sql, config_handler, conf, &errmsg);
+
+       sqlite3_str_finish(s);
+
+       if (rv) {
+               fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
+               if (errmsg) {
+                       fprintf(stderr, "database error: %s\n", errmsg);
+                       conf->errors++;
+               }
+               if (conf->log->error == 1) {
+                       fprintf(stderr, "unable to allocate memory\n");
+               }
+               fprintf(stderr, "zpm_exec failure: %s\n",
+                               conf->log->errmsg ? conf->log->errmsg : "unknown");
+               conf->errors++;
+       }
+
+       if (conf->errors && conf->exitonerror) {
+               zpm_close(conf->log);
+               zpm_close(conf->src);
+               exit(EXIT_FAILURE);
+       }
 }
 
 static int install_files(void *f, int ncols, char **vals, char **cols) {
@@ -1037,6 +1209,7 @@ static int install_files(void *f, int ncols, char **vals, char **cols) {
        int sametype = (!(diffs & D_TYPE));
        int mdsame = (!(diffs & D_MD));
        int hashsame = (!(diffs & D_HASH));
+       int ohashsame = (!(diffs & D_OHASH));
        int isdir = (diffs & D_ISDIR);
        int eisdir = (diffs & D_EISDIR);
        int accept = conf->accept;
@@ -1044,6 +1217,27 @@ static int install_files(void *f, int ncols, char **vals, char **cols) {
        int installing = (nitem.op == OP_NEW);
        update = (nitem.op == OP_UPDATE);
 
+       /* if a config file doesn't exist on disk, go ahead and do
+        * whatever you were going to do, this logic here just
+        * needs to determine if we should skip what we were going to do
+        *
+        * if the old item was a configuration item and the new one isn't, it
+        * will have been saved earlier, so we can just go ahead.  so we only
+        * test for an existing file, where the item is a configuration file
+        */
+       if (nitem.configuration && exist) {
+               if (!sametype && !conf->overwrite) {
+                       return seterror(conf, "configuration file exists with different type: %s", nitem.dest);
+               }
+
+               if (conf->accept) {
+                       return 0;
+               }
+
+               if (isdir && !mdsame) return 0;
+               if (!isdir && !ohashsame) return 0;
+       }
+
        if (update) {
                if (!exist) {
                        /* warn, it should exist */
@@ -1051,14 +1245,6 @@ static int install_files(void *f, int ncols, char **vals, char **cols) {
                        return install(conf, &nitem, 3);
                }
 
-               switch (adjust_for_config(conf, &nitem, diffs)) {
-                       case -1: return conf->errabort; break;
-                       case 1:
-                       fprintf(stderr, "skipping changed default config file: %s\n", nitem.dest);
-                       return 0; break;
-                       default: break;
-               }
-
                /* file exists in filesystem */
                if (sametype) {
                        if (mdsame && hashsame) {
@@ -1488,6 +1674,11 @@ int main(int ac, char **av) {
                        fprintf(stderr, "beginning %ssync\n", conf.dryrun ?
                                        "dryrun " : "");
                }
+
+               if (!conf.errors) {
+                       handle_config_files(&conf);
+               }
+
                /* have to do the removes first otherwise
                 * old files may conflict with update file
                 * type changes