1 #define _POSIX_C_SOURCE 200809L
15 /* needed for S_IFMT and AT_FDCWD */
24 struct zpm *log; /* logging db will be attached as "log" */
28 int errabort, errors, verbose, dryrun, conflicts;
29 int setuser, setgroup;
30 int reverse, exitonerror;
34 printf("usage: zpm $scriptname [-fncC] args ...\n");
37 static int exists(char *path, mode_t *mode) {
40 if (lstat(path, &st) == -1) {
43 if (mode) *mode = st.st_mode;
47 /* TODO maintain a list of already created directories */
48 static int create_leading_dirs(char *path) {
51 char pcopy[ZPM_PATH_MAX];
56 delim = strrchr(pcopy, '/');
57 if (!delim) return 1; /* not an error, but no leading dirs */
59 /* cut off last component */
68 delim = strchr(s, '/');
74 /* try to create the directory, if it exists
75 * and is a directory or a symlink, that's ok
77 if (mkdir(pcopy, 0755) == -1) {
80 if (lstat(pcopy, &st) == -1) {
84 switch (st.st_mode & S_IFMT) {
105 char *column(char *col, int ncols, char **vals, char **cols) {
109 for (i=0; i < ncols; i++) {
110 // fprintf(stderr, "checking '%s' = '%s'\n", cols[i], vals[i]);
112 if (!strcmp(col, cols[i])) {
120 #define IERR(x) do { conf->errors++; conf->log->errmsg = strdup(x); return conf->errabort; } while (0)
121 #define COL(x) column(x, ncols, vals, cols)
122 #define SYSERR(x) do { conf->log->error = 2; return conf->errabort; } while (0)
124 static char *ops[] = { "new", "remove", "update", 0 };
126 static int getop(char *opstr) {
129 if (!opstr) return 0;
130 for (i=0;ops[i];i++) {
131 if (!strcmp(opstr, ops[i])) {
138 static int report_conflicts(void *f, int ncols, char **vals, char **cols) {
139 struct config *conf = f;
140 char *path, *hash, *pkg, *conflict_type, *mds;
144 conflict_type = COL("conflict");
145 if (!strcmp(conflict_type, "hash")) {
147 fprintf(stderr, "hash conflict: package %s path %s hash %.8s\n",
150 if (!strcmp(conflict_type, "md")) {
152 fprintf(stderr, "md conflict: package %s path %s md %s\n",
155 fprintf(stderr, "%s conflict: package %s path %s\n",
156 conflict_type, pkg, path);
163 static int check_existing(void *f, int ncols, char **vals, char **cols) {
164 struct config *conf = f;
169 if (!path) IERR("can't check for existing");
172 printf("checkfor %s\n", path);
177 fprintf(stderr, "check for existing %s\n", path);
180 if (lstat(path, &st) == 0) {
181 fprintf(stderr, "%s exists \n", path);
185 /* not an error, file shouldn't exist*/
188 fprintf(stderr, "unable to check %s: %s\n",
189 path, strerror(errno));
197 static int update_files(void *f, int ncols, char **vals, char **cols) {
198 struct config *conf = f;
204 if (!pkg) IERR("can't get pkgid");
206 if (!path) IERR("can't get path");
208 if (!dest) IERR("no file dest");
210 /* hash is different, so no different than install,
211 * but we don't need to create leading directories
215 fprintf(stderr, "update %s\n", dest);
219 fprintf(stderr, "update not implemented: %s", dest);
225 static int remove_files(void *f, int ncols, char **vals, char **cols) {
226 struct config *conf = f;
231 if (!dest) IERR("no file dest");
234 char *ftype = COL("filetype");
238 case 'd': printf("rmdir %s\n", dest); break;
239 default: printf("unlink %s\n", dest); break;
244 if (lstat(dest, &st) == -1) {
248 if (S_ISDIR(st.st_mode)) {
250 fprintf(stderr, "rmdir(%s)\n", dest);
253 } else if (S_ISREG(st.st_mode)) {
254 /* TODO conf to import before removal */
256 fprintf(stderr, "unlink(%s)\n", dest);
258 if (unlink(dest) == -1) {
259 IERR("can't unlink");
262 if (unlink(dest) == -1) {
263 IERR("can't unlink");
270 #define MARK fprintf(stderr, "%s %d: mark\n", __func__, __LINE__)
272 static int install_files(void *f, int ncols, char **vals, char **cols) {
273 struct config *conf = f;
287 /* TODO put the result row in a hash table. May not actually
292 if (op == 0) IERR("invalid operation");
294 /* TODO config to dishonor setuid/setgid */
296 if (!path) IERR("no file path");
297 if (strlen(path) == 0) {
298 IERR("zero length path not allowed");
301 if (!dest) IERR("no file dest");
302 if (strlen(dest) == 0) {
303 IERR("zero length dest not allowed");
308 if (!val) IERR("can't determine mode");
309 mode = strtoul(val, NULL, 8);
311 val = COL("filetype");
312 if (!val || strlen(val) == 0) {
313 IERR("can't determine file type");
319 if (!hash) IERR("can't get hash");
323 fprintf(stderr, "installing '%c' %s\n", ftype, dest);
330 val = COL("username");
331 if (!val) IERR("no username");
333 if (!pw) IERR("no passwd entry");
336 if (conf->setgroup) {
337 val = COL("groupname");
338 if (!val) IERR("no groupname");
340 if (!gr) IERR("no group entry");
345 //printf("cld %s\n", path);
346 printf("new %c%o %d:%d %s -> %s\n", ftype, mode, uid, gid, path, dest);
350 /* TODO should these be owned by the path owner if they don't exist? */
351 /* probably, though they then belong to the package, sort of */
352 if (!create_leading_dirs(dest)) {
353 fprintf(stderr, "unable to create leading directories for %s\n",
359 if (mkdir(dest, mode) == -1) {
362 } else if (ftype == 'r') {
364 source = conf->src ? conf->src : conf->log;
365 if (conf->verbose > 1) {
366 fprintf(stderr, "extracting %8.8s to %s with mode %o\n",
369 if (!zpm_extract(source, hash, dest, mode)) {
370 IERR("can't extract file");
372 } else if (ftype == 'l') {
373 char *target = COL("target");
375 fprintf(stderr, "no target for symlink %s\n", path);
377 return conf->errabort;
380 if (strlen(target) == 0) {
381 IERR("zero length symlink not allowed");
384 if (conf->verbose > 0) {
385 fprintf(stderr, "symlink %s -> %s\n", dest, target);
387 if (symlink(target, dest) == -1) {
388 perror("symlink failed");
389 IERR("can't symlink");
392 fprintf(stderr, "unhandled filetype %c\n", ftype);
395 if (conf->setuser && conf->setgroup) {
396 if (chown(dest, uid, gid) == -1) {
401 struct timespec times[2] = { {0}, {0} };
402 double mtime = strtod(COL("mtime"),NULL);
404 times[0].tv_nsec = UTIME_OMIT;
405 times[1].tv_sec = (time_t)llrint(floor(mtime));
406 times[1].tv_nsec = lrint(floor(fmod(mtime,1.0)*1000000000));
408 utimensat(AT_FDCWD, dest, times, AT_SYMLINK_NOFOLLOW);
411 printf("%s\n", path);
417 static void check_conflicts(struct config *conf, char *conflict_type,
418 int (callback)(void *, int, char **, char **)) {
424 s = sqlite3_str_new(conf->log->db);
425 sqlite3_str_appendall(s, "select *, ");
427 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
429 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
431 sqlite3_str_appendall(s, " as dest from syncconflicts");
434 sqlite3_str_appendf(s," where conflict = %Q", conflict_type);
437 sqlite3_str_appendall(s," order by length(path) desc, path desc,pkgid collate vercmp desc, conflict desc");
439 sqlite3_str_appendall(s," order by length(path), path, pkgid collate vercmp, conflict");
443 sql = sqlite3_str_value(s);
444 if (conf->verbose > 2) {
445 fprintf(stderr, "stage query: %s\n", sql);
448 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
450 sqlite3_str_finish(s);
453 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
455 fprintf(stderr, "database error: %s\n", errmsg);
458 if (conf->log->error == 1) {
459 fprintf(stderr, "unable to allocate memory\n");
461 fprintf(stderr, "zpm_exec failure: %s\n",
462 conf->log->errmsg ? conf->log->errmsg : "unknown");
465 if (conf->log->errmsg) {
466 fprintf(stderr, "error: %s\n", conf->log->errmsg);
468 if (conf->errors && conf->exitonerror) {
469 zpm_close(conf->log);
470 zpm_close(conf->src);
473 /* TODO final report function in conf var */
476 static void runstage(struct config *conf, char *stage,
477 int (callback)(void *, int, char **, char **)) {
483 s = sqlite3_str_new(conf->log->db);
484 sqlite3_str_appendall(s, "select *, ");
486 sqlite3_str_appendf(s, "printf('%%s/%%s',rtrim(%Q,'/'),ltrim(path,'/'))", conf->rootdir);
488 sqlite3_str_appendf(s, "printf('/%%s', trim(path, '/'))");
490 sqlite3_str_appendall(s, " as dest from syncinfo");
493 sqlite3_str_appendf(s," where op = %Q", stage);
496 sqlite3_str_appendall(s," order by length(path) desc, path desc");
499 sql = sqlite3_str_value(s);
500 if (conf->verbose > 2) {
501 fprintf(stderr, "stage query: %s\n", sql);
504 rv = zpm_exec(conf->log, sql, callback, conf, &errmsg);
506 sqlite3_str_finish(s);
509 fprintf(stderr, "exec fail: %s\n", sqlite3_errstr(rv));
511 fprintf(stderr, "database error: %s\n", errmsg);
514 if (conf->log->error == 1) {
515 fprintf(stderr, "unable to allocate memory\n");
517 fprintf(stderr, "zpm_exec failure: %s\n",
518 conf->log->errmsg ? conf->log->errmsg : "unknown");
521 if (conf->log->errmsg) {
522 fprintf(stderr, "error: %s\n", conf->log->errmsg);
524 if (conf->errors && conf->exitonerror) {
525 zpm_close(conf->log);
526 zpm_close(conf->src);
529 /* TODO final report function in conf var */
532 int main(int ac, char **av){
536 char *pkgdbfile = 0, *localdbfile = 0;
553 if (geteuid() != 0) {
558 localdbfile = ZPM_LOCAL_DB;
559 if ((s = getenv("ZPMDB"))) {
560 /* TODO does this need to be copied ? */
564 if ((s = getenv("ZPM_ROOT_DIR"))) {
565 /* TODO does this need to be copied ? */
570 * -d localdb or ZPMDB * or /var/lib/zpm/zpm.db, or die
571 * -f 'package database', otherwise regular default of env
572 * ZPM_PACKAGE_FILE, or use pkgdb if otherwise not found
573 * -R root of pkg, will just chdir there
575 * args are pkgid triple, but will do a pkg find on the pkgdb
578 while ((opt = getopt(ac, av, "f:d:c:nCR:v")) != -1) {
580 case 'd': localdbfile = optarg; break;
581 case 'f': pkgdbfile = optarg; break;
582 case 'n': conf.dryrun = 1; break;
583 case 'v': conf.verbose++; break;
584 case 'C': conf.errabort = 0; break;
585 case 'R': conf.rootdir = optarg; break;
586 case 'N': conf.setuser = 0; conf.setgroup = 0; break;
594 /* verify root dir exists */
595 if (conf.rootdir && !exists(conf.rootdir, NULL)) {
596 fprintf(stderr, "rootdir %s does not exist\n", conf.rootdir);
599 if (!zpm_open(&localdb, localdbfile)) {
600 fprintf(stderr, "can't open zpm db %s\n", localdbfile);
606 if (!zpm_open(&pkgdb, pkgdbfile)) {
607 fprintf(stderr, "can't open src db %s\n", localdbfile);
614 /* TODO find pkgid from arg */
616 /* TODO set conf var to finalize error reporting */
618 fprintf(stderr, "syncing filesystem %s (ldb %s) from %s\n",
619 conf.rootdir ? conf.rootdir : "/",
620 localdbfile, pkgdbfile);
624 conf.exitonerror = 0;
625 check_conflicts(&conf, NULL, report_conflicts);
626 if (conf.conflicts) {
627 fprintf(stderr, "%d conflicts reported, aborting sync\n",
631 runstage(&conf, "new", check_existing);
634 conf.exitonerror = 1;
635 runstage(&conf, "new", install_files);
636 runstage(&conf, "update", update_files);
638 runstage(&conf, "remove", remove_files);
645 return conf.errors ? 1 : 0;