1 #define _POSIX_C_SOURCE 200809L
17 /* needed for S_IFMT and AT_FDCWD */
26 struct zpm *log; /* logging db will be attached as "log" */
30 int errabort, errors, verbose, dryrun, conflicts;
31 int setuser, setgroup;
32 int reverse, exitonerror;
33 int overwrite, absorb;
46 char *pkglist; /* space separated */
50 int configuration, oldwasconf;
51 struct timespec times[2];
55 printf("usage: zpm $scriptname [-fncC] args ...\n");
58 static int seterror(struct config *conf, char *msgfmt, ...) {
65 vsnprintf(msg, sizeof msg, msgfmt, ap);
69 if (conf->log->errmsg) {
70 free(conf->log->errmsg);
73 conf->log->errmsg = strdup(msg);
76 fprintf(stderr, "%s\n", msg);
79 return conf->errabort;
82 static int setsyserr(struct config *conf, char *msgfmt, ...) {
90 printed = vsnprintf(msg, sizeof msg, msgfmt, ap);
94 /* nothing we can really do */
95 return conf->errabort;
98 if ((size_t)printed < sizeof msg) {
99 snprintf(msg+printed, sizeof msg - printed, ": %s",
104 if (conf->log->errmsg) {
105 free(conf->log->errmsg);
108 conf->log->errmsg = strdup(msg);
111 fprintf(stderr, "%s\n", msg);
114 return conf->errabort;
117 static int exists(char *path, mode_t *mode) {
120 if (lstat(path, &st) == -1) {
123 if (mode) *mode = st.st_mode;
127 /* TODO maintain a list of already created directories */
128 static int create_leading_dirs(char *path) {
131 char pcopy[ZPM_PATH_MAX];
136 delim = strrchr(pcopy, '/');
137 if (!delim) return 1; /* not an error, but no leading dirs */
139 /* cut off last component */
148 delim = strchr(s, '/');
154 /* try to create the directory, if it exists
155 * and is a directory or a symlink, that's ok
156 * should be (eventually) a symlink to a directory
157 * so we want stat here, not lstat
159 if (mkdir(pcopy, 0755) == -1) {
162 if (stat(pcopy, &st) == -1) {
166 switch (st.st_mode & S_IFMT) {
186 static char *column(char *col, int ncols, char **vals, char **cols) {
190 for (i=0; i < ncols; i++) {
191 // fprintf(stderr, "checking '%s' = '%s'\n", cols[i], vals[i]);
193 if (!strcmp(col, cols[i])) {
201 #define COL(x) column(x, ncols, vals, cols)
202 #define SYSERR(x) do { conf->log->error = 2; return conf->errabort; } while (0)
205 static char *ops[] = { "new", "remove", "update", 0 };
213 static int getop(char *opstr) {
216 if (!opstr) return 0;
217 for (i=0;ops[i];i++) {
218 if (!strcmp(opstr, ops[i])) {
225 static int report_conflicts(void *f, int ncols, char **vals, char **cols) {
226 struct config *conf = f;
227 char *path, *hash, *pkg, *conflict_type, *mds;
231 conflict_type = COL("conflict");
232 if (!strcmp(conflict_type, "hash")) {
234 fprintf(stderr, "hash conflict: package %s path %s hash %.8s\n",
237 if (!strcmp(conflict_type, "md")) {
239 fprintf(stderr, "md conflict: package %s path %s md %s\n",
242 fprintf(stderr, "%s conflict: package %s path %s\n",
243 conflict_type, pkg, path);
250 static int check_existing(void *f, int ncols, char **vals, char **cols) {
251 struct config *conf = f;
257 return seterror(conf, "no path");
261 printf("checkfor %s\n", path);
267 fprintf(stderr, "check for existing %s\n", path);
270 if (lstat(path, &st) == 0) {
271 fprintf(stderr, "%s exists\n", path);
275 /* not an error, file shouldn't exist*/
278 fprintf(stderr, "unable to check %s: %s\n",
279 path, strerror(errno));
287 static int remove_files(void *f, int ncols, char **vals, char **cols) {
288 struct config *conf = f;
294 if (!dest) return seterror(conf,"no file dest");
297 char *ftype = COL("filetype");
301 case 'd': printf("rmdir %s\n", dest); break;
302 default: printf("unlink %s\n", dest); break;
308 if (lstat(dest, &st) == -1) {
309 return seterror(conf,"can't stat");
312 if (S_ISDIR(st.st_mode)) {
313 flags = AT_REMOVEDIR;
315 /* TODO check that expected filetype matches actual filetype */
318 fprintf(stderr, "%s(%s)\n", flags ? "rmdir" : "unlink", dest);
323 if (unlinkat(AT_FDCWD, dest, flags) == -1) {
328 return seterror(conf, "can't unlink");
335 #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__)
337 /* file does not exist */
338 #define D_NOEXIST 0x1
339 /* files are different types */
341 /* metadata is different */
343 /* content or link target is different */
345 /* file to be installed is a directory */
347 /* path on disk is a directory */
348 #define D_EISDIR 0x20
349 /* usernames different */
351 /* group names different */
353 /* file mode is different */
355 /* mtimes are different */
356 #define D_MTIME 0x200
357 /* the hash of the file we are supposedly replacing is different than
358 * the the hash of the file on disk
360 #define D_OHASH 0x400
361 /* an error occurred trying to compare the file (other than it doesn't exist */
362 #define D_ERROR 0x1000
363 /* there was a stat error */
364 #define D_STATERROR 0x2000
365 /* there was an error calling readlink */
366 #define D_RLERROR 0x4000
368 /* 1 = file doesn't exist, 2 = file is a directory, target isn't */
369 /* 4 == ftype different */
370 /* 8 = hash different when both are regular files */
371 static unsigned int file_compare(struct nitem *n, struct stat *st) {
372 int etype = 0, stat_type;
373 char ehash[ZPM_HASH_STRLEN+1];
374 unsigned int diff = 0;
379 case 'd': etype = S_IFDIR; diff |= D_ISDIR ; break;
380 case 'r': etype = S_IFREG; break;
381 case 'l': etype = S_IFLNK; break;
382 default: etype = 0; break;
386 /* new file, so check type, hash, etc */
387 if (lstat(n->dest, st) == 0) {
388 stat_type = st->st_mode & S_IFMT;
389 if (stat_type != etype) {
392 if (stat_type == S_IFDIR) {
396 if (n->hash && etype == S_IFREG && stat_type == S_IFREG) {
397 zpm_hash(n->dest, ehash, 0);
398 if (strcmp(n->hash, ehash) != 0) {
401 if (n->ohash && strcmp(n->ohash, ehash) != 0) {
405 if (n->hash && etype == S_IFLNK && stat_type == S_IFLNK) {
406 lsize = readlink(n->dest, link, sizeof link);
408 if (lsize == -1 || lsize == sizeof link) {
413 if (strcmp(n->target, link) != 0) {
418 if (n->uid != st->st_uid) {
422 if (n->gid != st->st_gid) {
426 if (n->mode != (st->st_mode & 07777)) {
432 case ENOENT: diff |= D_NOEXIST; break;
433 default: diff |= (D_STATERROR|D_ERROR); break;
440 static int read_item(struct config *conf, int ncols, char **vals, char **cols,
446 struct nitem zero = { 0 };
452 seterror(conf, "can't determine op");
458 seterror(conf, "can't determine op");
462 n->path = COL("path");
464 seterror(conf, "no file path");
467 if (strlen(n->path) == 0) {
468 seterror(conf, "zero length path not allowed");
472 /* TODO config to dishonor setuid/setgid */
473 n->dest = COL("dest");
475 seterror(conf, "no file dest");
479 if (strlen(n->dest) == 0) {
480 seterror(conf, "zero length dest not allowed");
487 seterror(conf, "can't determine mode");
491 n->mode = strtoul(val, NULL, 8);
493 val = COL("configuration");
495 seterror(conf, "can't determine config status");
498 lval = strtol(val, NULL, 10);
500 n->configuration = ((lval & 1) != 0);
501 n->oldwasconf = ((lval & 2) != 0);
503 val = COL("filetype");
504 if (!val || strlen(val) == 0) {
505 seterror(conf, "can't determine file type");
510 /* these can be null */
511 n->ohash = COL("ohash");
513 n->omds = COL("omds");
514 n->pkglist = COL("pkglist");
516 if (n->ftype == 'r') {
517 n->hash = COL("hash");
519 seterror(conf, "can't get hash");
522 } else if (n->ftype == 'l') {
523 n->target = COL("target");
525 seterror(conf, "can't get target");
528 if (strlen(n->target) == 0) {
529 seterror(conf, "zero length target not allowed");
536 val = COL("username");
538 seterror(conf, "no username");
543 seterror(conf, "no passwd entry");
551 if (conf->setgroup) {
552 val = COL("groupname");
554 seterror(conf, "no groupname");
559 seterror(conf, "no group entry");
568 double mtime = strtod(COL("mtime"),NULL);
570 mtime = (double)time(NULL);
573 n->mtime = (time_t)mtime;
575 n->times[0].tv_sec = 0;
576 n->times[0].tv_nsec = UTIME_OMIT;
577 n->times[1].tv_sec = (time_t)llrint(floor(mtime));
578 n->times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000));
583 static int remove_dir(struct config *conf, char *path) {
588 setsyserr(conf, "can't rmdir %s", path);
594 static int remove_existing(struct config *conf, char *path) {
599 setsyserr(conf, "can't unlink %s", path);
605 static int set_md(struct config *conf, struct nitem *item) {
610 if (item->ftype != 'l') {
611 printf("chmod %o %s\n", item->mode, item->dest);
613 if (conf->setuser && conf->setgroup) {
614 printf("lchown %d:%d %s\n", item->uid, item->gid,
617 printf("mtime %.0f %s\n", (double)item->mtime, item->dest);
622 /* can't chmod a symlink */
623 if (item->ftype != 'l') {
624 rv = chmod(item->dest, item->mode);
627 setsyserr(conf, "can't chmod %o %s", item->mode, item->dest);
628 return conf->errabort;
632 if (conf->setuser && conf->setgroup) {
633 rv = lchown(item->dest, item->uid, item->gid);
635 setsyserr(conf, "can't lchown %s", item->dest);
636 return conf->errabort;
640 rv = utimensat(AT_FDCWD, item->dest, item->times, AT_SYMLINK_NOFOLLOW);
642 setsyserr(conf, "can't set mtime %.0f %s", (double)item->mtime,
644 return conf->errabort;
649 /* install a file or create a directory or symlink. path should not exist
652 /* flags: 1 = set md, 2 = create leading dirs, 4 = unlink existing file,
653 * 8 = rmdir existing dir, 16 = return true/false
657 #define INS_UNLINK 0x4
658 #define INS_RMDIR 0x8
660 #define INS_ZPMNEW 0x20
661 static int install(struct config *conf, struct nitem *item, unsigned int flags) {
665 int mkleading = (flags & 2);
666 int setmd = (flags & 1);
667 int unlink_file = (flags & 4);
668 int rm_dir = (flags & 8);
669 int failure = conf->errabort;
679 printf("unlink %s\n", item->dest);
681 printf("rmdir %s\n", item->dest);
684 printf("install %c%o %d:%d %s", item->ftype,
685 item->mode, item->uid, item->gid,
687 if (item->ftype == 'l') {
688 printf(" -> %s", item->target);
695 source = conf->src ? conf->src : conf->log;
698 rv = remove_existing(conf, item->dest);
700 rv = remove_dir(conf, item->dest);
708 rv = create_leading_dirs(item->dest);
710 setsyserr(conf, "can't create leading dirs for %s", item->dest);
715 if (item->ftype == 'r') {
716 rv = zpm_extract(source, item->hash, item->dest, item->mode);
718 seterror(conf, "can't extract %s", item->dest);
724 switch (item->ftype) {
725 case 'd': rv = mkdir(item->dest, item->mode);
727 case 'l': rv = symlink(item->target, item->dest);
734 setsyserr(conf, "installing %s failed", item->dest);
739 return set_md(conf, item) == 0 ? success : failure;
746 * figure out what the difference is for a config file, only called
747 * for an update of a configuration file
748 * return -1 on an error
749 * return 1 if the new file should not be installed
750 * return 0 if the new file should be installed
752 static int adjust_for_config(struct config *conf, struct nitem *n, unsigned int
755 if (!n->oldwasconf) {
759 /* TODO what if old was a directory? */
760 if (!n->configuration) {
761 /* replacing conf with non-conf */
762 /* absorb file, mark todo */
763 char hash[ZPM_HASH_STRLEN+1];
766 fprintf(stderr, "importing old conf file\n");
768 if (zpm_import(conf->log, n->dest, 0, hash)) {
769 zpm_note_add(conf->log, n->pkglist, n->dest, hash,
770 "replaced config file with non-config. zpm-cat %.8s", hash);
772 fprintf(stderr, "unable to import existing config file %s\n", n->dest);
776 fprintf(stderr, "dry-run: would replace config file %s with non-config file\n", n->dest);
781 int sametype = (!(diffs & D_TYPE));
782 int isdir = (diffs & D_ISDIR);
783 int eisdir = (diffs & D_EISDIR);
785 /* both old and new are config files */
786 if (isdir && sametype) {
787 /* both config directories, can only be changing
788 * metadata, so no adjustment needed
791 fprintf(stderr, "both config dirs, ok to update\n");
797 char hash[ZPM_HASH_STRLEN+1];
799 /* replacing old file with new directory */
800 /* absorb, make note */
802 if (zpm_import(conf->log, n->dest, 0, hash)) {
803 zpm_note_add(conf->log, n->pkglist, n->dest, hash,
804 "replaced config file with config directory. zpm-cat %.8s", hash);
806 fprintf(stderr, "unable to import existing config file %s\n", n->dest);
810 fprintf(stderr, "dry-run: would replace config file %s with config directory\n", n->dest);
816 /* replacing old conf directory with a conf file.
817 * nothing needs to be done, if the directory
818 * is empty, it's ok to remove. if it's not empty,
819 * the install will fail
824 /* replacing old file with new file */
825 /* new is same as on disk */
826 if (!(diffs & D_HASH)) {
828 fprintf(stderr, "new config file is already on disk, probably shouldn't happen\n");
833 /* new is different than on disk, but on disk is same as old */
834 if (!(diffs & D_OHASH)) {
836 fprintf(stderr, "old config file not changed from default, replacing with new default\n");
838 /* ok to do the update, since same as default */
839 fprintf(stderr, "updating default config %s\n", n->dest);
843 /* new is different than on disk, and disk different than old */
846 fprintf(stderr, "new default config file is different than on disk, and old default was changed, should keep on-disk config\n");
849 zpm_note_add(conf->log, n->pkglist, n->dest, n->hash,
850 "default config file update. zpm-cat %.8s", n->hash);
851 /* TODO check for note error */
853 fprintf(stderr, "dry-run: default config file %s update\n",
860 static int install_files(void *f, int ncols, char **vals, char **cols) {
861 struct config *conf = f;
863 struct stat existing;
866 /* TODO put the result row in a hash table. May not actually
869 if (!read_item(conf, ncols, vals, cols, &nitem)) {
870 fprintf(stderr, "can't read item\n");
871 return conf->errabort;
874 if (conf->verbose && !conf->dryrun) {
875 fprintf(stderr, "%s '%c' %s\n", nitem.opstr, nitem.ftype,
879 unsigned int diffs = file_compare(&nitem, &existing);
880 if (diffs >= D_ERROR) {
881 return seterror(conf, "can't check %s", nitem.dest);
885 * exist & same type & md same & hash same: do nothing, but warn bug
886 * exist & same type & md diff & hash same: fix md
887 * exist & same type & md same & hash diff: replace
888 * exist & same type & md diff & hash diff: replace & fix
889 * no exist: install and warn
890 * dir & not dir : remove, mkdir
891 * not dir & not dir & diff type: remove, install
892 * not dir & dir : remove dir if empty, error if not empty, install
895 * no exist: create leading dirs, install
897 * exist & same type & md same & hash same & accept or over: do nothing
898 * exist & same & md diff or hash diff & overwrite : update
899 * exist & same & md diff or hash diff & accept : error, can't accept
900 * exist & same & md diff or hash diff & not accept : error
902 * exist & different type & not overwrite : error
903 * not dir & not dir & overwrite : remove and install
904 * not dir & dir & overwrite: remove empty or error, install
905 * dir & dir & overwrite: fix md
906 * dir & not dir & overwrite: remove and mkdir
908 int exist = (!(diffs & D_NOEXIST));
909 int sametype = (!(diffs & D_TYPE));
910 int mdsame = (!(diffs & D_MD));
911 int hashsame = (!(diffs & D_HASH));
912 int isdir = (diffs & D_ISDIR);
913 int eisdir = (diffs & D_EISDIR);
914 int accept = conf->absorb;
915 int overwrite = conf->overwrite;
916 int installing = (nitem.op == OP_NEW);
917 update = (nitem.op == OP_UPDATE);
921 /* warn, it should exist */
922 fprintf(stderr, "%s missing, installing", nitem.dest);
923 return install(conf, &nitem, 3);
926 switch (adjust_for_config(conf, &nitem, diffs)) {
927 case -1: return conf->errabort; break;
929 fprintf(stderr, "skipping changed default config file: %s\n", nitem.dest);
934 /* file exists in filesystem */
936 if (mdsame && hashsame) {
937 /* warn, bug in logic. This shouldn't occur,
938 * because if there is nothing to do, it
939 * shouldn't be listed as an update
941 /* could be an update. We're checking against
942 * what's actually on disk, not what was
943 * expected to have been on disk. So, if
944 * the admin has modified the file, or if
945 * it had been installed ignoring the user
946 * and group, it might be correct on disk
947 * but not as in the local database
949 /* TODO detect whether this a logic bug or
950 * an on-disk difference
953 fprintf(stderr, "%s should not be an update\n", nitem.dest);
954 fprintf(stderr, "old hash: %s\n", nitem.ohash);
955 fprintf(stderr, "new hash: %s\n", nitem.hash);
956 fprintf(stderr, "old mds: %s\n", nitem.omds);
957 fprintf(stderr, "new mds: %s\n", nitem.mds);
962 if (!mdsame && hashsame) {
964 return set_md(conf, &nitem);
966 if (mdsame && !hashsame) {
968 return install(conf, &nitem, 3);
970 if (!mdsame && !hashsame) {
972 return install(conf, &nitem, 3);
976 /* file exists, and is not the same type */
978 if (isdir && !eisdir) {
979 /* remove existing */
981 return install(conf, &nitem, 7);
983 if (!isdir && eisdir) {
984 /* remove dir, or error */
986 return install(conf, &nitem, 11);
988 if (!isdir && !isdir) {
989 /* necessarily !sametype, sametype handled above */
990 /* remove existing */
992 return install(conf, &nitem, 7);
994 /* error, should not be possible, assert(0)? */
995 fprintf(stderr,"impossible state: %s:%d\n", __func__, __LINE__);
1000 return install(conf, &nitem, 3);
1003 /* file exists in filesystem */
1005 if (mdsame && hashsame && (accept || overwrite)) {
1007 if (conf->dryrun || conf->verbose) {
1008 fprintf(stderr, "accept %s: %s\n",
1009 eisdir ? "directory" : "file", nitem.dest);
1014 if (mdsame && hashsame && !(accept || overwrite)) {
1016 return seterror(conf, "file exists: %s", nitem.dest);
1019 if (mdsame && !hashsame && overwrite) {
1021 return install(conf, &nitem, eisdir ? 11 : 7);
1024 if (nitem.configuration && accept) {
1025 /* accept a changed config file */
1026 if (conf->dryrun || conf->verbose) {
1027 fprintf(stderr, "accept %smodified config %s: %s\n", (!mdsame || !hashsame) ? "" : "un",
1028 eisdir ? "directory" : "file", nitem.dest);
1033 if (mdsame && !hashsame && !overwrite) {
1034 /* accept doesn't matter, since it's
1035 * not an acceptable file */
1037 if (nitem.ftype == 'l') {
1040 lsize = readlink(nitem.dest, link, sizeof link);
1041 if (lsize == -1 || (size_t)lsize >= sizeof link) {
1042 return seterror(conf, "%s (linkdiff): expecting %s -> %s, unable to read link", accept ? "existing file not acceptable" : "file exists", nitem.dest, nitem.target, link);
1045 /* links must be different */
1046 return seterror(conf, "%s (linkdiff): expecting %s -> %s, have -> %s", accept ? "existing file not acceptable" : "file exists", nitem.dest, nitem.target, link);
1049 return seterror(conf, "%s (hashdiff): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1052 if (!mdsame && hashsame && overwrite) {
1054 return set_md(conf, &nitem);
1056 if (!mdsame && hashsame && !overwrite) {
1057 /* accept doesn't matter, since it's
1058 * not an acceptable file */
1060 return seterror(conf, "%s (mddiff): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1062 if (!mdsame && !hashsame && overwrite) {
1064 return install(conf, &nitem, eisdir ? 11 : 7);
1066 if (!mdsame && !hashsame && !overwrite) {
1067 /* accept doesn't matter, since it's
1068 * not an acceptable file */
1070 return seterror(conf, "%s (md+hash): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1072 /* TODO error, should be impossible */
1073 return seterror(conf, "impossible state reached");
1076 /* file exists, and is not the same type */
1079 return seterror(conf, "%s (difftype): %s", accept ? "existing file not acceptable" : "file exists", nitem.dest);
1082 /* not the same type, but ok to overwrite */
1084 /* remove existing */
1085 return install(conf, &nitem, 7);
1088 /* existing path is a directory */
1091 /* impossible, if isdir and eisdir, would
1095 return set_md(conf, &nitem);
1097 /* remove empty dir or error */
1099 return install(conf, &nitem, 11);
1101 /* if we get here, we missed a case */
1103 return seterror(conf, "impossible state 2 reached");
1106 /* TODO extra verbose print perms, mtime, etc, probably ls -l
1109 if (conf->verbose) {
1110 printf("%s\n", nitem.path);
1116 static void check_conflicts(struct config *conf, char *conflict_type,
1117 int (callback)(void *, int, char **, char **)) {
1123 s = sqlite3_str_new(conf->log->db);
1124 sqlite3_str_appendall(s, "select *, ");
1125 if (conf->rootdir) {
1126 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1128 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1130 sqlite3_str_appendall(s, " as dest from syncconflicts");
1132 if (conflict_type) {
1133 sqlite3_str_appendf(s," where conflict = %Q", conflict_type);
1135 if (conf->reverse) {
1136 sqlite3_str_appendall(s," order by length(path) desc, path desc,pkgid collate vercmp desc, conflict desc");
1138 sqlite3_str_appendall(s," order by length(path), path, pkgid collate vercmp, conflict");
1142 sql = sqlite3_str_value(s);
1144 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
1146 sqlite3_str_finish(s);
1149 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1151 fprintf(stderr, "database error: %s\n", errmsg);
1154 if (conf->log->error == 1) {
1155 fprintf(stderr, "unable to allocate memory\n");
1157 fprintf(stderr, "zpm_exec failure: %s\n",
1158 conf->log->errmsg ? conf->log->errmsg : "unknown");
1161 if (conf->log->errmsg) {
1162 fprintf(stderr, "error: %s\n", conf->log->errmsg);
1164 if (conf->errors && conf->exitonerror) {
1165 zpm_close(conf->log);
1166 zpm_close(conf->src);
1169 /* TODO final report function in conf var */
1172 static void runstage(struct config *conf, char *stage,
1173 int (callback)(void *, int, char **, char **)) {
1179 s = sqlite3_str_new(conf->log->db);
1180 sqlite3_str_appendall(s, "select *, ");
1181 if (conf->rootdir) {
1182 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
1184 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
1186 sqlite3_str_appendall(s, " as dest from syncinfo");
1189 sqlite3_str_appendf(s," where op = %Q", stage);
1191 if (conf->reverse) {
1192 sqlite3_str_appendall(s," order by length(path) desc, path desc");
1195 sql = sqlite3_str_value(s);
1197 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
1199 sqlite3_str_finish(s);
1202 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
1204 fprintf(stderr, "database error: %s\n", errmsg);
1207 if (conf->log->error == 1) {
1208 fprintf(stderr, "unable to allocate memory\n");
1210 fprintf(stderr, "zpm_exec failure: %s\n",
1211 conf->log->errmsg ? conf->log->errmsg : "unknown");
1215 if (conf->log->errmsg) {
1216 fprintf(stderr, "error: %s\n", conf->log->errmsg);
1219 if (conf->errors && conf->exitonerror) {
1220 zpm_close(conf->log);
1221 zpm_close(conf->src);
1224 /* TODO final report function in conf var */
1227 int main(int ac, char **av){
1231 char *pkgdbfile = 0, *localdbfile = 0;
1250 if (geteuid() != 0) {
1255 localdbfile = ZPM_LOCAL_DB;
1256 if ((s = getenv("ZPMDB"))) {
1257 /* TODO does this need to be copied ? */
1261 if ((s = getenv("ZPM_ROOT_DIR"))) {
1262 /* TODO does this need to be copied ? */
1267 * -d localdb or ZPMDB * or /var/lib/zpm/zpm.db, or die
1268 * -f 'package database', otherwise regular default of env
1269 * ZPM_PACKAGE_FILE, or use pkgdb if otherwise not found
1270 * -R root of pkg, will just chdir there
1272 * args are pkgid triple, but will do a pkg find on the pkgdb
1275 while ((opt = getopt(ac, av, "f:d:c:nCR:vOA")) != -1) {
1277 case 'd': localdbfile = optarg; break;
1278 case 'f': pkgdbfile = optarg; break;
1279 case 'n': conf.dryrun = 1; break;
1280 case 'v': conf.verbose++; break;
1281 case 'C': conf.errabort = 0; break;
1282 case 'R': conf.rootdir = optarg; break;
1283 case 'N': conf.setuser = 0; conf.setgroup = 0; break;
1284 case 'O': conf.overwrite = 1; break;
1285 case 'A': conf.absorb = 1; break;
1293 /* verify root dir exists */
1294 if (conf.rootdir && !exists(conf.rootdir, NULL)) {
1295 fprintf(stderr, "rootdir %s does not exist\n", conf.rootdir);
1298 if (!zpm_open(&localdb, localdbfile)) {
1299 fprintf(stderr, "can't open zpm db %s\n", localdbfile);
1302 conf.log = &localdb;
1305 if (!zpm_open(&pkgdb, pkgdbfile)) {
1306 fprintf(stderr, "can't open src db %s\n", localdbfile);
1313 /* TODO find pkgid from arg */
1315 /* TODO set conf var to finalize error reporting */
1317 fprintf(stderr, "syncing filesystem %s (ldb %s) from %s\n",
1318 conf.rootdir ? conf.rootdir : "/",
1319 localdbfile, pkgdbfile);
1323 conf.exitonerror = 0;
1324 check_conflicts(&conf, NULL, report_conflicts);
1326 if (conf.conflicts) {
1327 fprintf(stderr, "%d conflicts reported, aborting sync\n",
1331 /* no point in running it if we're just going to
1332 * overwrite everything
1334 if (!conf.overwrite && !conf.absorb && !conf.dryrun) {
1335 runstage(&conf, "new", check_existing);
1339 fprintf(stderr, "beginning %ssync\n", conf.dryrun ?
1342 /* have to do the removes first otherwise
1343 * old files may conflict with update file
1347 conf.exitonerror = conf.dryrun ? 0 : 1;
1348 conf.errabort = conf.dryrun ? 0 : 1;
1351 fprintf(stderr, "removing old files\n");
1353 runstage(&conf, "remove", remove_files);
1356 fprintf(stderr, "updating files\n");
1358 runstage(&conf, "update", install_files);
1360 fprintf(stderr, "installing files\n");
1362 runstage(&conf, "new", install_files);
1366 zpm_close(&localdb);
1367 zpm_close(conf.src);
1368 return conf.errors ? 1 : 0;