1 #define _POSIX_C_SOURCE 200809L
16 /* needed for S_IFMT and AT_FDCWD */
25 struct zpm *log; /* logging db will be attached as "log" */
29 int errabort, errors, verbose, dryrun, conflicts;
30 int setuser, setgroup;
31 int reverse, exitonerror;
32 int overwrite, absorb;
36 printf("usage: zpm $scriptname [-fncC] args ...\n");
39 static int seterror(struct config *conf, char *msgfmt, ...) {
46 vsnprintf(msg, sizeof msg, msgfmt, ap);
50 if (conf->log->errmsg) {
51 free(conf->log->errmsg);
54 conf->log->errmsg = strdup(msg);
57 fprintf(stderr, "%s\n", msg);
60 return conf->errabort;
63 static int setsyserr(struct config *conf, char *msgfmt, ...) {
71 printed = vsnprintf(msg, sizeof msg, msgfmt, ap);
75 /* nothing we can really do */
76 return conf->errabort;
79 if ((size_t)printed < sizeof msg) {
80 snprintf(msg+printed, sizeof msg - printed, ": %s",
85 if (conf->log->errmsg) {
86 free(conf->log->errmsg);
89 conf->log->errmsg = strdup(msg);
92 fprintf(stderr, "%s\n", msg);
95 return conf->errabort;
98 static int exists(char *path, mode_t *mode) {
101 if (lstat(path, &st) == -1) {
104 if (mode) *mode = st.st_mode;
108 /* TODO maintain a list of already created directories */
109 static int create_leading_dirs(char *path) {
112 char pcopy[ZPM_PATH_MAX];
117 delim = strrchr(pcopy, '/');
118 if (!delim) return 1; /* not an error, but no leading dirs */
120 /* cut off last component */
129 delim = strchr(s, '/');
135 /* try to create the directory, if it exists
136 * and is a directory or a symlink, that's ok
138 if (mkdir(pcopy, 0755) == -1) {
141 if (lstat(pcopy, &st) == -1) {
145 switch (st.st_mode & S_IFMT) {
166 static char *column(char *col, int ncols, char **vals, char **cols) {
170 for (i=0; i < ncols; i++) {
171 // fprintf(stderr, "checking '%s' = '%s'\n", cols[i], vals[i]);
173 if (!strcmp(col, cols[i])) {
181 #define COL(x) column(x, ncols, vals, cols)
182 #define SYSERR(x) do { conf->log->error = 2; return conf->errabort; } while (0)
185 static char *ops[] = { "new", "remove", "update", 0 };
193 static int getop(char *opstr) {
196 if (!opstr) return 0;
197 for (i=0;ops[i];i++) {
198 if (!strcmp(opstr, ops[i])) {
205 static int report_conflicts(void *f, int ncols, char **vals, char **cols) {
206 struct config *conf = f;
207 char *path, *hash, *pkg, *conflict_type, *mds;
211 conflict_type = COL("conflict");
212 if (!strcmp(conflict_type, "hash")) {
214 fprintf(stderr, "hash conflict: package %s path %s hash %.8s\n",
217 if (!strcmp(conflict_type, "md")) {
219 fprintf(stderr, "md conflict: package %s path %s md %s\n",
222 fprintf(stderr, "%s conflict: package %s path %s\n",
223 conflict_type, pkg, path);
230 static int check_existing(void *f, int ncols, char **vals, char **cols) {
231 struct config *conf = f;
237 return seterror(conf, "no path");
241 printf("checkfor %s\n", path);
246 fprintf(stderr, "check for existing %s\n", path);
249 if (lstat(path, &st) == 0) {
250 fprintf(stderr, "%s exists\n", path);
254 /* not an error, file shouldn't exist*/
257 fprintf(stderr, "unable to check %s: %s\n",
258 path, strerror(errno));
266 static int remove_files(void *f, int ncols, char **vals, char **cols) {
267 struct config *conf = f;
272 if (!dest) return seterror(conf,"no file dest");
275 char *ftype = COL("filetype");
279 case 'd': printf("rmdir %s\n", dest); break;
280 default: printf("unlink %s\n", dest); break;
285 if (lstat(dest, &st) == -1) {
286 return seterror(conf,"can't stat");
289 if (S_ISDIR(st.st_mode)) {
291 fprintf(stderr, "rmdir(%s)\n", dest);
294 } else if (S_ISREG(st.st_mode)) {
295 /* TODO conf to import before removal */
297 fprintf(stderr, "unlink(%s)\n", dest);
299 if (unlink(dest) == -1) {
300 return seterror(conf, "can't unlink");
303 if (unlink(dest) == -1) {
304 return seterror(conf, "can't unlink");
311 #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__)
324 struct timespec times[2];
327 #define D_NOEXIST 0x1
332 #define D_EISDIR 0x20
336 #define D_MTIME 0x200
337 #define D_ERROR 0x1000
338 #define D_STATERROR 0x2000
339 #define D_RLERROR 0x4000
341 /* 1 = file doesn't exist, 2 = file is a directory, target isn't */
342 /* 4 == ftype different */
343 /* 8 = hash different when both are regular files */
344 static unsigned int file_compare(struct nitem *n, struct stat *st) {
345 int etype = 0, stat_type;
346 char ehash[ZPM_HASH_STRLEN+1];
347 unsigned int diff = 0;
352 case 'd': etype = S_IFDIR; diff |= D_ISDIR ; break;
353 case 'r': etype = S_IFREG; break;
354 case 'l': etype = S_IFLNK; break;
355 default: etype = 0; break;
359 /* new file, so check type, hash, etc */
360 if (lstat(n->dest, st) == 0) {
361 stat_type = st->st_mode & S_IFMT;
362 if (stat_type != etype) {
365 if (stat_type == S_IFDIR) {
369 if (n->hash && etype == S_IFREG && stat_type == S_IFREG) {
370 zpm_hash(n->dest, ehash, 0);
371 if (strcmp(n->hash, ehash) != 0) {
375 if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) {
376 lsize = readlink(n->dest, link, sizeof link);
377 if (lsize == -1 || lsize == sizeof link) {
380 } else if (strcmp(n->target, link) != 0) {
384 if (n->uid != st->st_uid) {
388 if (n->gid != st->st_gid) {
392 if (n->mode != (st->st_mode & 07777)) {
398 case ENOENT: diff |= D_NOEXIST; break;
399 default: diff |= (D_STATERROR|D_ERROR); break;
406 static int read_item(struct config *conf, int ncols, char **vals, char **cols,
411 struct nitem zero = { 0 };
417 seterror(conf, "can't determine op");
422 seterror(conf, "can't determine op");
426 n->path = COL("path");
428 seterror(conf, "no file path");
431 if (strlen(n->path) == 0) {
432 seterror(conf, "zero length path not allowed");
436 /* TODO config to dishonor setuid/setgid */
437 n->dest = COL("dest");
439 seterror(conf, "no file dest");
443 if (strlen(n->dest) == 0) {
444 seterror(conf, "zero length dest not allowed");
451 seterror(conf, "can't determine mode");
455 n->mode = strtoul(val, NULL, 8);
457 val = COL("filetype");
458 if (!val || strlen(val) == 0) {
459 seterror(conf, "can't determine file type");
464 if (n->ftype == 'r') {
465 n->hash = COL("hash");
467 seterror(conf, "can't get hash");
470 } else if (n->ftype == 'l') {
471 n->target = COL("target");
473 seterror(conf, "can't get target");
476 if (strlen(n->target) == 0) {
477 seterror(conf, "zero length target not allowed");
484 val = COL("username");
486 seterror(conf, "no username");
491 seterror(conf, "no passwd entry");
499 if (conf->setgroup) {
500 val = COL("groupname");
502 seterror(conf, "no groupname");
507 seterror(conf, "no group entry");
515 double mtime = strtod(COL("mtime"),NULL);
517 n->times[0].tv_sec = 0;
518 n->times[0].tv_nsec = UTIME_OMIT;
519 n->times[1].tv_sec = (time_t)llrint(floor(mtime));
520 n->times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000));
525 static int remove_dir(struct config *conf, char *path) {
530 setsyserr(conf, "can't rmdir %s", path);
536 static int remove_existing(struct config *conf, char *path) {
541 setsyserr(conf, "can't unlink %s", path);
547 static int set_md(struct config *conf, struct nitem *item) {
550 rv = chmod(item->path, item->mode);
552 setsyserr(conf, "can't chmod");
553 return conf->errabort;
556 if (conf->setuser && conf->setgroup) {
557 rv = chown(item->path, item->uid, item->gid);
559 setsyserr(conf, "can't chown %s", item->dest);
560 return conf->errabort;
564 rv = utimensat(AT_FDCWD, item->dest, item->times, AT_SYMLINK_NOFOLLOW);
566 setsyserr(conf, "can't set mtime");
567 return conf->errabort;
572 /* install a file or create a directory or symlink. path should not exist
575 /* flags: 1 = set md, 2 = create leading dirs, 4 = unlink existing file,
576 * 8 = rmdir existing dir, 16 = return true/false
578 static int install(struct config *conf, struct nitem *item, unsigned int flags) {
582 int mkleading = (flags & 2);
583 int setmd = (flags & 1);
584 int unlink_file = (flags & 4);
585 int rm_dir = (flags & 8);
586 int failure = conf->errabort;
594 source = conf->src ? conf->src : conf->log;
597 rv = remove_existing(conf, item->dest);
599 rv = remove_dir(conf, item->dest);
607 rv = create_leading_dirs(item->dest);
609 setsyserr(conf, "can't create leading dirs for %s", item->dest);
614 if (item->ftype == 'r') {
615 rv = zpm_extract(source, item->hash, item->dest, item->mode);
617 seterror(conf, "can't extract %s", item->dest);
623 switch (item->ftype) {
624 case 'd': rv = mkdir(item->dest, item->mode);
626 case 'l': rv = symlink(item->target, item->dest);
633 setsyserr(conf, "installing %s failed", item->dest);
638 return set_md(conf, item) == 0 ? success : failure;
644 static int install_files(void *f, int ncols, char **vals, char **cols) {
645 struct config *conf = f;
647 struct stat existing;
650 /* TODO put the result row in a hash table. May not actually
653 if (!read_item(conf, ncols, vals, cols, &nitem)) {
654 fprintf(stderr, "can't read item\n");
655 return conf->errabort;
659 fprintf(stderr, "%d '%c' %s\n", nitem.op, nitem.ftype,
664 printf("new %c%o %d:%d %s -> %s\n", nitem.ftype, nitem.mode,
665 nitem.uid, nitem.gid, nitem.path, nitem.dest);
669 unsigned int diffs = file_compare(&nitem, &existing);
670 if (diffs >= D_ERROR) {
671 return seterror(conf, "can't check %s", nitem.dest);
675 fprintf(stderr, "diffs = %u\n", diffs);
679 * exist & same type & md same & hash same: do nothing, but warn bug
680 * exist & same type & md diff & hash same: fix md
681 * exist & same type & md same & hash diff: replace
682 * exist & same type & md diff & hash diff: replace & fix
683 * no exist: install and warn
684 * dir & not dir : remove, mkdir
685 * not dir & not dir & diff type: remove, install
686 * not dir & dir : remove dir if empty, error if not empty, install
689 * no exist: create leading dirs, install
691 * exist & same type & md same & hash same & accept or over: do nothing
692 * exist & same & md diff or hash diff & overwrite : update
693 * exist & same & md diff or hash diff & accept : error, can't accept
694 * exist & same & md diff or hash diff & not accept : error
696 * exist & different type & not overwrite : error
697 * not dir & not dir & overwrite : remove and install
698 * not dir & dir & overwrite: remove empty or error, install
699 * dir & dir & overwrite: fix md
700 * dir & not dir & overwrite: remove and mkdir
702 int exist = (!(diffs & D_NOEXIST));
703 int sametype = (!(diffs & D_TYPE));
704 int mdsame = (!(diffs & D_MD));
705 int hashsame = (!(diffs & D_HASH));
706 int isdir = (diffs & D_ISDIR);
707 int eisdir = (diffs & D_EISDIR);
708 int accept = conf->absorb;
709 int overwrite = conf->overwrite;
710 int installing = (nitem.op == OP_NEW);
711 update = (nitem.op == OP_UPDATE);
715 /* warn, it should exist */
716 fprintf(stderr, "%s missing, installing", nitem.dest);
717 return install(conf, &nitem, 3);
720 /* file exists in filesystem */
722 if (mdsame && hashsame) {
723 fprintf(stderr, "%s should not be an update", nitem.dest);
724 /* warn, bug in logic. This shouldn't
725 * occur, because if there is nothing
726 * to do, it shouldn't be listed
732 if (!mdsame && hashsame) {
734 return set_md(conf, &nitem);
736 if (mdsame && !hashsame) {
738 return install(conf, &nitem, 3);
740 if (!mdsame && !hashsame) {
742 return install(conf, &nitem, 3);
746 /* file exists, and is not the same type */
748 if (isdir && !eisdir) {
749 /* remove existing */
751 return install(conf, &nitem, 7);
753 if (!isdir && eisdir) {
754 /* remove dir, or error */
756 return install(conf, &nitem, 11);
758 if (!isdir && !isdir) {
759 /* necessarily !sametype, sametype handled above */
760 /* remove existing */
762 return install(conf, &nitem, 7);
764 /* error, should not be possible, assert(0)? */
769 return install(conf, &nitem, 3);
772 /* file exists in filesystem */
774 if (mdsame && hashsame && (accept || overwrite)) {
778 if (mdsame && hashsame && !(accept || overwrite)) {
780 return seterror(conf, "bad conditions");
782 if (mdsame && !hashsame && overwrite) {
784 return install(conf, &nitem, eisdir ? 11 : 7);
786 if (mdsame && !hashsame && !overwrite) {
787 /* accept doesn't matter, since it's
788 * not an acceptable file */
790 return seterror(conf, accept ? "existing file not acceptable" : "file exists");
792 if (!mdsame && hashsame && overwrite) {
794 return set_md(conf, &nitem);
796 if (!mdsame && hashsame && !overwrite) {
797 /* accept doesn't matter, since it's
798 * not an acceptable file */
800 return seterror(conf, accept ? "existing file not acceptable" : "file exists");
802 if (!mdsame && !hashsame && overwrite) {
804 return install(conf, &nitem, eisdir ? 11 : 7);
806 if (!mdsame && !hashsame && !overwrite) {
807 /* accept doesn't matter, since it's
808 * not an acceptable file */
810 return seterror(conf, accept ? "existing file not acceptable" : "file exists");
812 /* TODO error, should be impossible */
813 return seterror(conf, "impossible state reached");
816 /* file exists, and is not the same type */
819 return seterror(conf, accept ? "existing file not acceptable" : "file exists");
822 /* not the same type, but ok to overwrite */
824 /* remove existing */
825 return install(conf, &nitem, 7);
828 /* existing path is a directory */
831 /* impossible, if isdir and eisdir, would
835 return set_md(conf, &nitem);
837 /* remove empty dir or error */
839 return install(conf, &nitem, 11);
841 /* if we get here, we missed a case */
843 return seterror(conf, "impossible state 2 reached");
847 printf("%s\n", nitem.path);
853 static void check_conflicts(struct config *conf, char *conflict_type,
854 int (callback)(void *, int, char **, char **)) {
860 s = sqlite3_str_new(conf->log->db);
861 sqlite3_str_appendall(s, "select *, ");
863 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
865 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
867 sqlite3_str_appendall(s, " as dest from syncconflicts");
870 sqlite3_str_appendf(s," where conflict = %Q", conflict_type);
873 sqlite3_str_appendall(s," order by length(path) desc, path desc,pkgid collate vercmp desc, conflict desc");
875 sqlite3_str_appendall(s," order by length(path), path, pkgid collate vercmp, conflict");
879 sql = sqlite3_str_value(s);
880 if (conf->verbose > 2) {
881 fprintf(stderr, "stage query: %s\n", sql);
884 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
886 sqlite3_str_finish(s);
889 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
891 fprintf(stderr, "database error: %s\n", errmsg);
894 if (conf->log->error == 1) {
895 fprintf(stderr, "unable to allocate memory\n");
897 fprintf(stderr, "zpm_exec failure: %s\n",
898 conf->log->errmsg ? conf->log->errmsg : "unknown");
901 if (conf->log->errmsg) {
902 fprintf(stderr, "error: %s\n", conf->log->errmsg);
904 if (conf->errors && conf->exitonerror) {
905 zpm_close(conf->log);
906 zpm_close(conf->src);
909 /* TODO final report function in conf var */
912 static void runstage(struct config *conf, char *stage,
913 int (callback)(void *, int, char **, char **)) {
919 s = sqlite3_str_new(conf->log->db);
920 sqlite3_str_appendall(s, "select *, ");
922 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
924 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
926 sqlite3_str_appendall(s, " as dest from syncinfo");
929 sqlite3_str_appendf(s," where op = %Q", stage);
932 sqlite3_str_appendall(s," order by length(path) desc, path desc");
935 sql = sqlite3_str_value(s);
936 if (conf->verbose > 2) {
937 fprintf(stderr, "stage query: %s\n", sql);
940 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
942 sqlite3_str_finish(s);
945 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
947 fprintf(stderr, "database error: %s\n", errmsg);
950 if (conf->log->error == 1) {
951 fprintf(stderr, "unable to allocate memory\n");
953 fprintf(stderr, "zpm_exec failure: %s\n",
954 conf->log->errmsg ? conf->log->errmsg : "unknown");
957 if (conf->log->errmsg) {
958 fprintf(stderr, "error: %s\n", conf->log->errmsg);
960 if (conf->errors && conf->exitonerror) {
961 zpm_close(conf->log);
962 zpm_close(conf->src);
965 /* TODO final report function in conf var */
968 int main(int ac, char **av){
972 char *pkgdbfile = 0, *localdbfile = 0;
991 if (geteuid() != 0) {
996 localdbfile = ZPM_LOCAL_DB;
997 if ((s = getenv("ZPMDB"))) {
998 /* TODO does this need to be copied ? */
1002 if ((s = getenv("ZPM_ROOT_DIR"))) {
1003 /* TODO does this need to be copied ? */
1008 * -d localdb or ZPMDB * or /var/lib/zpm/zpm.db, or die
1009 * -f 'package database', otherwise regular default of env
1010 * ZPM_PACKAGE_FILE, or use pkgdb if otherwise not found
1011 * -R root of pkg, will just chdir there
1013 * args are pkgid triple, but will do a pkg find on the pkgdb
1016 while ((opt = getopt(ac, av, "f:d:c:nCR:vOA")) != -1) {
1018 case 'd': localdbfile = optarg; break;
1019 case 'f': pkgdbfile = optarg; break;
1020 case 'n': conf.dryrun = 1; break;
1021 case 'v': conf.verbose++; break;
1022 case 'C': conf.errabort = 0; break;
1023 case 'R': conf.rootdir = optarg; break;
1024 case 'N': conf.setuser = 0; conf.setgroup = 0; break;
1025 case 'O': conf.overwrite = 1; break;
1026 case 'A': conf.absorb = 1; break;
1034 /* verify root dir exists */
1035 if (conf.rootdir && !exists(conf.rootdir, NULL)) {
1036 fprintf(stderr, "rootdir %s does not exist\n", conf.rootdir);
1039 if (!zpm_open(&localdb, localdbfile)) {
1040 fprintf(stderr, "can't open zpm db %s\n", localdbfile);
1043 conf.log = &localdb;
1046 if (!zpm_open(&pkgdb, pkgdbfile)) {
1047 fprintf(stderr, "can't open src db %s\n", localdbfile);
1054 /* TODO find pkgid from arg */
1056 /* TODO set conf var to finalize error reporting */
1058 fprintf(stderr, "syncing filesystem %s (ldb %s) from %s\n",
1059 conf.rootdir ? conf.rootdir : "/",
1060 localdbfile, pkgdbfile);
1064 conf.exitonerror = 0;
1065 check_conflicts(&conf, NULL, report_conflicts);
1066 if (conf.conflicts) {
1067 fprintf(stderr, "%d conflicts reported, aborting sync\n",
1071 /* no point in running it if we're just going to
1072 * overwrite everything
1074 if (conf.overwrite == 0 || conf.absorb == 0) {
1075 runstage(&conf, "new", check_existing);
1079 fprintf(stderr, "beginning sync\n");
1081 /* have to do the removes first otherwise
1082 * old files may conflict with update file
1086 conf.exitonerror = 1;
1089 fprintf(stderr, "removing old files\n");
1091 runstage(&conf, "remove", remove_files);
1094 fprintf(stderr, "updating files\n");
1096 runstage(&conf, "update", install_files);
1098 fprintf(stderr, "installing files\n");
1100 runstage(&conf, "new", install_files);
1104 zpm_close(&localdb);
1105 zpm_close(conf.src);
1106 return conf.errors ? 1 : 0;