1 #define _POSIX_C_SOURCE 200809L
18 /* needed for S_IFMT and AT_FDCWD */
27 struct zpm *log; /* logging db will be attached as "log" */
31 int errabort, errors, verbose, dryrun, conflicts;
32 int setuser, setgroup;
33 int reverse, exitonerror;
34 int overwrite, accept, acceptdir, ignoredirmd;
35 int ops_total, ops_completed;
36 int ops_remove, ops_remove_completed;
37 int ops_update, ops_update_completed;
38 int ops_install, ops_install_completed;
39 int progress; /* type of progress meter */
52 char *pkglist; /* space separated */
56 int configuration, oldwasconf;
57 struct timespec times[2];
61 printf("usage: zpm $scriptname [-fncC] args ...\n");
64 static void warn(char *fmt, ...) {
68 vfprintf(stderr, fmt, args);
70 fprintf(stderr, "\n");
73 static void pdots(int len, int ch, int was, int now, int total) {
74 was = len * was / total;
78 now = len * now / total;
85 static int seterror(struct config *conf, char *msgfmt, ...) {
92 vsnprintf(msg, sizeof msg, msgfmt, ap);
96 if (conf->log->errmsg) {
97 free(conf->log->errmsg);
100 conf->log->errmsg = strdup(msg);
103 fprintf(stderr, "%s\n", msg);
106 return conf->errabort;
109 static int setsyserr(struct config *conf, char *msgfmt, ...) {
116 va_start(ap, msgfmt);
117 printed = vsnprintf(msg, sizeof msg, msgfmt, ap);
121 /* nothing we can really do */
122 return conf->errabort;
125 if ((size_t)printed < sizeof msg) {
126 snprintf(msg+printed, sizeof msg - printed, ": %s",
131 if (conf->log->errmsg) {
132 free(conf->log->errmsg);
135 conf->log->errmsg = strdup(msg);
138 fprintf(stderr, "%s\n", msg);
141 return conf->errabort;
144 static int exists(char *path, mode_t *mode) {
147 if (lstat(path, &st) == -1) {
150 if (mode) *mode = st.st_mode;
154 /* TODO maintain a list of already created directories */
155 static int create_leading_dirs(char *path) {
158 char pcopy[ZPM_PATH_MAX];
163 delim = strrchr(pcopy, '/');
164 if (!delim) return 1; /* not an error, but no leading dirs */
166 /* cut off last component */
175 delim = strchr(s, '/');
181 /* try to create the directory, if it exists
182 * and is a directory or a symlink, that's ok
183 * should be (eventually) a symlink to a directory
184 * so we want stat here, not lstat
186 if (mkdir(pcopy, 0755) == -1) {
189 if (stat(pcopy, &st) == -1) {
193 switch (st.st_mode & S_IFMT) {
213 static char *column(char *col, int ncols, char **vals, char **cols) {
217 for (i=0; i < ncols; i++) {
218 // fprintf(stderr, "checking '%s' = '%s'\n", cols[i], vals[i]);
220 if (!strcmp(col, cols[i])) {
228 #define COL(x) column(x, ncols, vals, cols)
229 #define SYSERR(x) do { conf->log->error = 2; return conf->errabort; } while (0)
232 static char *ops[] = { "new", "remove", "update", 0 };
240 static int getop(char *opstr) {
243 if (!opstr) return 0;
244 for (i=0;ops[i];i++) {
245 if (!strcmp(opstr, ops[i])) {
252 static int report_conflicts(void *f, int ncols, char **vals, char **cols) {
253 struct config *conf = f;
254 char *path, *hash, *pkg, *conflict_type, *mds;
258 conflict_type = COL("conflict");
259 if (!strcmp(conflict_type, "hash")) {
261 fprintf(stderr, "hash conflict: package %s path %s hash %.8s\n",
264 if (!strcmp(conflict_type, "md")) {
266 fprintf(stderr, "md conflict: package %s path %s md %s\n",
269 fprintf(stderr, "%s conflict: package %s path %s\n",
270 conflict_type, pkg, path);
277 static int read_item(struct config *conf, int ncols, char **vals, char **cols,
283 struct nitem zero = { 0 };
289 seterror(conf, "can't determine op");
295 seterror(conf, "can't determine op");
299 n->path = COL("path");
301 seterror(conf, "no file path");
304 if (strlen(n->path) == 0) {
305 seterror(conf, "zero length path not allowed");
309 /* TODO config to dishonor setuid/setgid */
310 n->dest = COL("dest");
312 seterror(conf, "no file dest");
316 if (strlen(n->dest) == 0) {
317 seterror(conf, "zero length dest not allowed");
324 seterror(conf, "can't determine mode");
328 n->mode = strtoul(val, NULL, 8);
330 val = COL("configuration");
332 seterror(conf, "can't determine config status");
335 lval = strtol(val, NULL, 10);
337 n->configuration = ((lval & 1) != 0);
338 n->oldwasconf = ((lval & 2) != 0);
340 val = COL("filetype");
341 if (!val || strlen(val) == 0) {
342 seterror(conf, "can't determine file type");
347 /* these can be null */
348 n->ohash = COL("ohash");
350 n->omds = COL("omds");
351 n->pkglist = COL("pkglist");
353 if (n->ftype == 'r') {
354 n->hash = COL("hash");
356 seterror(conf, "can't get hash");
359 } else if (n->ftype == 'l') {
360 n->target = COL("target");
362 seterror(conf, "can't get target");
365 if (strlen(n->target) == 0) {
366 seterror(conf, "zero length target not allowed");
373 val = COL("username");
375 seterror(conf, "no username");
380 seterror(conf, "no passwd entry");
388 if (conf->setgroup) {
389 val = COL("groupname");
391 seterror(conf, "no groupname");
396 seterror(conf, "no group entry for %s", val);
405 double mtime = strtod(COL("mtime"),NULL);
407 mtime = (double)time(NULL);
410 n->mtime = (time_t)mtime;
412 n->times[0].tv_sec = 0;
413 n->times[0].tv_nsec = UTIME_OMIT;
414 n->times[1].tv_sec = (time_t)llrint(floor(mtime));
415 n->times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000));
420 /* file does not exist */
421 #define D_NOEXIST 0x1
422 /* files are different types */
424 /* metadata is different */
426 /* content or link target is different */
428 /* file to be installed is a directory */
430 /* path on disk is a directory */
431 #define D_EISDIR 0x20
432 /* usernames different */
434 /* group names different */
436 /* file mode is different */
438 /* mtimes are different */
439 #define D_MTIME 0x200
440 /* the hash of the file we are supposedly replacing is different than
441 * the the hash of the file on disk
443 #define D_OHASH 0x400
444 /* file exists, and is a directory, and is empty */
445 #define D_ISEMPTY 0x800
446 /* an error occurred trying to compare the file (other than it doesn't exist */
447 #define D_ERROR 0x1000
448 /* there was a stat error */
449 #define D_STATERROR 0x2000
450 /* there was an error calling readlink */
451 #define D_RLERROR 0x4000
453 static int dir_is_empty(char *path) {
474 /* 1 = file doesn't exist, 2 = file is a directory, target isn't */
475 /* 4 == ftype different */
476 /* 8 = hash different when both are regular files */
477 static unsigned int file_compare(struct nitem *n, struct stat *st) {
478 int etype = 0, stat_type;
479 char ehash[ZPM_HASH_STRLEN+1];
480 unsigned int diff = 0;
485 case 'd': etype = S_IFDIR; diff |= D_ISDIR ; break;
486 case 'r': etype = S_IFREG; break;
487 case 'l': etype = S_IFLNK; break;
488 default: etype = 0; break;
492 /* new file, so check type, hash, etc */
493 if (lstat(n->dest, st) == 0) {
494 stat_type = st->st_mode & S_IFMT;
495 if (stat_type != etype) {
498 if (stat_type == S_IFDIR) {
500 if (dir_is_empty(n->dest)) {
505 if (n->hash && etype == S_IFREG && stat_type == S_IFREG) {
506 zpm_hash(n->dest, ehash, 0);
507 if (strcmp(n->hash, ehash) != 0) {
510 if (n->ohash && strcmp(n->ohash, ehash) != 0) {
514 if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) {
515 lsize = readlink(n->dest, link, sizeof link);
517 if (lsize == -1 || lsize == sizeof link) {
522 if (strcmp(n->target, link) != 0) {
527 if (n->uid != st->st_uid) {
531 if (n->gid != st->st_gid) {
535 if (n->mode != (st->st_mode & 07777)) {
541 case ENOENT: diff |= D_NOEXIST; break;
542 default: diff |= (D_STATERROR|D_ERROR); break;
550 /* 0 = not acceptable
551 * 1 = accept and create/update/remove
553 * 3 = remove and overwrite
554 * 4 = update metadata
556 static int acceptable(struct config *conf, unsigned int diffs, int op) {
557 int exist = (!(diffs & D_NOEXIST));
558 int sametype = (!(diffs & D_TYPE));
559 int mdsame = (!(diffs & D_MD));
560 int hashsame = (!(diffs & D_HASH));
561 int isdir = (diffs & D_ISDIR);
564 return op == OP_REMOVE ? 2 : 1;
567 if (op == OP_UPDATE) {
568 return sametype ? 4 : 3;
571 if (op == OP_REMOVE) {
575 /* the hard cases, should be installing new, but already exists */
578 return conf->overwrite ? 3 : 0;
581 if (mdsame && (conf->accept || conf->overwrite)) {
586 if (mdsame || conf->ignoredirmd) {
587 return conf->acceptdir ? 2 : 0;
589 if (conf->overwrite) {
594 if (hashsame && (conf->accept || conf->overwrite)) {
598 return conf->overwrite ? 3 : 0;
601 static int check_existing(void *f, int ncols, char **vals, char **cols) {
602 struct config *conf = f;
606 if (!read_item(conf, ncols, vals, cols, &nitem)) {
607 fprintf(stderr, "can't read item\n");
608 return conf->errabort;
611 if (conf->verbose > 1) {
612 fprintf(stderr, "check for existing %s\n", nitem.path);
615 if (lstat(nitem.path, &st) == -1) {
617 /* not an error, file shouldn't exist*/
620 fprintf(stderr, "unable to check %s: %s\n",
621 nitem.path, strerror(errno));
628 unsigned int diffs = file_compare(&nitem, &st);
629 int sametype = (!(diffs & D_TYPE));
631 if (diffs >= D_ERROR) {
632 return seterror(conf, "can't check %s", nitem.dest);
635 if (sametype && nitem.configuration) {
639 int action = acceptable(conf, diffs, nitem.op);
642 fprintf(stderr, "%s exists and is not acceptable\n", nitem.path);
644 fprintf(stderr, "%s exists\n", nitem.path);
652 static int remove_files(void *f, int ncols, char **vals, char **cols) {
653 struct config *conf = f;
659 if (!dest) return seterror(conf,"no file dest");
662 char *ftype = COL("filetype");
666 case 'd': printf("rmdir %s\n", dest); break;
667 default: printf("unlink %s\n", dest); break;
674 if (conf->progress == 2) {
675 fprintf(stderr, "%s(%s)\n", flags ? "rmdir" : "unlink", dest);
676 } else if (conf->progress == 1) {
678 pdots(50, '.', conf->ops_completed, conf->ops_completed + 1, conf->ops_total);
679 conf->ops_completed++;
680 conf->ops_completed++;
682 pdots(50, '.', conf->ops_completed, conf->ops_completed + 1, conf->ops_total);
683 conf->ops_completed++;
689 if (lstat(dest, &st) == -1) {
692 /* TODO chatter if verbose */
695 return seterror(conf, "can't stat %s: %s", dest, strerror(errno));
700 if (S_ISDIR(st.st_mode)) {
701 flags = AT_REMOVEDIR;
703 /* TODO check that expected filetype matches actual filetype */
707 if (unlinkat(AT_FDCWD, dest, flags) == -1) {
711 case ENOTEMPTY: /* fall through */
713 /* TODO chatter, or possibly require */
716 return seterror(conf, "can't unlink %s: %s", dest, strerror(errno));
723 #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__)
725 static int remove_dir(struct config *conf, char *path) {
730 setsyserr(conf, "can't rmdir %s", path);
736 static int remove_existing(struct config *conf, char *path) {
741 setsyserr(conf, "can't unlink %s", path);
747 static int set_md(struct config *conf, struct nitem *item) {
752 if (item->ftype != 'l') {
753 printf("chmod %o %s\n", item->mode, item->dest);
755 if (conf->setuser && conf->setgroup) {
756 printf("lchown %d:%d %s\n", item->uid, item->gid,
759 printf("mtime %.0f %s\n", (double)item->mtime, item->dest);
764 if (conf->setuser && conf->setgroup) {
765 rv = lchown(item->dest, item->uid, item->gid);
767 setsyserr(conf, "can't lchown %s", item->dest);
768 return conf->errabort;
772 /* have to chmod after the chown, setuid bits may (and will)
773 * be cleared after a chown
775 /* can't chmod a symlink */
776 if (item->ftype != 'l') {
777 rv = chmod(item->dest, item->mode);
780 setsyserr(conf, "can't chmod %o %s", item->mode, item->dest);
781 return conf->errabort;
786 rv = utimensat(AT_FDCWD, item->dest, item->times, AT_SYMLINK_NOFOLLOW);
788 setsyserr(conf, "can't set mtime %.0f %s", (double)item->mtime,
790 return conf->errabort;
795 /* install a file or create a directory or symlink. path should not exist
798 /* flags: 1 = set md, 2 = create leading dirs, 4 = unlink existing file,
799 * 8 = rmdir existing dir, 16 = return true/false
803 #define INS_UNLINK 0x4
804 #define INS_RMDIR 0x8
806 #define INS_ZPMNEW 0x20
807 static int install(struct config *conf, struct nitem *item, unsigned int flags) {
811 int mkleading = (flags & 2);
812 int setmd = (flags & 1);
813 int unlink_file = (flags & 4);
814 int rm_dir = (flags & 8);
815 int failure = conf->errabort;
818 if (flags & INS_RTF) {
825 printf("unlink %s\n", item->dest);
827 printf("rmdir %s\n", item->dest);
830 printf("install %c%o %d:%d %s", item->ftype,
831 item->mode, item->uid, item->gid,
833 if (item->ftype == 'l') {
834 printf(" -> %s", item->target);
841 source = conf->src ? conf->src : conf->log;
844 rv = remove_existing(conf, item->dest);
846 rv = remove_dir(conf, item->dest);
854 rv = create_leading_dirs(item->dest);
856 setsyserr(conf, "can't create leading dirs for %s", item->dest);
862 switch (item->ftype) {
863 case 'r': rv = zpm_extract(source, item->hash, item->dest, item->mode);
864 if (rv == 0) rv = -1;
866 case 'd': rv = mkdir(item->dest, item->mode);
868 case 'l': rv = symlink(item->target, item->dest);
875 switch (item->ftype) {
877 seterror(conf, "can't extract %s", item->dest);
880 setsyserr(conf, "install mkdir(\"%s\") failed", item->dest);
883 setsyserr(conf, "install symlink(\"%s\") failed", item->dest);
886 setsyserr(conf, "installing %s failed", item->dest);
891 return set_md(conf, item) == 0 ? success : failure;
897 static int save_config_file(struct config *conf, struct nitem *n, char *msgfmt) {
898 char hash[ZPM_HASH_STRLEN+1];
901 msgfmt = "saved config file %.8s";
904 if (zpm_import(conf->log, n->dest, 0, hash)) {
905 zpm_note_add(conf->log, n->pkglist, n->path, hash, msgfmt, hash);
907 warn("unable to import existing config file %s", n->dest);
916 * figure out what the difference is for a config file, only called
917 * for an update of a configuration file
918 * return -1 on an error
919 * return 1 if the new file should not be installed
920 * return 0 if the new file should be installed
922 static int adjust_for_config(struct nitem *n, unsigned int diffs) {
924 if (!n->oldwasconf) {
928 int sametype = (!(diffs & D_TYPE));
929 int isdir = (diffs & D_ISDIR);
930 int eisdir = (diffs & D_EISDIR);
932 /* TODO what if old was a directory? */
933 if (!n->configuration) {
934 /* replacing conf with non-conf */
935 /* absorb file, mark todo */
939 /* both old and new are config files */
940 if (isdir && sametype) {
941 /* both config directories, can only be changing
942 * metadata, so no adjustment needed
952 /* replacing old conf directory with a conf file.
953 * nothing needs to be done, if the directory
954 * is empty, it's ok to remove. if it's not empty,
955 * the install will fail
960 /* replacing old file with new file */
961 /* new is same as on disk */
962 if (!(diffs & D_HASH)) {
966 /* new is different than on disk, but on disk is same as old */
967 if (!(diffs & D_OHASH)) {
976 static int config_handler(void *f, int ncols, char **vals, char **cols) {
977 struct config *conf = f;
979 struct stat existing;
985 if (!read_item(conf, ncols, vals, cols, &nitem)) {
986 fprintf(stderr, "can't read item\n");
988 return conf->errabort;
991 unsigned int diffs = file_compare(&nitem, &existing);
992 if (diffs >= D_ERROR) {
993 return seterror(conf, "can't check %s", nitem.dest);
996 int exist = (!(diffs & D_NOEXIST));
997 int sametype = (!(diffs & D_TYPE));
998 //int mdsame = (!(diffs & D_MD));
999 int hashsame = (!(diffs & D_HASH));
1000 int oldhashsame = (!(diffs & D_OHASH));
1001 int isdir = (diffs & D_ISDIR);
1002 int eisdir = (diffs & D_EISDIR);
1003 update = (nitem.op == OP_UPDATE);
1005 notehash = nitem.hash;
1007 /* if the file doesn't exist in the system, nothing to do */
1008 /* could possibly note if we expected it, but the regular handling
1015 if (nitem.op == OP_UPDATE && !nitem.oldwasconf) {
1016 /* possibly save anyway */
1020 /* so, old was conf, and something exists in the filesystem */
1023 warn("won't %s %s%s, %s exists",
1024 nitem.op == OP_NEW ? "install" : nitem.opstr,
1027 eisdir ? "directory" : "file"
1030 return conf->errabort;
1033 /* all below are same type of file */
1034 /* what about sametype, but old was different type */
1040 /* save or note cases */
1042 if (nitem.op == OP_REMOVE) {
1043 save ="saved removed config file %.8s";
1046 if (nitem.op == OP_UPDATE) {
1047 if (!nitem.configuration) {
1048 /* replacing config with non-config */
1049 save = "replacing configuration file %.8s with non-configuration file";
1050 } else if (oldhashsame) {
1051 /* config file hasn't changed from old default,
1052 * so go ahead and install the new one
1054 save = "replaced old default config (%.8s) with new one.";
1055 notehash = nitem.ohash;
1057 note = "kept existing config file. new default version is %.8s";
1062 if (nitem.op == OP_NEW && !hashsame) {
1063 note = "config file already existed. would have installed %.8s";
1068 * save files, add notes
1070 if (!conf->dryrun) {
1072 warn("saving config file: %s (root %s)", nitem.path, conf->rootdir ? conf->rootdir : "/");
1073 save_config_file(conf, &nitem, save);
1076 zpm_note_add(conf->log, nitem.pkglist, nitem.path, nitem.hash,
1081 fprintf(stderr, "dry run: %s %s: ", nitem.pkglist,
1083 warn(save, notehash);
1086 fprintf(stderr, "dry run: %s %s: ", nitem.pkglist,
1088 warn(note, notehash);
1096 static void handle_config_files(struct config *conf) {
1102 s = sqlite3_str_new(conf->log->db);
1103 sqlite3_str_appendall(s, "select *, ");
1104 if (conf->rootdir) {
1105 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1107 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1109 sqlite3_str_appendall(s, " as dest from syncinfo");
1111 sqlite3_str_appendall(s," where configuration > 0");
1113 if (conf->reverse) {
1114 sqlite3_str_appendall(s," order by length(path) desc, path desc");
1117 sql = sqlite3_str_value(s);
1119 rv = zpm_exec(conf->log, sql, config_handler, conf, &errmsg);
1121 sqlite3_str_finish(s);
1124 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1126 fprintf(stderr, "database error: %s\n", errmsg);
1129 if (conf->log->error == 1) {
1130 fprintf(stderr, "unable to allocate memory\n");
1132 fprintf(stderr, "zpm_exec failure: %s\n",
1133 conf->log->errmsg ? conf->log->errmsg : "unknown");
1137 if (conf->errors && conf->exitonerror) {
1138 zpm_close(conf->log);
1139 zpm_close(conf->src);
1144 static int install_files(void *f, int ncols, char **vals, char **cols) {
1145 struct config *conf = f;
1147 struct stat existing;
1150 /* TODO put the result row in a hash table. May not actually
1153 if (!read_item(conf, ncols, vals, cols, &nitem)) {
1154 fprintf(stderr, "can't read item\n");
1155 return conf->errabort;
1160 used = sqlite3_memory_used()/1024/1024;
1161 high = sqlite3_memory_highwater(0)/1024/1024;
1162 fprintf(stderr, "memory = %ld MB / %ld MB\n", used, high);
1164 if (conf->verbose && !conf->dryrun) {
1165 if (conf->progress == 2) {
1166 fprintf(stderr, "%s '%c' %s\n", nitem.opstr, nitem.ftype,
1168 } else if (conf->progress == 1) {
1170 pdots(50, '.', conf->ops_completed, conf->ops_completed + 1, conf->ops_total);
1171 conf->ops_completed++;
1172 conf->ops_completed++;
1174 pdots(50, '.', conf->ops_completed, conf->ops_completed + 1, conf->ops_total);
1175 conf->ops_completed++;
1179 unsigned int diffs = file_compare(&nitem, &existing);
1180 if (diffs >= D_ERROR) {
1181 return seterror(conf, "can't check %s", nitem.dest);
1185 * exist & same type & md same & hash same: do nothing, but warn bug
1186 * exist & same type & md diff & hash same: fix md
1187 * exist & same type & md same & hash diff: replace
1188 * exist & same type & md diff & hash diff: replace & fix
1189 * no exist: install and warn
1190 * dir & not dir : remove, mkdir
1191 * not dir & not dir & diff type: remove, install
1192 * not dir & dir : remove dir if empty, error if not empty, install
1195 * no exist: create leading dirs, install
1197 * exist & same type & md same & hash same & accept or over: do nothing
1198 * exist & same & md diff or hash diff & overwrite : update
1199 * exist & same & md diff or hash diff & accept : error, can't accept
1200 * exist & same & md diff or hash diff & not accept : error
1202 * exist & different type & not overwrite : error
1203 * not dir & not dir & overwrite : remove and install
1204 * not dir & dir & overwrite: remove empty or error, install
1205 * dir & dir & overwrite: fix md
1206 * dir & not dir & overwrite: remove and mkdir
1208 int exist = (!(diffs & D_NOEXIST));
1209 int sametype = (!(diffs & D_TYPE));
1210 int mdsame = (!(diffs & D_MD));
1211 int hashsame = (!(diffs & D_HASH));
1212 int ohashsame = (!(diffs & D_OHASH));
1213 int isdir = (diffs & D_ISDIR);
1214 int eisdir = (diffs & D_EISDIR);
1215 int accept = conf->accept;
1216 int overwrite = conf->overwrite;
1217 int installing = (nitem.op == OP_NEW);
1218 update = (nitem.op == OP_UPDATE);
1220 /* if a config file doesn't exist on disk, go ahead and do
1221 * whatever you were going to do, this logic here just
1222 * needs to determine if we should skip what we were going to do
1224 * if the old item was a configuration item and the new one isn't, it
1225 * will have been saved earlier, so we can just go ahead. so we only
1226 * test for an existing file, where the item is a configuration file
1228 if (nitem.configuration && exist) {
1229 if (!sametype && !conf->overwrite) {
1230 return seterror(conf, "configuration file exists with different type: %s", nitem.dest);
1237 if (isdir && !mdsame) return 0;
1238 if (!isdir && !ohashsame) return 0;
1243 /* warn, it should exist */
1244 fprintf(stderr, "%s missing, installing", nitem.dest);
1245 return install(conf, &nitem, 3);
1248 /* file exists in filesystem */
1250 if (mdsame && hashsame) {
1251 /* warn, bug in logic. This shouldn't occur,
1252 * because if there is nothing to do, it
1253 * shouldn't be listed as an update
1255 /* could be an update. We're checking against
1256 * what's actually on disk, not what was
1257 * expected to have been on disk. So, if
1258 * the admin has modified the file, or if
1259 * it had been installed ignoring the user
1260 * and group, it might be correct on disk
1261 * but not as in the local database
1263 /* TODO detect whether this a logic bug or
1264 * an on-disk difference
1267 fprintf(stderr, "%s should not be an update\n", nitem.dest);
1268 fprintf(stderr, "old hash: %s\n", nitem.ohash);
1269 fprintf(stderr, "new hash: %s\n", nitem.hash);
1270 fprintf(stderr, "old mds: %s\n", nitem.omds);
1271 fprintf(stderr, "new mds: %s\n", nitem.mds);
1276 if (!mdsame && hashsame) {
1278 return set_md(conf, &nitem);
1280 if (mdsame && !hashsame) {
1282 return install(conf, &nitem, 3);
1284 if (!mdsame && !hashsame) {
1286 return install(conf, &nitem, 3);
1290 /* file exists, and is not the same type */
1292 if (isdir && !eisdir) {
1293 /* remove existing */
1295 return install(conf, &nitem, 7);
1297 if (!isdir && eisdir) {
1298 /* remove dir, or error */
1300 return install(conf, &nitem, 11);
1302 if (!isdir && !isdir) {
1303 /* necessarily !sametype, sametype handled above */
1304 /* remove existing */
1306 return install(conf, &nitem, 7);
1308 /* error, should not be possible, assert(0)? */
1309 fprintf(stderr,"impossible state: %s:%d\n", __func__, __LINE__);
1314 return install(conf, &nitem, 3);
1317 /* file exists in filesystem */
1319 if (mdsame && hashsame && (accept || overwrite)) {
1321 if (conf->dryrun || conf->verbose) {
1322 fprintf(stderr, "accept %s: %s\n",
1323 eisdir ? "directory" : "file", nitem.dest);
1328 if (mdsame && isdir && conf->acceptdir) {
1332 if (!mdsame && isdir && conf->ignoredirmd) {
1333 /* TODO warn ignoring dir md */
1337 if (mdsame && hashsame && !(accept || overwrite)) {
1339 return seterror(conf, "file exists: %s", nitem.dest);
1342 if (mdsame && !hashsame && overwrite) {
1344 return install(conf, &nitem, eisdir ? 11 : 7);
1347 if (nitem.configuration && accept) {
1348 /* accept a changed config file */
1349 if (conf->dryrun || conf->verbose) {
1350 fprintf(stderr, "accept %smodified config %s: %s\n", (!mdsame || !hashsame) ? "" : "un",
1351 eisdir ? "directory" : "file", nitem.dest);
1356 if (mdsame && !hashsame && !overwrite) {
1357 /* accept doesn't matter, since it's
1358 * not an acceptable file */
1360 if (nitem.ftype == 'l') {
1363 lsize = readlink(nitem.dest, link, sizeof link);
1364 if (lsize == -1 || (size_t)lsize >= sizeof link) {
1365 return seterror(conf, "%s (linkdiff): expecting %s -> %s, unable to read link", accept ? "existing file not acceptable" : "file exists", nitem.dest, nitem.target, link);
1368 /* links must be different */
1369 return seterror(conf, "%s (linkdiff): expecting %s -> %s, have -> %s", accept ? "existing file not acceptable" : "file exists", nitem.dest, nitem.target, link);
1372 return seterror(conf, "%s (hashdiff): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1375 if (!mdsame && hashsame && overwrite) {
1377 return set_md(conf, &nitem);
1379 if (!mdsame && hashsame && !overwrite) {
1380 /* accept doesn't matter, since it's
1381 * not an acceptable file */
1383 return seterror(conf, "%s (mddiff): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1385 if (!mdsame && !hashsame && overwrite) {
1387 return install(conf, &nitem, eisdir ? 11 : 7);
1389 if (!mdsame && !hashsame && !overwrite) {
1390 /* accept doesn't matter, since it's
1391 * not an acceptable file */
1393 return seterror(conf, "%s (md+hash): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1395 /* TODO error, should be impossible */
1396 return seterror(conf, "impossible state reached");
1399 /* file exists, and is not the same type */
1402 return seterror(conf, "%s (difftype): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1405 /* not the same type, but ok to overwrite */
1407 /* remove existing */
1408 return install(conf, &nitem, 7);
1411 /* existing path is a directory */
1414 /* impossible, if isdir and eisdir, would
1418 return set_md(conf, &nitem);
1420 /* remove empty dir or error */
1422 return install(conf, &nitem, 11);
1424 /* if we get here, we missed a case */
1426 return seterror(conf, "impossible state 2 reached");
1429 /* TODO extra verbose print perms, mtime, etc, probably ls -l
1432 if (conf->verbose) {
1433 printf("%s\n", nitem.path);
1439 static void check_conflicts(struct config *conf, char *conflict_type,
1440 int (callback)(void *, int, char **, char **)) {
1446 s = sqlite3_str_new(conf->log->db);
1447 sqlite3_str_appendall(s, "select *, ");
1448 if (conf->rootdir) {
1449 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1451 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1453 sqlite3_str_appendall(s, " as dest from syncconflicts");
1455 if (conflict_type) {
1456 sqlite3_str_appendf(s," where conflict = %Q", conflict_type);
1458 if (conf->reverse) {
1459 sqlite3_str_appendall(s," order by length(path) desc, path desc,pkgid collate vercmp desc, conflict desc");
1461 sqlite3_str_appendall(s," order by length(path), path, pkgid collate vercmp, conflict");
1465 sql = sqlite3_str_value(s);
1467 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
1469 sqlite3_str_finish(s);
1472 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1474 fprintf(stderr, "database error: %s\n", errmsg);
1477 if (conf->log->error == 1) {
1478 fprintf(stderr, "unable to allocate memory\n");
1480 fprintf(stderr, "zpm_exec failure: %s\n",
1481 conf->log->errmsg ? conf->log->errmsg : "unknown");
1484 if (conf->log->errmsg) {
1485 fprintf(stderr, "error: %s\n", conf->log->errmsg);
1487 if (conf->errors && conf->exitonerror) {
1488 zpm_close(conf->log);
1489 zpm_close(conf->src);
1492 /* TODO final report function in conf var */
1495 static void runstage(struct config *conf, char *stage,
1496 int (callback)(void *, int, char **, char **)) {
1502 s = sqlite3_str_new(conf->log->db);
1503 sqlite3_str_appendall(s, "select *, ");
1504 if (conf->rootdir) {
1505 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1507 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1509 sqlite3_str_appendall(s, " as dest from syncinfo");
1512 sqlite3_str_appendf(s," where op = %Q", stage);
1514 if (conf->reverse) {
1515 sqlite3_str_appendall(s," order by length(path) desc, path desc");
1518 sql = sqlite3_str_value(s);
1520 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
1522 sqlite3_str_finish(s);
1525 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1527 fprintf(stderr, "database error: %s\n", errmsg);
1530 if (conf->log->error == 1) {
1531 fprintf(stderr, "unable to allocate memory\n");
1533 fprintf(stderr, "zpm_exec failure: %s\n",
1534 conf->log->errmsg ? conf->log->errmsg : "unknown");
1538 if (conf->log->errmsg) {
1539 fprintf(stderr, "error: %s\n", conf->log->errmsg);
1542 if (conf->errors && conf->exitonerror) {
1543 zpm_close(conf->log);
1544 zpm_close(conf->src);
1547 /* TODO final report function in conf var */
1550 static void count_ops(struct config *conf) {
1551 conf->ops_remove = zpm_db_int(conf->log, "select count(*) from syncinfo where op = 'remove'");
1552 conf->ops_update = zpm_db_int(conf->log, "select count(*) from syncinfo where op = 'update'");
1553 conf->ops_install = zpm_db_int(conf->log, "select count(*) from syncinfo where op = 'new'");
1554 conf->ops_total = conf->ops_remove + conf->ops_update + conf->ops_install;
1557 int main(int ac, char **av) {
1561 char *pkgdbfile = 0, *localdbfile = 0;
1564 struct config conf = { 0 };
1581 if (geteuid() != 0) {
1586 localdbfile = ZPM_LOCAL_DB;
1587 if ((s = getenv("ZPMDB"))) {
1588 /* TODO does this need to be copied ? */
1592 if ((s = getenv("ZPM_ROOT_DIR"))) {
1593 /* TODO does this need to be copied ? */
1598 * -d localdb or ZPMDB * or /var/lib/zpm/zpm.db, or die
1599 * -f 'package database', otherwise regular default of env
1600 * ZPM_PACKAGE_FILE, or use pkgdb if otherwise not found
1601 * -R root of pkg, will just chdir there
1603 * args are pkgid triple, but will do a pkg find on the pkgdb
1606 while ((opt = getopt(ac, av, "f:d:c:nCR:vOAMDp")) != -1) {
1608 case 'd': localdbfile = optarg; break;
1609 case 'f': pkgdbfile = optarg; break;
1610 case 'n': conf.dryrun = 1; break;
1611 case 'v': conf.verbose++; break;
1612 case 'C': conf.errabort = 0; break;
1613 case 'R': conf.rootdir = optarg; break;
1614 case 'N': conf.setuser = 0; conf.setgroup = 0; break;
1615 case 'O': conf.overwrite = 1; break;
1616 case 'A': conf.accept = 1; break;
1617 case 'M': conf.ignoredirmd = 1;
1618 case 'D': conf.acceptdir = 0;
1619 case 'p': conf.progress++;
1627 /* verify root dir exists */
1628 if (conf.rootdir && !exists(conf.rootdir, NULL)) {
1629 fprintf(stderr, "rootdir %s does not exist\n", conf.rootdir);
1632 if (!zpm_open(&localdb, localdbfile)) {
1633 fprintf(stderr, "can't open zpm db %s\n", localdbfile);
1636 conf.log = &localdb;
1639 /* TODO open read-only */
1640 if (!zpm_open(&pkgdb, pkgdbfile)) {
1641 fprintf(stderr, "can't open src db %s\n", localdbfile);
1648 /* TODO find pkgid from arg */
1650 /* TODO set conf var to finalize error reporting */
1652 fprintf(stderr, "syncing filesystem %s (ldb %s) from %s\n",
1653 conf.rootdir ? conf.rootdir : "/",
1654 localdbfile, pkgdbfile);
1658 conf.exitonerror = 0;
1659 check_conflicts(&conf, NULL, report_conflicts);
1661 if (conf.conflicts) {
1662 fprintf(stderr, "%d conflicts reported, aborting sync\n",
1666 /* no point in running it if we're just going to
1667 * overwrite everything
1669 if (!conf.overwrite && !conf.accept && !conf.dryrun) {
1670 runstage(&conf, "new", check_existing);
1674 fprintf(stderr, "beginning %ssync\n", conf.dryrun ?
1679 handle_config_files(&conf);
1682 /* have to do the removes first otherwise
1683 * old files may conflict with update file
1687 conf.exitonerror = conf.dryrun ? 0 : 1;
1688 conf.errabort = conf.dryrun ? 0 : 1;
1690 fprintf(stderr, "file ops: %d\n", conf.ops_total);
1691 if (conf.ops_remove > 0) {
1693 fprintf(stderr, "removing %d file%s\n", conf.ops_remove, conf.ops_remove > 1 ? "s" : "");
1696 conf.ops_completed = 0;
1697 conf.ops_total = conf.ops_remove;
1698 runstage(&conf, "remove", remove_files);
1699 if (conf.verbose && conf.progress < 2) {
1700 fprintf(stderr, " done\n");
1705 if (conf.ops_update > 0) {
1707 fprintf(stderr, "updating %d file%s\n", conf.ops_update, conf.ops_update > 1 ? "s" : "");
1710 conf.ops_completed = 0;
1711 conf.ops_total = conf.ops_update;
1712 runstage(&conf, "update", install_files);
1713 if (conf.verbose && conf.progress < 2) {
1714 fprintf(stderr, " done\n");
1719 if (conf.ops_install > 0) {
1721 fprintf(stderr, "installing %d file%s\n", conf.ops_install, conf.ops_install > 1 ? "s" : "");
1724 conf.ops_completed = 0;
1725 conf.ops_total = conf.ops_install;
1726 runstage(&conf, "new", install_files);
1727 if (conf.verbose && conf.progress < 2) {
1728 fprintf(stderr, " done\n");
1735 zpm_close(&localdb);
1736 zpm_close(conf.src);
1737 return conf.errors ? 1 : 0;