1 #define _POSIX_C_SOURCE 200809L
18 /* needed for S_IFMT and AT_FDCWD */
25 #include "lib/buffer.h"
28 struct zpm *log; /* logging db will be attached as "log" */
32 int errabort, errors, verbose, dryrun, conflicts;
33 int setuser, setgroup;
34 int reverse, exitonerror;
35 int overwrite, accept, acceptdir, ignoredirmd;
36 unsigned long ops_total, ops_completed;
37 unsigned long ops_remove, ops_remove_completed;
38 unsigned long ops_update, ops_update_completed;
39 unsigned long ops_install, ops_install_completed;
40 int progress; /* type of progress meter */
41 struct zpm_buffer note;
54 char *pkglist; /* space separated */
58 int configuration, oldwasconf;
59 struct timespec times[2];
63 printf("usage: zpm syncfs ...\n");
66 static void warn(char *fmt, ...) {
70 vfprintf(stderr, fmt, args);
72 fprintf(stderr, "\n");
75 static void add_to_note(struct config *cf, char *fmt, ...) {
79 zpm_buffer_appendvf(&cf->note, fmt, ap);
83 static void pdots(int len, int ch, int was, int now, int total) {
84 was = len * was / total;
88 now = len * now / total;
95 static int seterror(struct config *conf, char *msgfmt, ...) {
101 va_start(ap, msgfmt);
102 vsnprintf(msg, sizeof msg, msgfmt, ap);
106 if (conf->log->errmsg) {
107 free(conf->log->errmsg);
110 conf->log->errmsg = strdup(msg);
113 fprintf(stderr, "%s\n", msg);
116 return conf->errabort;
119 static int setsyserr(struct config *conf, char *msgfmt, ...) {
126 va_start(ap, msgfmt);
127 printed = vsnprintf(msg, sizeof msg, msgfmt, ap);
131 /* nothing we can really do */
132 return conf->errabort;
135 if ((size_t)printed < sizeof msg) {
136 snprintf(msg+printed, sizeof msg - printed, ": %s",
141 if (conf->log->errmsg) {
142 free(conf->log->errmsg);
145 conf->log->errmsg = strdup(msg);
148 fprintf(stderr, "%s\n", msg);
151 return conf->errabort;
154 static int exists(char *path, mode_t *mode) {
157 if (lstat(path, &st) == -1) {
160 if (mode) *mode = st.st_mode;
164 /* TODO maintain a list of already created directories */
165 static int create_leading_dirs(char *path) {
168 char pcopy[ZPM_PATH_MAX];
173 delim = strrchr(pcopy, '/');
174 if (!delim) return 1; /* not an error, but no leading dirs */
176 /* cut off last component */
185 delim = strchr(s, '/');
191 /* try to create the directory, if it exists
192 * and is a directory or a symlink, that's ok
193 * should be (eventually) a symlink to a directory
194 * so we want stat here, not lstat
196 if (mkdir(pcopy, 0755) == -1) {
199 if (stat(pcopy, &st) == -1) {
203 switch (st.st_mode & S_IFMT) {
223 static char *column(char *col, int ncols, char **vals, char **cols) {
227 for (i=0; i < ncols; i++) {
228 // fprintf(stderr, "checking '%s' = '%s'\n", cols[i], vals[i]);
230 if (!strcmp(col, cols[i])) {
238 #define COL(x) column(x, ncols, vals, cols)
239 #define SYSERR(x) do { conf->log->error = 2; return conf->errabort; } while (0)
242 /* TODO handle other ops properly */
243 static char *ops[] = { "new", "remove", "update", 0 };
251 static int getop(char *opstr) {
254 if (!opstr) return 0;
255 for (i=0;ops[i];i++) {
256 if (!strcmp(opstr, ops[i])) {
263 static int report_conflicts(void *f, int ncols, char **vals, char **cols) {
264 struct config *conf = f;
265 char *path, *hash, *pkg, *conflict_type, *mds;
269 conflict_type = COL("conflict");
270 if (!strcmp(conflict_type, "hash")) {
272 fprintf(stderr, "hash conflict: package %s path %s hash %.8s\n",
275 if (!strcmp(conflict_type, "md")) {
277 fprintf(stderr, "md conflict: package %s path %s md %s\n",
280 fprintf(stderr, "%s conflict: package %s path %s\n",
281 conflict_type, pkg, path);
288 static int read_item(struct config *conf, int ncols, char **vals, char **cols,
294 struct nitem zero = { 0 };
300 seterror(conf, "can't determine op");
306 seterror(conf, "can't determine op");
310 n->path = COL("path");
312 seterror(conf, "no file path");
315 if (strlen(n->path) == 0) {
316 seterror(conf, "zero length path not allowed");
320 /* TODO config to dishonor setuid/setgid */
321 n->dest = COL("dest");
323 seterror(conf, "no file dest");
327 if (strlen(n->dest) == 0) {
328 seterror(conf, "zero length dest not allowed");
335 seterror(conf, "can't determine mode");
339 n->mode = strtoul(val, NULL, 8);
341 val = COL("configuration");
343 seterror(conf, "can't determine config status");
346 lval = strtol(val, NULL, 10);
348 n->configuration = ((lval & 1) != 0);
349 n->oldwasconf = ((lval & 2) != 0);
351 val = COL("filetype");
352 if (!val || strlen(val) == 0) {
353 seterror(conf, "can't determine file type");
358 /* these can be null */
359 n->ohash = COL("ohash");
361 n->omds = COL("omds");
362 n->pkglist = COL("pkgid");
364 if (n->ftype == 'r') {
365 n->hash = COL("hash");
367 seterror(conf, "can't get hash");
370 } else if (n->ftype == 'l') {
371 n->target = COL("target");
373 seterror(conf, "can't get target");
376 if (strlen(n->target) == 0) {
377 seterror(conf, "zero length target not allowed");
384 val = COL("username");
386 seterror(conf, "no username");
391 seterror(conf, "no passwd entry");
399 if (conf->setgroup) {
400 val = COL("groupname");
402 seterror(conf, "no groupname");
407 seterror(conf, "no group entry for %s", val);
416 double mtime = strtod(COL("mtime"),NULL);
418 mtime = (double)time(NULL);
421 n->mtime = (time_t)mtime;
423 n->times[0].tv_sec = 0;
424 n->times[0].tv_nsec = UTIME_OMIT;
425 n->times[1].tv_sec = (time_t)llrint(floor(mtime));
426 n->times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000));
431 /* file does not exist */
432 #define D_NOEXIST 0x1
433 /* files are different types */
435 /* metadata is different */
437 /* content or link target is different */
439 /* file to be installed is a directory */
441 /* path on disk is a directory */
442 #define D_EISDIR 0x20
443 /* usernames different */
445 /* group names different */
447 /* file mode is different */
449 /* mtimes are different */
450 #define D_MTIME 0x200
451 /* the hash of the file we are supposedly replacing is different than
452 * the the hash of the file on disk
454 #define D_OHASH 0x400
455 /* file exists, and is a directory, and is empty */
456 #define D_ISEMPTY 0x800
457 /* an error occurred trying to compare the file (other than it doesn't exist */
458 #define D_ERROR 0x1000
459 /* there was a stat error */
460 #define D_STATERROR 0x2000
461 /* there was an error calling readlink */
462 #define D_RLERROR 0x4000
464 static int dir_is_empty(char *path) {
485 /* 1 = file doesn't exist, 2 = file is a directory, target isn't */
486 /* 4 == ftype different */
487 /* 8 = hash different when both are regular files */
488 static unsigned int file_compare(struct nitem *n, struct stat *st) {
489 int etype = 0, stat_type;
490 char ehash[ZPM_HASH_STRLEN+1];
491 unsigned int diff = 0;
496 case 'd': etype = S_IFDIR; diff |= D_ISDIR ; break;
497 case 'r': etype = S_IFREG; break;
498 case 'l': etype = S_IFLNK; break;
499 default: etype = 0; break;
503 /* new file, so check type, hash, etc */
504 if (lstat(n->dest, st) == 0) {
505 stat_type = st->st_mode & S_IFMT;
506 if (stat_type != etype) {
509 if (stat_type == S_IFDIR) {
511 if (dir_is_empty(n->dest)) {
516 if (n->hash && etype == S_IFREG && stat_type == S_IFREG) {
517 zpm_hash(n->dest, ehash, 0);
518 if (strcmp(n->hash, ehash) != 0) {
521 if (n->ohash && strcmp(n->ohash, ehash) != 0) {
525 if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) {
526 lsize = readlink(n->dest, link, sizeof link);
528 if (lsize == -1 || lsize == sizeof link) {
533 if (strcmp(n->target, link) != 0) {
538 if (n->uid != st->st_uid) {
542 if (n->gid != st->st_gid) {
546 if (n->mode != (st->st_mode & 07777)) {
552 case ENOENT: diff |= D_NOEXIST; break;
553 default: diff |= (D_STATERROR|D_ERROR); break;
561 /* 0 = not acceptable
562 * 1 = accept and create/update/remove
564 * 3 = remove and overwrite
565 * 4 = update metadata
567 static int acceptable(struct config *conf, unsigned int diffs, int op) {
568 int exist = (!(diffs & D_NOEXIST));
569 int sametype = (!(diffs & D_TYPE));
570 int mdsame = (!(diffs & D_MD));
571 int hashsame = (!(diffs & D_HASH));
572 int isdir = (diffs & D_ISDIR);
575 return op == OP_REMOVE ? 2 : 1;
578 if (op == OP_UPDATE) {
579 return sametype ? 4 : 3;
582 if (op == OP_REMOVE) {
586 /* the hard cases, should be installing new, but already exists */
589 return conf->overwrite ? 3 : 0;
592 if (mdsame && (conf->accept || conf->overwrite)) {
597 if (mdsame || conf->ignoredirmd) {
598 return conf->acceptdir ? 2 : 0;
600 if (conf->overwrite) {
605 if (hashsame && (conf->accept || conf->overwrite)) {
609 return conf->overwrite ? 3 : 0;
612 static int check_existing(void *f, int ncols, char **vals, char **cols) {
613 struct config *conf = f;
617 if (!read_item(conf, ncols, vals, cols, &nitem)) {
618 fprintf(stderr, "can't read item\n");
619 return conf->errabort;
622 if (conf->verbose > 1) {
623 fprintf(stderr, "check for existing %s\n", nitem.path);
626 if (lstat(nitem.path, &st) == -1) {
628 /* not an error, file shouldn't exist*/
631 fprintf(stderr, "unable to check %s: %s\n",
632 nitem.path, strerror(errno));
639 unsigned int diffs = file_compare(&nitem, &st);
640 int sametype = (!(diffs & D_TYPE));
642 if (diffs >= D_ERROR) {
643 return seterror(conf, "can't check %s", nitem.dest);
646 if (sametype && nitem.configuration) {
650 int action = acceptable(conf, diffs, nitem.op);
653 fprintf(stderr, "%s exists and is not acceptable\n", nitem.path);
655 fprintf(stderr, "%s exists\n", nitem.path);
663 static void update_progress(struct config *conf, char *op, char *path) {
664 if (!conf->verbose) {
668 if (conf->progress == 0) {
669 pdots(50, '.', conf->ops_completed-1, conf->ops_completed, conf->ops_total);
670 } else if (conf->progress == 1) {
671 size_t len = strlen(path);
676 printf("\r%lu/%lu %.10s %.50s\n", conf->ops_completed,
677 conf->ops_total, op, path+offset);
678 } else if (conf->progress == 2) {
679 printf("%lu/%lu %s %s\n", conf->ops_completed,
680 conf->ops_total, op, path);
685 static int remove_files(void *f, int ncols, char **vals, char **cols) {
686 struct config *conf = f;
691 conf->ops_completed++;
694 char *ftype = COL("filetype");
696 if (!dest) return seterror(conf,"no file dest");
697 if (!ftype) return seterror(conf,"no file type");
699 update_progress(conf, *ftype == 'd' ? "rmdir" : "unlink", dest);
707 if (lstat(dest, &st) == -1) {
710 if (conf->verbose > 1) {
711 fprintf(stderr, "expected file not found: '%s'\n", dest);
715 return seterror(conf, "can't stat %s: %s", dest, strerror(errno));
720 if (S_ISDIR(st.st_mode)) {
721 flags = AT_REMOVEDIR;
723 /* TODO check that expected filetype matches actual filetype */
724 /* alternatively, just use the expected type, skip the stat,
725 * and let it fail if the type is wrong
730 if (unlinkat(AT_FDCWD, dest, flags) == -1) {
734 case ENOTEMPTY: /* fall through */
736 fprintf(stderr, "expected empty directory: %s\n", dest);
739 return seterror(conf, "can't unlink %s: %s", dest, strerror(errno));
746 #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__)
748 static int remove_dir(struct config *conf, char *path) {
753 setsyserr(conf, "can't rmdir %s", path);
759 static int remove_existing(struct config *conf, char *path) {
764 setsyserr(conf, "can't unlink %s", path);
770 static int set_md(struct config *conf, struct nitem *item) {
775 if (item->ftype != 'l') {
776 printf("chmod %o %s\n", item->mode, item->dest);
778 if (conf->setuser && conf->setgroup) {
779 printf("lchown %d:%d %s\n", item->uid, item->gid,
782 printf("mtime %.0f %s\n", (double)item->mtime, item->dest);
787 if (conf->setuser && conf->setgroup) {
788 rv = lchown(item->dest, item->uid, item->gid);
790 setsyserr(conf, "can't lchown %s", item->dest);
791 return conf->errabort;
795 /* have to chmod after the chown, setuid bits may (and will)
796 * be cleared after a chown
798 /* can't chmod a symlink */
799 if (item->ftype != 'l') {
800 rv = chmod(item->dest, item->mode);
803 setsyserr(conf, "can't chmod %o %s", item->mode, item->dest);
804 return conf->errabort;
808 rv = utimensat(AT_FDCWD, item->dest, item->times, AT_SYMLINK_NOFOLLOW);
810 setsyserr(conf, "can't set mtime %.0f %s", (double)item->mtime,
812 return conf->errabort;
817 /* install a file or create a directory or symlink. path should not exist
820 /* flags: 1 = set md, 2 = create leading dirs, 4 = unlink existing file,
821 * 8 = rmdir existing dir, 16 = return true/false
825 #define INS_UNLINK 0x4
826 #define INS_RMDIR 0x8
828 #define INS_ZPMNEW 0x20
829 static int install(struct config *conf, struct nitem *item, unsigned int flags) {
833 int mkleading = (flags & 2);
834 int setmd = (flags & 1);
835 int unlink_file = (flags & 4);
836 int rm_dir = (flags & 8);
837 int failure = conf->errabort;
840 if (flags & INS_RTF) {
847 printf("unlink %s\n", item->dest);
849 printf("rmdir %s\n", item->dest);
852 printf("install %c%o %d:%d %s", item->ftype,
853 item->mode, item->uid, item->gid,
855 if (item->ftype == 'l') {
856 printf(" -> %s", item->target);
863 source = conf->src ? conf->src : conf->log;
866 rv = create_leading_dirs(item->dest);
868 setsyserr(conf, "can't create leading dirs for %s", item->dest);
874 rv = remove_existing(conf, item->dest);
876 rv = remove_dir(conf, item->dest);
884 switch (item->ftype) {
885 case 'r': rv = zpm_extract(source, item->hash, item->dest, item->mode);
886 if (rv == 0) rv = -1;
888 case 'd': rv = mkdir(item->dest, item->mode);
890 case 'l': rv = symlink(item->target, item->dest);
897 switch (item->ftype) {
899 seterror(conf, "can't extract %s", item->dest);
902 setsyserr(conf, "install mkdir(\"%s\") failed", item->dest);
905 setsyserr(conf, "install symlink(\"%s\") failed", item->dest);
908 setsyserr(conf, "installing %s failed", item->dest);
913 return set_md(conf, item) == 0 ? success : failure;
919 static int save_config_file(struct config *conf, struct nitem *n, char *msgfmt) {
920 char hash[ZPM_HASH_STRLEN+1];
923 msgfmt = "saved config file %.8s";
926 if (zpm_import(conf->log, n->dest, 0, hash)) {
927 zpm_note_add(conf->log, n->pkglist, n->path, hash, msgfmt, hash);
929 warn("unable to import existing config file %s", n->dest);
938 * figure out what the difference is for a config file, only called
939 * for an update of a configuration file
940 * return -1 on an error
941 * return 1 if the new file should not be installed
942 * return 0 if the new file should be installed
944 static int adjust_for_config(struct nitem *n, unsigned int diffs) {
946 if (!n->oldwasconf) {
950 int sametype = (!(diffs & D_TYPE));
951 int isdir = (diffs & D_ISDIR);
952 int eisdir = (diffs & D_EISDIR);
954 /* what if old was a directory? */
955 if (!n->configuration) {
956 /* replacing conf with non-conf */
957 /* absorb file, mark todo */
961 /* both old and new are config files */
962 if (isdir && sametype) {
963 /* both config directories, can only be changing
964 * metadata, so no adjustment needed
974 /* replacing old conf directory with a conf file.
975 * nothing needs to be done, if the directory
976 * is empty, it's ok to remove. if it's not empty,
977 * the install will fail
982 /* replacing old file with new file */
983 /* new is same as on disk */
984 if (!(diffs & D_HASH)) {
988 /* new is different than on disk, but on disk is same as old */
989 if (!(diffs & D_OHASH)) {
998 static int config_handler(void *f, int ncols, char **vals, char **cols) {
999 struct config *conf = f;
1001 struct stat existing;
1007 if (!read_item(conf, ncols, vals, cols, &nitem)) {
1008 fprintf(stderr, "can't read item\n");
1010 return conf->errabort;
1013 unsigned int diffs = file_compare(&nitem, &existing);
1014 if (diffs >= D_ERROR) {
1015 return seterror(conf, "can't check %s", nitem.dest);
1018 int exist = (!(diffs & D_NOEXIST));
1019 int sametype = (!(diffs & D_TYPE));
1020 //int mdsame = (!(diffs & D_MD));
1021 int hashsame = (!(diffs & D_HASH));
1022 int oldhashsame = (!(diffs & D_OHASH));
1023 int isdir = (diffs & D_ISDIR);
1024 int eisdir = (diffs & D_EISDIR);
1025 update = (nitem.op == OP_UPDATE);
1027 notehash = nitem.hash;
1029 /* if the file doesn't exist in the system, nothing to do */
1030 /* could possibly note if we expected it, but the regular handling
1037 if (nitem.op == OP_UPDATE && !nitem.oldwasconf) {
1038 /* possibly save anyway */
1042 /* so, old was conf, and something exists in the filesystem */
1045 warn("won't %s %s%s, %s exists",
1046 nitem.op == OP_NEW ? "install" : nitem.opstr,
1049 eisdir ? "directory" : "file"
1052 return conf->errabort;
1055 /* all below are same type of file */
1056 /* what about sametype, but old was different type */
1062 /* save or note cases */
1064 if (nitem.op == OP_REMOVE) {
1065 save ="saved removed config file %.8s";
1068 if (nitem.op == OP_UPDATE) {
1069 if (!nitem.configuration) {
1070 /* replacing config with non-config */
1071 save = "replacing configuration file %.8s with non-configuration file";
1072 } else if (oldhashsame) {
1073 /* config file hasn't changed from old default,
1074 * so go ahead and install the new one
1076 save = "replaced old default config (%.8s) with new one.";
1077 notehash = nitem.ohash;
1079 note = "kept existing config file. new default version is %.8s";
1084 if (nitem.op == OP_NEW && !hashsame) {
1085 note = "config file already existed. would have installed %.8s";
1090 * save files, add notes
1092 if (!conf->dryrun) {
1094 warn("saving config file: %s (root %s)", nitem.path, conf->rootdir ? conf->rootdir : "/");
1095 save_config_file(conf, &nitem, save);
1098 fprintf(stderr, "%s (%s) '%s' ", nitem.pkglist,
1099 nitem.hash, nitem.path);
1100 fprintf(stderr, note, nitem.hash);
1101 fprintf(stderr, "\n");
1103 zpm_note_add(conf->log, nitem.pkglist, nitem.path,
1104 nitem.hash, note, nitem.hash);
1108 fprintf(stderr, "dry run: %s %s: ", nitem.pkglist,
1110 warn(save, notehash);
1113 fprintf(stderr, "dry run: %s %s: ", nitem.pkglist,
1115 warn(note, notehash);
1123 static void handle_config_files(struct config *conf) {
1129 s = sqlite3_str_new(conf->log->db);
1130 sqlite3_str_appendall(s, "select *, ");
1131 if (conf->rootdir) {
1132 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1134 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1136 sqlite3_str_appendall(s, " as dest from syncinfo");
1138 sqlite3_str_appendall(s," where configuration > 0 and op in ('new','update','remove')");
1140 if (conf->reverse) {
1141 sqlite3_str_appendall(s," order by length(path) desc, path desc");
1144 sql = sqlite3_str_value(s);
1146 rv = zpm_exec(conf->log, sql, config_handler, conf, &errmsg);
1148 sqlite3_str_finish(s);
1151 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1153 fprintf(stderr, "database error: %s\n", errmsg);
1156 if (conf->log->error == 1) {
1157 fprintf(stderr, "unable to allocate memory\n");
1159 fprintf(stderr, "zpm_exec failure: %s\n",
1160 conf->log->errmsg ? conf->log->errmsg : "unknown");
1164 if (conf->errors && conf->exitonerror) {
1165 zpm_close(conf->log);
1166 zpm_close(conf->src);
1171 static int install_files(void *f, int ncols, char **vals, char **cols) {
1172 struct config *conf = f;
1174 struct stat existing;
1177 /* put the result row in a hash table? May not actually
1180 if (!read_item(conf, ncols, vals, cols, &nitem)) {
1181 fprintf(stderr, "can't read item\n");
1182 return conf->errabort;
1187 used = sqlite3_memory_used()/1024/1024;
1188 high = sqlite3_memory_highwater(0)/1024/1024;
1189 fprintf(stderr, "memory = %ld MB / %ld MB\n", used, high);
1192 sprintf(action, "%.8s %c", nitem.opstr, nitem.ftype);
1193 conf->ops_completed++;
1194 update_progress(conf, action, nitem.dest);
1196 unsigned int diffs = file_compare(&nitem, &existing);
1197 if (diffs >= D_ERROR) {
1198 return seterror(conf, "can't check %s", nitem.dest);
1202 * exist & same type & md same & hash same: do nothing, but warn bug
1203 * exist & same type & md diff & hash same: fix md
1204 * exist & same type & md same & hash diff: replace
1205 * exist & same type & md diff & hash diff: replace & fix
1206 * no exist: install and warn
1207 * dir & not dir : remove, mkdir
1208 * not dir & not dir & diff type: remove, install
1209 * not dir & dir : remove dir if empty, error if not empty, install
1212 * no exist: create leading dirs, install
1214 * exist & same type & md same & hash same & accept or over: do nothing
1215 * exist & same & md diff or hash diff & overwrite : update
1216 * exist & same & md diff or hash diff & accept : error, can't accept
1217 * exist & same & md diff or hash diff & not accept : error
1219 * exist & different type & not overwrite : error
1220 * not dir & not dir & overwrite : remove and install
1221 * not dir & dir & overwrite: remove empty or error, install
1222 * dir & dir & overwrite: fix md
1223 * dir & not dir & overwrite: remove and mkdir
1225 int exist = (!(diffs & D_NOEXIST));
1226 int sametype = (!(diffs & D_TYPE));
1227 int mdsame = (!(diffs & D_MD));
1228 int hashsame = (!(diffs & D_HASH));
1229 int ohashsame = (!(diffs & D_OHASH));
1230 int isdir = (diffs & D_ISDIR);
1231 int eisdir = (diffs & D_EISDIR);
1232 int accept = conf->accept;
1233 int overwrite = conf->overwrite;
1234 int installing = (nitem.op == OP_NEW);
1235 update = (nitem.op == OP_UPDATE);
1237 /* if a config file doesn't exist on disk, go ahead and do
1238 * whatever you were going to do, this logic here just
1239 * needs to determine if we should skip what we were going to do
1241 * if the old item was a configuration item and the new one isn't, it
1242 * will have been saved earlier, so we can just go ahead. so we only
1243 * test for an existing file, where the item is a configuration file
1245 if (nitem.configuration && exist) {
1246 if (!sametype && !conf->overwrite) {
1247 return seterror(conf, "configuration file exists with different type: %s", nitem.dest);
1254 if (isdir && !mdsame) return 0;
1255 if (!isdir && !ohashsame) return 0;
1260 /* warn, it should exist */
1261 add_to_note(conf, "%s missing, installing\n", nitem.dest);
1262 fprintf(stderr, "%s missing, installing\n", nitem.dest);
1263 return install(conf, &nitem, 3);
1266 /* file exists in filesystem */
1268 if (mdsame && hashsame) {
1269 /* warn, bug in logic. This shouldn't occur,
1270 * because if there is nothing to do, it
1271 * shouldn't be listed as an update
1273 /* could be an update. We're checking against
1274 * what's actually on disk, not what was
1275 * expected to have been on disk. So, if
1276 * the admin has modified the file, or if
1277 * it had been installed ignoring the user
1278 * and group, it might be correct on disk
1279 * but not as in the local database
1282 /* TODO detect whether this a logic bug or
1283 * an on-disk difference
1286 fprintf(stderr, "%s should not be an update\n", nitem.dest);
1287 fprintf(stderr, "old hash: %s\n", nitem.ohash);
1288 fprintf(stderr, "new hash: %s\n", nitem.hash);
1289 fprintf(stderr, "old mds: %s\n", nitem.omds);
1290 fprintf(stderr, "new mds: %s\n", nitem.mds);
1295 if (!mdsame && hashsame) {
1297 return set_md(conf, &nitem);
1301 /* doesn't matter on the md */
1302 int flags = INS_MD | INS_CLD;
1303 if (nitem.ftype == 'l') {
1304 flags |= INS_UNLINK;
1306 return install(conf, &nitem, flags);
1310 /* file exists, and is not the same type */
1312 if (isdir && !eisdir) {
1313 /* remove existing */
1315 return install(conf, &nitem, 7);
1317 if (!isdir && eisdir) {
1318 /* remove dir, or error */
1320 return install(conf, &nitem, 11);
1322 if (!isdir && !isdir) {
1323 /* necessarily !sametype, sametype handled above */
1324 /* remove existing */
1326 return install(conf, &nitem, 7);
1328 /* error, should not be possible, assert(0)? */
1329 fprintf(stderr,"impossible state: %s:%d\n", __func__, __LINE__);
1334 return install(conf, &nitem, 3);
1337 /* file exists in filesystem */
1339 if (mdsame && hashsame && (accept || overwrite)) {
1341 if (conf->dryrun || conf->verbose > 1) {
1342 fprintf(stderr, "accept %s: %s\n",
1343 eisdir ? "directory" : "file", nitem.dest);
1348 if (mdsame && isdir && conf->acceptdir) {
1352 if (!mdsame && isdir && conf->ignoredirmd) {
1353 if (conf->verbose > 1) {
1354 fprintf(stderr, "ignoring directory metadata difference: %s\n", nitem.dest);
1359 if (mdsame && hashsame && !(accept || overwrite)) {
1361 return seterror(conf, "file exists: %s", nitem.dest);
1364 if (mdsame && !hashsame && overwrite) {
1366 return install(conf, &nitem, eisdir ? 11 : 7);
1369 if (nitem.configuration && accept) {
1370 /* accept a changed config file */
1371 if (conf->dryrun || conf->verbose) {
1372 fprintf(stderr, "accept %smodified config %s: %s\n", (!mdsame || !hashsame) ? "" : "un",
1373 eisdir ? "directory" : "file", nitem.dest);
1378 if (mdsame && !hashsame && !overwrite) {
1379 /* accept doesn't matter, since it's
1380 * not an acceptable file */
1382 if (nitem.ftype == 'l') {
1385 lsize = readlink(nitem.dest, link, sizeof link);
1386 if (lsize == -1 || (size_t)lsize >= sizeof link) {
1387 return seterror(conf, "%s (linkdiff): expecting %s -> %s, unable to read link", accept ? "existing file not acceptable" : "file exists", nitem.dest, nitem.target, link);
1390 /* links must be different */
1391 return seterror(conf, "%s (linkdiff): expecting %s -> %s, have -> %s", accept ? "existing file not acceptable" : "file exists", nitem.dest, nitem.target, link);
1394 return seterror(conf, "%s (hashdiff): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1397 if (!mdsame && hashsame && overwrite) {
1399 return set_md(conf, &nitem);
1401 if (!mdsame && hashsame && !overwrite) {
1402 /* accept doesn't matter, since it's
1403 * not an acceptable file */
1405 return seterror(conf, "%s (mddiff): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1407 if (!mdsame && !hashsame && overwrite) {
1409 return install(conf, &nitem, eisdir ? 11 : 7);
1411 if (!mdsame && !hashsame && !overwrite) {
1412 /* accept doesn't matter, since it's
1413 * not an acceptable file */
1415 return seterror(conf, "%s (md+hash): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1417 /* error, should be impossible */
1418 return seterror(conf, "impossible state reached");
1421 /* file exists, and is not the same type */
1424 return seterror(conf, "%s (difftype): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1427 /* not the same type, but ok to overwrite */
1429 /* remove existing */
1430 return install(conf, &nitem, 7);
1433 /* existing path is a directory */
1436 /* impossible, if isdir and eisdir, would be same type
1438 return set_md(conf, &nitem);
1440 /* remove empty dir or error */
1442 return install(conf, &nitem, 11);
1444 /* if we get here, we missed a case */
1445 return seterror(conf, "impossible state 2 reached");
1448 /* TODO extra verbose print perms, mtime, etc, probably ls -l format
1450 if (conf->verbose) {
1451 printf("%s\n", nitem.path);
1457 static void check_conflicts(struct config *conf, char *conflict_type,
1458 int (callback)(void *, int, char **, char **)) {
1464 s = sqlite3_str_new(conf->log->db);
1465 sqlite3_str_appendall(s, "select *, ");
1466 if (conf->rootdir) {
1467 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1469 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1471 sqlite3_str_appendall(s, " as dest from syncconflicts");
1473 if (conflict_type) {
1474 sqlite3_str_appendf(s," where conflict = %Q", conflict_type);
1476 if (conf->reverse) {
1477 sqlite3_str_appendall(s," order by length(path) desc, path desc,pkgid collate vercmp desc, conflict desc");
1479 sqlite3_str_appendall(s," order by length(path), path, pkgid collate vercmp, conflict");
1483 sql = sqlite3_str_value(s);
1485 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
1487 sqlite3_str_finish(s);
1490 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1492 fprintf(stderr, "database error: %s\n", errmsg);
1495 if (conf->log->error == 1) {
1496 fprintf(stderr, "unable to allocate memory\n");
1498 fprintf(stderr, "zpm_exec failure: %s\n",
1499 conf->log->errmsg ? conf->log->errmsg : "unknown");
1502 if (conf->log->errmsg) {
1503 fprintf(stderr, "error: %s\n", conf->log->errmsg);
1505 if (conf->errors && conf->exitonerror) {
1506 zpm_close(conf->log);
1507 zpm_close(conf->src);
1510 /* TODO final report function in conf var */
1513 static void runstage(struct config *conf, char *stage,
1514 int (callback)(void *, int, char **, char **)) {
1520 s = sqlite3_str_new(conf->log->db);
1521 sqlite3_str_appendall(s, "select *, ");
1522 if (conf->rootdir) {
1523 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1525 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1527 sqlite3_str_appendall(s, " as dest from syncinfo");
1530 sqlite3_str_appendf(s," where op = %Q", stage);
1532 if (conf->reverse) {
1533 sqlite3_str_appendall(s," order by length(path) desc, path desc");
1535 sqlite3_str_appendall(s," order by length(path), path");
1538 sql = sqlite3_str_value(s);
1540 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
1542 sqlite3_str_finish(s);
1545 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1547 fprintf(stderr, "database error: %s\n", errmsg);
1550 if (conf->log->error == 1) {
1551 fprintf(stderr, "unable to allocate memory\n");
1553 fprintf(stderr, "zpm_exec failure: %s\n",
1554 conf->log->errmsg ? conf->log->errmsg : "unknown");
1558 if (conf->log->errmsg) {
1559 fprintf(stderr, "error: %s\n", conf->log->errmsg);
1562 if (conf->errors && conf->exitonerror) {
1563 zpm_close(conf->log);
1564 zpm_close(conf->src);
1567 /* TODO final report function in conf var */
1570 static void count_ops(struct config *conf) {
1571 conf->ops_remove = zpm_db_int(conf->log, "select count(*) from syncinfo where op = 'remove'");
1572 conf->ops_update = zpm_db_int(conf->log, "select count(*) from syncinfo where op = 'update'");
1573 conf->ops_install = zpm_db_int(conf->log, "select count(*) from syncinfo where op = 'new'");
1574 conf->ops_total = conf->ops_remove + conf->ops_update + conf->ops_install;
1577 int main(int ac, char **av) {
1581 char *pkgdbfile = 0, *localdbfile = 0;
1584 struct config conf = { 0 };
1600 zpm_buffer_init(&conf.note);
1602 if (geteuid() != 0) {
1607 localdbfile = ZPM_LOCAL_DB;
1608 if ((s = getenv("ZPMDB"))) {
1612 if ((s = getenv("ZPM_ROOT_DIR"))) {
1617 * -d localdb or ZPMDB * or /var/lib/zpm/zpm.db, or die
1618 * -f 'package database', otherwise regular default of env
1619 * ZPM_PACKAGE_FILE, or use pkgdb if otherwise not found
1620 * -R root of pkg, will just chdir there
1622 * args are pkgid triple, but will do a pkg find on the pkgdb
1625 while ((opt = getopt(ac, av, "f:d:c:nCR:vqOAMDp")) != -1) {
1627 case 'd': localdbfile = optarg; break;
1628 case 'f': pkgdbfile = optarg; break;
1629 case 'n': conf.dryrun = 1; break;
1630 case 'v': conf.verbose++; break;
1631 case 'q': conf.verbose--; break;
1632 case 'C': conf.errabort = 0; break;
1633 case 'R': conf.rootdir = optarg; break;
1634 case 'N': conf.setuser = 0; conf.setgroup = 0; break;
1635 case 'O': conf.overwrite = 1; break;
1636 case 'A': conf.accept = 1; break;
1637 case 'M': conf.ignoredirmd = 1;
1638 case 'D': conf.acceptdir = 0;
1639 case 'p': conf.progress++;
1647 /* verify root dir exists */
1648 if (conf.rootdir && !exists(conf.rootdir, NULL)) {
1649 fprintf(stderr, "rootdir %s does not exist\n", conf.rootdir);
1652 if (!zpm_open(&localdb, localdbfile)) {
1653 fprintf(stderr, "can't open zpm db %s\n", localdbfile);
1656 conf.log = &localdb;
1659 /* TODO open read-only */
1660 if (!zpm_open(&pkgdb, pkgdbfile)) {
1661 fprintf(stderr, "can't open src db %s\n", localdbfile);
1668 /* TODO set conf var to finalize error reporting */
1670 fprintf(stderr, "syncing filesystem %s (ldb %s)\n",
1671 conf.rootdir ? conf.rootdir : "/",
1676 conf.exitonerror = 0;
1677 check_conflicts(&conf, NULL, report_conflicts);
1679 if (conf.conflicts) {
1680 fprintf(stderr, "%d conflicts reported, aborting sync\n",
1684 /* no point in running it if we're just going to
1685 * overwrite everything
1687 if (!conf.overwrite && !conf.accept && !conf.dryrun) {
1688 runstage(&conf, "new", check_existing);
1691 if (conf.verbose && !conf.errors) {
1692 fprintf(stderr, "beginning %ssync\n", conf.dryrun ?
1697 handle_config_files(&conf);
1700 /* have to do the removes first otherwise
1701 * old files may conflict with update file
1705 conf.exitonerror = conf.dryrun ? 0 : 1;
1706 conf.errabort = conf.dryrun ? 0 : 1;
1708 fprintf(stderr, "file ops: %lu\n", conf.ops_total);
1709 if (conf.ops_remove > 0) {
1711 fprintf(stderr, "removing %lu file%s\n", conf.ops_remove, conf.ops_remove > 1 ? "s" : "");
1714 conf.ops_completed = 0;
1715 conf.ops_total = conf.ops_remove;
1716 runstage(&conf, "remove", remove_files);
1717 if (conf.verbose && conf.progress < 2) {
1718 fprintf(stderr, " done\n");
1723 if (conf.ops_update > 0) {
1725 fprintf(stderr, "updating %lu file%s\n", conf.ops_update, conf.ops_update > 1 ? "s" : "");
1728 conf.ops_completed = 0;
1729 conf.ops_total = conf.ops_update;
1730 runstage(&conf, "update", install_files);
1731 if (conf.verbose && conf.progress < 2) {
1732 fprintf(stderr, " done\n");
1737 if (conf.ops_install > 0) {
1739 fprintf(stderr, "installing %lu file%s\n", conf.ops_install, conf.ops_install > 1 ? "s" : "");
1742 conf.ops_completed = 0;
1743 conf.ops_total = conf.ops_install;
1744 runstage(&conf, "new", install_files);
1745 if (conf.verbose && conf.progress < 2) {
1746 fprintf(stderr, " done\n");
1753 zpm_close(&localdb);
1754 zpm_close(conf.src);
1755 /* TODO add the note */
1756 zpm_buffer_free(&conf.note);
1757 return conf.errors ? 1 : 0;