#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include "zpm.h" #include "sqlite/sqlite3.h" struct opts { char *package, *version; int release; int complete, addcontent, isconfig, opt_l, recursive, verbose; int followsymlinks; int xargs, noclear; int striparg, init; mode_t clmode; char *cltype, *clhash, *clmtime, *prefix, *strip, *cltarget; char *clgroup, *cluser; }; #if 0 static void warn(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); } #endif static int dieval = EXIT_FAILURE; static void die(char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); exit(dieval); } char *cleanpath(char *path, char *strip, char *prefix) { char *s = path; char *t = s; size_t len = 0; char *new; if (strip) { len = strlen(strip); if (strncmp(path, strip, len)) { /* strip is not a prefix */ return 0; } s += len; } len += strlen(s)+1; new = malloc(len); if (!new) { return NULL; } memset(new, 0, len); t = new; if (*s == 0) { /* path is only stripped */ return new; } if (prefix) { strcpy(new, prefix); t = new + strlen(prefix); } /* skip leading curdir */ if (*s && *s == '.' && s[1] && s[1] == '/') { s += 2; } /* skip leading slashes */ while (*s == '/') { s++; } if (*t != '/') { *t++ = '/'; } for (; *s; s++) { /* skip multiple slashes */ if (*s == '/' && s[1] && s[1] == '/') { continue; } /* skip trailing slash */ if (*s == '/' && !s[1]) { continue; } /* skip curdirs */ if (*s == '/' && s[1] && s[1] == '.' && s[2] && s[2] == '/') { s += 1; continue; } if (*s == '/' && s[1] && s[1] == '.' && s[2] == 0) { s += 1; continue; } *t++ = *s; } *t = 0; return new; } char *getuser(uid_t uid) { struct passwd *pw; pw = getpwuid(uid); if (pw) { return strdup(pw->pw_name); } return 0; } char *getgroup(uid_t uid) { struct passwd *pw; pw = getpwuid(uid); if (pw) { return strdup(pw->pw_name); } return 0; } void free_file(struct zpm_file *file) { free(file->path); free(file->owner); free(file->group); free(file->target); file->path = file->owner = file->group = file->target = 0; } int stat_file(struct zpm_file *file, char *path, struct opts *opt) { struct stat st; int rv; file->data = path; errno = 0; if (opt->followsymlinks) { rv = stat(path, &st); } else { rv = lstat(path, &st); } if (rv == -1) { return 0; } switch (st.st_mode & S_IFMT) { case S_IFBLK: file->type = 'b'; break; case S_IFCHR: file->type = 'c'; break; case S_IFDIR: file->type = 'd'; break; case S_IFIFO: file->type = 'p'; break; case S_IFLNK: file->type = 'l'; break; case S_IFREG: file->type = 'r'; break; case S_IFSOCK: file->type = 's'; break; default: file->type = 0; break; } file->configuration = opt->isconfig; if (file->type == 0) { return 0; } if (file->type == 'l') { if (opt->cltarget) { file->target = strdup(opt->cltarget); } else { char linkval[4096]; size_t n; n = readlink(path, linkval, sizeof linkval); if (n >= sizeof linkval) { return 0; } linkval[n] = 0; file->target = strdup(linkval); } } /* strip and prefix */ file->path = cleanpath(path, opt->strip, opt->prefix); if (file->path == 0) { free_file(file); return 0; } if (opt->clmode) { file->mode = opt->clmode & 07777; } else { file->mode = st.st_mode & 07777; } if (opt->cluser) { file->owner = strdup(opt->cluser); } else { file->owner = getuser(st.st_uid); } if (file->owner == 0) { free_file(file); return 0; } if (opt->clgroup) { file->group = strdup(opt->clgroup); } else { file->group = getgroup(st.st_gid); } if (file->group == 0) { free_file(file); return 0; } file->mtime = st.st_mtime; return 1; } int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt); char *pathcat(char *dir, char *path) { size_t dirlen = 0, pathlen = 0; char *cat; /* chop off trailing / on dir */ if (dir) { dirlen = strlen(dir); while (dirlen && dir[dirlen-1] == '/') { dirlen--; } } if (path) { pathlen = strlen(path); while (*path && *path == '/') { path++; pathlen--; } } cat = malloc(dirlen + pathlen + 2); if (cat) { strncpy(cat, dir, dirlen); cat[dirlen] = '/'; strcpy(cat+dirlen+1, path); } return cat; } int add_dir(struct zpm *zpm, char *path, struct opts *opt) { DIR *dir; struct dirent *de; struct zpm_file file = { 0 }; int fail = 0; char *dpath = 0; dir = opendir(path); if (!dir) { zpm_seterror(zpm, "can't open dir %s: %s", path, strerror(errno)); return 0; } while ((de = readdir(dir))) { if (!strcmp(de->d_name, ".")) { continue; } if (!strcmp(de->d_name, "..")) { continue; } dpath = pathcat(path, de->d_name); if (!dpath) { zpm_seterror(zpm, "pathcat failed"); fail = 1; break; } if (!stat_file(&file, dpath, opt)) { zpm_seterror(zpm, "stat %s failed: %s", dpath, strerror(errno)); fail = 1; break; } if (!add_file(zpm, &file, opt)) { free_file(&file); fail = 1; break; } free_file(&file); free(dpath); } closedir(dir); return fail ? 0 : 1; } int add_file(struct zpm *zpm, struct zpm_file *file, struct opts *opt) { char hash[ZPM_HASH_STRLEN+1]; if (file->path[0] != 0) { if (file->type == 'r') { if (opt->addcontent) { if (zpm_import(zpm, file->data, 0, hash)) { strcpy(file->hash, hash); } else { return 0; } } else { zpm_hash(file->data, hash, 0); strcpy(file->hash, hash); } } zpm_db_run(zpm, "insert or replace into packagefiles " "(package,version,release,path,mode,mtime,username," "groupname,filetype,hash,configuration,target)" " values " "(%Q, %Q, %d, %Q, %o, %d, %Q, %Q, '%c', %Q, %d, %Q)", opt->package, opt->version, opt->release, file->path, file->mode, (int)file->mtime, file->owner, file->group, file->type, file->type == 'r' ? file->hash : NULL, file->configuration, file->type == 'l' ? file->target : NULL ); if (zpm->error) { return 0; } if (opt->verbose > 1) { printf("%c%o %s:%s %s\n", file->type, file->mode, file->owner, file->group, file->path); } else if (opt->verbose > 0) { printf("%s\n", file->path); fflush(stdout); } } if (opt->recursive && file->type == 'd') { if (!add_dir(zpm, file->data, opt)) { return 0; } } return 1; } int main(int ac, char **av) { struct zpm zpm; struct opts opt = { 0 }; int i; int option; char *pkgstr = 0, *pkgid = 0; char package[128]; char version[64]; int release; char *dbfile = getenv("ZPMDB"); if (!dbfile) { dbfile = "/var/lib/zpm/local.db"; } opt.addcontent = 1; while ((option = getopt(ac, av, "iCF:H:M:NP:S:T:cf:g:hlm:ru:vxzp:")) != -1) { switch (option) { case 'f': dbfile = optarg; break; case 'i': opt.init = 1; break; case 'C': opt.complete = 1; break; case 'F': opt.cltype = optarg; break; case 'H': opt.clhash = optarg; break; case 'M': opt.clmtime = optarg; break; case 'N': opt.addcontent = 0; break; case 'P': opt.prefix = optarg; break; case 'S': opt.strip = optarg; break; case 's': opt.striparg = 1; break; case 'T': opt.cltarget = optarg; break; case 'c': opt.isconfig = 1; break; case 'g': opt.clgroup = optarg; break; case 'h': opt.followsymlinks = 1; break; case 'l': opt.opt_l = 1; break; case 'm': opt.clmode = strtol(optarg, NULL, 8); break; case 'r': opt.recursive = 1; break; case 'u': opt.cluser = optarg; break; case 'v': opt.verbose++; break; case 'x': opt.xargs = 1; dieval = 255; break; case 'z': opt.noclear = 1; break; case 'p': pkgstr = optarg; break; default: exit(EXIT_FAILURE); break; } } if (ac < optind) { exit(EXIT_FAILURE); } if (opt.init) { if (!zpm_init(&zpm, dbfile)) { die("can't open zpm db %s", dbfile); } if (opt.verbose) { fprintf(stderr, "inited %s\n", dbfile); } } else { if (!zpm_open(&zpm, dbfile)) { die("can't open zpm db %s", dbfile); } } #if 0 i = sqlite3_config(SQLITE_CONFIG_MMAP_SIZE,98222080); if (i != SQLITE_OK) { exit(3); } #endif if (!pkgstr) { pkgstr = dbfile; } pkgid = zpm_findpkg(&zpm, pkgstr, NULL); if (!pkgid) { int rv; if (opt.init) { rv = zpm_parse_package(pkgstr, package, version, &release); if (rv != 3) { die("incomplete package string '%s'\n", pkgstr); } else { rv = zpm_create_package(&zpm, package, version, release); pkgid = zpm_findpkg(&zpm, pkgstr, NULL); if (!rv) { die("unable to create package %s\n", pkgstr); } } } else { die("package %s not found\n", pkgstr); } } zpm_parse_package(pkgid, package, version, &release); opt.package = package; opt.version = version; opt.release = release; if (opt.verbose) { printf("adding to %s %s\n", dbfile, pkgid); } fflush(stdout); //zpm_begin(&zpm); struct zpm_file file; for (i=optind; av[i]; i++) { if (!stat_file(&file, av[i], &opt)) { die("can't stat %s: %s", av[i], strerror(errno)); } file.data = av[i]; if (opt.striparg) { opt.strip = av[i]; } if (add_file(&zpm, &file, &opt)) { free_file(&file); } else { die("error: %s", zpm.errmsg); } } if (opt.complete) { zpm_db_run(&zpm, "update packages set build_time = %d where package = %Q and version = %Q and release = %d", time(NULL), package, version, release); zpm_package_sethash(&zpm, pkgid, 0); } else if (!opt.noclear) { zpm_db_run(&zpm, "update packages set build_time = null, hash = null where package = %Q and version = %Q and release = %d", package, version, release); } /* TODO error check */ //zpm_commit(&zpm); zpm_close(&zpm); return zpm.error; }