Index: head/usr.sbin/pkg_install/add/extract.c =================================================================== --- head/usr.sbin/pkg_install/add/extract.c (revision 131279) +++ head/usr.sbin/pkg_install/add/extract.c (revision 131280) @@ -1,277 +1,277 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 18 July 1993 * * This is the package extraction code for the add module. * */ #include __FBSDID("$FreeBSD$"); #include #include #include "lib.h" #include "add.h" #define STARTSTRING "tar cf -" #define TOOBIG(str) \ (((int)strlen(str) + FILENAME_MAX + where_count > maxargs) ||\ ((int)strlen(str) + FILENAME_MAX + perm_count > maxargs)) #define PUSHOUT(todir) /* push out string */ \ if (where_count > (int)sizeof(STARTSTRING)-1) { \ strcat(where_args, "|tar --unlink -xpf - -C "); \ strcat(where_args, todir); \ if (system(where_args)) { \ cleanup(0); \ errx(2, "%s: can not invoke %ld byte tar pipeline: %s", \ __func__, (long)strlen(where_args), where_args); \ } \ strcpy(where_args, STARTSTRING); \ where_count = sizeof(STARTSTRING)-1; \ } \ if (perm_count) { \ apply_perms(todir, perm_args); \ perm_args[0] = 0;\ perm_count = 0; \ } static void rollback(const char *name, const char *home, PackingList start, PackingList stop) { PackingList q; char try[FILENAME_MAX], bup[FILENAME_MAX]; const char *dir; dir = home; for (q = start; q != stop; q = q->next) { if (q->type == PLIST_FILE) { snprintf(try, FILENAME_MAX, "%s/%s", dir, q->name); if (make_preserve_name(bup, FILENAME_MAX, name, try) && fexists(bup)) { (void)chflags(try, 0); (void)unlink(try); if (rename(bup, try)) warnx("rollback: unable to rename %s back to %s", bup, try); } } else if (q->type == PLIST_CWD) { if (strcmp(q->name, ".")) dir = q->name; else dir = home; } } } #define add_char(buf, len, pos, ch) do {\ if ((pos) < (len)) { \ buf[(pos)] = (ch); \ buf[(pos) + 1] = '\0'; \ } \ ++(pos); \ } while (0) -int +static int add_arg(char *buf, int len, const char *str) { int i = 0; add_char(buf, len, i, ' '); for (; *str != '\0'; ++str) { if (!isalnum(*str) && *str != '/' && *str != '.' && *str != '-') add_char(buf, len, i, '\\'); add_char(buf, len, i, *str); } return (i); } void extract_plist(const char *home, Package *pkg) { PackingList p = pkg->head; char *last_file; char *where_args, *perm_args, *last_chdir; int maxargs, where_count = 0, perm_count = 0, add_count; Boolean preserve; maxargs = sysconf(_SC_ARG_MAX) / 2; /* Just use half the argument space */ where_args = alloca(maxargs); if (!where_args) { cleanup(0); errx(2, "%s: can't get argument list space", __func__); } perm_args = alloca(maxargs); if (!perm_args) { cleanup(0); errx(2, "%s: can't get argument list space", __func__); } strcpy(where_args, STARTSTRING); where_count = sizeof(STARTSTRING)-1; perm_args[0] = 0; last_chdir = 0; preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE; /* Reset the world */ Owner = NULL; Group = NULL; Mode = NULL; last_file = NULL; (const char *)Directory = home; /* Do it */ while (p) { char cmd[FILENAME_MAX]; switch(p->type) { case PLIST_NAME: PkgName = p->name; if (Verbose) printf("extract: Package name is %s\n", p->name); break; case PLIST_FILE: last_file = p->name; if (Verbose) printf("extract: %s/%s\n", Directory, p->name); if (!Fake) { char try[FILENAME_MAX]; /* first try to rename it into place */ snprintf(try, FILENAME_MAX, "%s/%s", Directory, p->name); if (fexists(try)) { (void)chflags(try, 0); /* XXX hack - if truly immutable, rename fails */ if (preserve && PkgName) { char pf[FILENAME_MAX]; if (make_preserve_name(pf, FILENAME_MAX, PkgName, try)) { if (rename(try, pf)) { warnx( "unable to back up %s to %s, aborting pkg_add", try, pf); rollback(PkgName, home, pkg->head, p); return; } } } } if (rename(p->name, try) == 0) { /* try to add to list of perms to be changed and run in bulk. */ if (p->name[0] == '/' || TOOBIG(p->name)) { PUSHOUT(Directory); } add_count = add_arg(&perm_args[perm_count], maxargs - perm_count, p->name); if (add_count < 0 || add_count >= maxargs - perm_count) { cleanup(0); errx(2, "%s: oops, miscounted strings!", __func__); } perm_count += add_count; } else { /* rename failed, try copying with a big tar command */ if (last_chdir != Directory) { if (last_chdir == NULL) { PUSHOUT(Directory); } else { PUSHOUT(last_chdir); } last_chdir = Directory; } else if (p->name[0] == '/' || TOOBIG(p->name)) { PUSHOUT(Directory); } add_count = add_arg(&where_args[where_count], maxargs - where_count, p->name); if (add_count < 0 || add_count >= maxargs - where_count) { cleanup(0); errx(2, "%s: oops, miscounted strings!", __func__); } where_count += add_count; add_count = add_arg(&perm_args[perm_count], maxargs - perm_count, p->name); if (add_count < 0 || add_count >= maxargs - perm_count) { cleanup(0); errx(2, "%s: oops, miscounted strings!", __func__); } perm_count += add_count; } } break; case PLIST_CWD: if (Verbose) printf("extract: CWD to %s\n", p->name); PUSHOUT(Directory); if (strcmp(p->name, ".")) { if (!Fake && make_hierarchy(p->name) == FAIL) { cleanup(0); errx(2, "%s: unable to cwd to '%s'", __func__, p->name); } Directory = p->name; } else (const char *)Directory = home; break; case PLIST_CMD: if ((strstr(p->name, "%B") || strstr(p->name, "%F") || strstr(p->name, "%f")) && last_file == NULL) { cleanup(0); errx(2, "%s: no last file specified for '%s' command", __func__, p->name); } if (strstr(p->name, "%D") && Directory == NULL) { cleanup(0); errx(2, "%s: no directory specified for '%s' command", __func__, p->name); } format_cmd(cmd, FILENAME_MAX, p->name, Directory, last_file); PUSHOUT(Directory); if (Verbose) printf("extract: execute '%s'\n", cmd); if (!Fake && system(cmd)) warnx("command '%s' failed", cmd); break; case PLIST_CHMOD: PUSHOUT(Directory); Mode = p->name; break; case PLIST_CHOWN: PUSHOUT(Directory); Owner = p->name; break; case PLIST_CHGRP: PUSHOUT(Directory); Group = p->name; break; case PLIST_COMMENT: break; case PLIST_IGNORE: p = p->next; break; default: break; } p = p->next; } PUSHOUT(Directory); } Index: head/usr.sbin/pkg_install/add/perform.c =================================================================== --- head/usr.sbin/pkg_install/add/perform.c (revision 131279) +++ head/usr.sbin/pkg_install/add/perform.c (revision 131280) @@ -1,597 +1,598 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 18 July 1993 * * This is the main body of the add module. * */ #include __FBSDID("$FreeBSD$"); #include #include #include "lib.h" #include "add.h" #include #include #include static int pkg_do(char *); static int sanity_check(char *); static char LogDir[FILENAME_MAX]; static int zapLogDir; /* Should we delete LogDir? */ int pkg_perform(char **pkgs) { int i, err_cnt = 0; signal(SIGINT, cleanup); signal(SIGHUP, cleanup); if (AddMode == SLAVE) err_cnt = pkg_do(NULL); else { for (i = 0; pkgs[i]; i++) err_cnt += pkg_do(pkgs[i]); } return err_cnt; } static Package Plist; static char *Home; /* * This is seriously ugly code following. Written very fast! * [And subsequently made even worse.. Sigh! This code was just born * to be hacked, I guess.. :) -jkh] */ static int pkg_do(char *pkg) { char pkg_fullname[FILENAME_MAX]; char playpen[FILENAME_MAX]; char extract_contents[FILENAME_MAX]; char *where_to, *extract; FILE *cfile; int code; PackingList p; struct stat sb; - int inPlace, conflictsfound, i, errcode; + int inPlace, conflictsfound, errcode; /* support for separate pre/post install scripts */ int new_m = 0; char pre_script[FILENAME_MAX] = INSTALL_FNAME; char post_script[FILENAME_MAX]; char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX]; char *conflict[2]; char **matched; conflictsfound = 0; code = 0; zapLogDir = 0; LogDir[0] = '\0'; strcpy(playpen, FirstPen); inPlace = 0; /* Are we coming in for a second pass, everything already extracted? */ if (!pkg) { fgets(playpen, FILENAME_MAX, stdin); playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */ if (chdir(playpen) == FAIL) { warnx("pkg_add in SLAVE mode can't chdir to %s", playpen); return 1; } read_plist(&Plist, stdin); where_to = playpen; } /* Nope - do it now */ else { /* Is it an ftp://foo.bar.baz/file.t[bg]z specification? */ if (isURL(pkg)) { if (!(Home = fileGetURL(NULL, pkg))) { warnx("unable to fetch '%s' by URL", pkg); return 1; } where_to = Home; strcpy(pkg_fullname, pkg); cfile = fopen(CONTENTS_FNAME, "r"); if (!cfile) { warnx( "unable to open table of contents file '%s' - not a package?", CONTENTS_FNAME); goto bomb; } read_plist(&Plist, cfile); fclose(cfile); } else { strcpy(pkg_fullname, pkg); /* * Copy for sanity's sake, * could remove pkg_fullname */ if (strcmp(pkg, "-")) { if (stat(pkg_fullname, &sb) == FAIL) { warnx("can't stat package file '%s'", pkg_fullname); goto bomb; } sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME); extract = extract_contents; } else { extract = NULL; sb.st_size = 100000; /* Make up a plausible average size */ } Home = make_playpen(playpen, sb.st_size * 4); if (!Home) errx(1, "unable to make playpen for %qd bytes", (long long)sb.st_size * 4); where_to = Home; /* Since we can call ourselves recursively, keep notes on where we came from */ if (!getenv("_TOP")) setenv("_TOP", Home, 1); if (unpack(pkg_fullname, extract)) { warnx( "unable to extract table of contents file from '%s' - not a package?", pkg_fullname); goto bomb; } cfile = fopen(CONTENTS_FNAME, "r"); if (!cfile) { warnx( "unable to open table of contents file '%s' - not a package?", CONTENTS_FNAME); goto bomb; } read_plist(&Plist, cfile); fclose(cfile); /* Extract directly rather than moving? Oh goodie! */ if (find_plist_option(&Plist, "extract-in-place")) { if (Verbose) printf("Doing in-place extraction for %s\n", pkg_fullname); p = find_plist(&Plist, PLIST_CWD); if (p) { if (!isdir(p->name) && !Fake) { if (Verbose) printf("Desired prefix of %s does not exist, creating..\n", p->name); vsystem("mkdir -p %s", p->name); if (chdir(p->name) == -1) { warn("unable to change directory to '%s'", p->name); goto bomb; } } where_to = p->name; inPlace = 1; } else { warnx( "no prefix specified in '%s' - this is a bad package!", pkg_fullname); goto bomb; } } /* * Apply a crude heuristic to see how much space the package will * take up once it's unpacked. I've noticed that most packages * compress an average of 75%, so multiply by 4 for good measure. */ if (!extract && !inPlace && min_free(playpen) < sb.st_size * 4) { warnx("projected size of %qd exceeds available free space.\n" "Please set your PKG_TMPDIR variable to point to a location with more\n" "free space and try again", (long long)sb.st_size * 4); warnx("not extracting %s\ninto %s, sorry!", pkg_fullname, where_to); goto bomb; } /* If this is a direct extract and we didn't want it, stop now */ if (inPlace && Fake) goto success; /* Finally unpack the whole mess. If extract is null we already + did so so don't bother doing it again. */ if (extract && unpack(pkg_fullname, NULL)) { warnx("unable to extract '%s'!", pkg_fullname); goto bomb; } } /* Check for sanity and dependencies */ if (sanity_check(pkg)) goto bomb; /* If we're running in MASTER mode, just output the plist and return */ if (AddMode == MASTER) { printf("%s\n", where_playpen()); write_plist(&Plist, stdout); return 0; } } /* * If we have a prefix, delete the first one we see and add this * one in place of it. */ if (Prefix) { delete_plist(&Plist, FALSE, PLIST_CWD, NULL); add_plist_top(&Plist, PLIST_CWD, Prefix); } setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1); /* Protect against old packages with bogus @name and origin fields */ if (Plist.name == NULL) Plist.name = "anonymous"; if (Plist.origin == NULL) Plist.origin = "anonymous/anonymous"; /* * See if we're already registered either with the same name (the same * version) or some other version with the same origin. */ - if ((isinstalledpkg(Plist.name) || + if ((isinstalledpkg(Plist.name) > 0 || matchbyorigin(Plist.origin, NULL) != NULL) && !Force) { warnx("package '%s' or its older version already installed", Plist.name); code = 1; goto success; /* close enough for government work */ } /* Now check the packing list for conflicts */ for (p = Plist.head; p != NULL; p = p->next) { if (p->type == PLIST_CONFLICTS) { + int i; conflict[0] = strdup(p->name); conflict[1] = NULL; matched = matchinstalled(MATCH_GLOB, conflict, &errcode); free(conflict[0]); if (errcode == 0 && matched != NULL) for (i = 0; matched[i] != NULL; i++) - if (isinstalledpkg(matched[i])) { + if (isinstalledpkg(matched[i]) > 0) { warnx("package '%s' conflicts with %s", Plist.name, matched[i]); conflictsfound = 1; } continue; } } if(conflictsfound) { if(!Force) { warnx("please use pkg_delete first to remove conflicting package(s) or -f to force installation"); code = 1; goto bomb; } else warnx("-f specified; proceeding anyway"); } /* Now check the packing list for dependencies */ for (p = Plist.head; p ; p = p->next) { char *deporigin; if (p->type != PLIST_PKGDEP) continue; deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : NULL; if (Verbose) { printf("Package '%s' depends on '%s'", Plist.name, p->name); if (deporigin != NULL) printf(" with '%s' origin", deporigin); printf(".\n"); } - if (!isinstalledpkg(p->name) && + if (isinstalledpkg(p->name) <= 0 && !(deporigin != NULL && matchbyorigin(deporigin, NULL) != NULL)) { char path[FILENAME_MAX], *cp = NULL; if (!Fake) { if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) { const char *ext; ext = strrchr(pkg_fullname, '.'); if (ext == NULL) ext = ".tbz"; snprintf(path, FILENAME_MAX, "%s/%s%s", getenv("_TOP"), p->name, ext); if (fexists(path)) cp = path; else cp = fileFindByPath(pkg, p->name); if (cp) { if (Verbose) printf("Loading it from %s.\n", cp); if (vsystem("pkg_add %s'%s'", Verbose ? "-v " : "", cp)) { warnx("autoload of dependency '%s' failed%s", cp, Force ? " (proceeding anyway)" : "!"); if (!Force) ++code; } } else { warnx("could not find package %s %s", p->name, Force ? " (proceeding anyway)" : "!"); if (!Force) ++code; } } else if ((cp = fileGetURL(pkg, p->name)) != NULL) { if (Verbose) printf("Finished loading %s over FTP.\n", p->name); if (!fexists("+CONTENTS")) { warnx("autoloaded package %s has no +CONTENTS file?", p->name); if (!Force) ++code; } else if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) { warnx("pkg_add of dependency '%s' failed%s", p->name, Force ? " (proceeding anyway)" : "!"); if (!Force) ++code; } else if (Verbose) printf("\t'%s' loaded successfully.\n", p->name); /* Nuke the temporary playpen */ leave_playpen(); } } else { if (Verbose) printf("and was not found%s.\n", Force ? " (proceeding anyway)" : ""); else printf("Package dependency %s for %s not found%s\n", p->name, pkg, Force ? " (proceeding anyway)" : "!"); if (!Force) ++code; } } else if (Verbose) printf(" - already installed.\n"); } if (code != 0) goto bomb; /* Look for the requirements file */ if (fexists(REQUIRE_FNAME)) { vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ if (Verbose) printf("Running requirements file first for %s..\n", Plist.name); if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, Plist.name)) { warnx("package %s fails requirements %s", pkg_fullname, Force ? "installing anyway" : "- not installed"); if (!Force) { code = 1; goto success; /* close enough for government work */ } } } /* * Test whether to use the old method of passing tokens to installation * scripts, and set appropriate variables.. */ if (fexists(POST_INSTALL_FNAME)) { new_m = 1; sprintf(post_script, "%s", POST_INSTALL_FNAME); pre_arg[0] = '\0'; post_arg[0] = '\0'; } else { if (fexists(INSTALL_FNAME)) { sprintf(post_script, "%s", INSTALL_FNAME); sprintf(pre_arg, "PRE-INSTALL"); sprintf(post_arg, "POST-INSTALL"); } } /* If we're really installing, and have an installation file, run it */ if (!NoInstall && fexists(pre_script)) { vsystem("chmod +x %s", pre_script); /* make sure */ if (Verbose) printf("Running pre-install for %s..\n", Plist.name); if (!Fake && vsystem("./%s %s %s", pre_script, Plist.name, pre_arg)) { warnx("install script returned error status"); unlink(pre_script); code = 1; goto success; /* nothing to uninstall yet */ } } /* Now finally extract the entire show if we're not going direct */ if (!inPlace && !Fake) extract_plist(".", &Plist); if (!Fake && fexists(MTREE_FNAME)) { if (Verbose) printf("Running mtree for %s..\n", Plist.name); p = find_plist(&Plist, PLIST_CWD); if (Verbose) printf("mtree -U -f %s -d -e -p %s >%s\n", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL); if (!Fake) { if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s >%s", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL)) warnx("mtree returned a non-zero status - continuing"); } } /* Run the installation script one last time? */ if (!NoInstall && fexists(post_script)) { vsystem("chmod +x %s", post_script); /* make sure */ if (Verbose) printf("Running post-install for %s..\n", Plist.name); if (!Fake && vsystem("./%s %s %s", post_script, Plist.name, post_arg)) { warnx("install script returned error status"); unlink(post_script); code = 1; goto fail; } } /* Time to record the deed? */ if (!NoRecord && !Fake) { char contents[FILENAME_MAX]; FILE *contfile; if (getuid() != 0) warnx("not running as root - trying to record install anyway"); sprintf(LogDir, "%s/%s", LOG_DIR, Plist.name); zapLogDir = 1; if (Verbose) printf("Attempting to record package into %s..\n", LogDir); if (make_hierarchy(LogDir)) { warnx("can't record package into '%s', you're on your own!", LogDir); bzero(LogDir, FILENAME_MAX); code = 1; goto success; /* close enough for government work */ } /* Make sure pkg_info can read the entry */ vsystem("chmod a+rx %s", LogDir); move_file(".", DESC_FNAME, LogDir); move_file(".", COMMENT_FNAME, LogDir); if (fexists(INSTALL_FNAME)) move_file(".", INSTALL_FNAME, LogDir); if (fexists(POST_INSTALL_FNAME)) move_file(".", POST_INSTALL_FNAME, LogDir); if (fexists(DEINSTALL_FNAME)) move_file(".", DEINSTALL_FNAME, LogDir); if (fexists(POST_DEINSTALL_FNAME)) move_file(".", POST_DEINSTALL_FNAME, LogDir); if (fexists(REQUIRE_FNAME)) move_file(".", REQUIRE_FNAME, LogDir); if (fexists(DISPLAY_FNAME)) move_file(".", DISPLAY_FNAME, LogDir); if (fexists(MTREE_FNAME)) move_file(".", MTREE_FNAME, LogDir); sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME); contfile = fopen(contents, "w"); if (!contfile) { warnx("can't open new contents file '%s'! can't register pkg", contents); goto success; /* can't log, but still keep pkg */ } write_plist(&Plist, contfile); fclose(contfile); for (p = Plist.head; p ; p = p->next) { char *deporigin, **depnames; int i; if (p->type != PLIST_PKGDEP) continue; deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : NULL; if (Verbose) { printf("Trying to record dependency on package '%s'", p->name); if (deporigin != NULL) printf(" with '%s' origin", deporigin); printf(".\n"); } depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) : NULL; if (depnames == NULL) { depnames = alloca(sizeof(*depnames) * 2); depnames[0] = p->name; depnames[1] = NULL; } for (i = 0; depnames[i] != NULL; i++) { sprintf(contents, "%s/%s/%s", LOG_DIR, depnames[i], REQUIRED_BY_FNAME); if (strcmp(p->name, depnames[i]) != 0) warnx("warning: package '%s' requires '%s', but '%s' " "is installed", Plist.name, p->name, depnames[i]); contfile = fopen(contents, "a"); if (!contfile) warnx("can't open dependency file '%s'!\n" "dependency registration is incomplete", contents); else { fprintf(contfile, "%s\n", Plist.name); if (fclose(contfile) == EOF) warnx("cannot properly close file %s", contents); } } } if (Verbose) printf("Package %s registered in %s\n", Plist.name, LogDir); } if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) { FILE *fp; char buf[BUFSIZ]; snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name); fp = fopen(buf, "r"); if (fp) { putc('\n', stdout); while (fgets(buf, sizeof(buf), fp)) fputs(buf, stdout); putc('\n', stdout); (void) fclose(fp); } else warnx("cannot open %s as display file", buf); } goto success; bomb: code = 1; goto success; fail: /* Nuke the whole (installed) show, XXX but don't clean directories */ if (!Fake) delete_package(FALSE, FALSE, &Plist); success: /* delete the packing list contents */ free_plist(&Plist); leave_playpen(); return code; } static int sanity_check(char *pkg) { int code = 0; if (!fexists(CONTENTS_FNAME)) { warnx("package %s has no CONTENTS file!", pkg); code = 1; } else if (!fexists(COMMENT_FNAME)) { warnx("package %s has no COMMENT file!", pkg); code = 1; } else if (!fexists(DESC_FNAME)) { warnx("package %s has no DESC file!", pkg); code = 1; } return code; } void cleanup(int sig) { static int in_cleanup = 0; if (!in_cleanup) { in_cleanup = 1; if (sig) printf("Signal %d received, cleaning up..\n", sig); if (!Fake && zapLogDir && LogDir[0]) vsystem("%s -rf %s", REMOVE_CMD, LogDir); leave_playpen(); } if (sig) exit(1); } Index: head/usr.sbin/pkg_install/delete/perform.c =================================================================== --- head/usr.sbin/pkg_install/delete/perform.c (revision 131279) +++ head/usr.sbin/pkg_install/delete/perform.c (revision 131280) @@ -1,382 +1,398 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 18 July 1993 * * This is the main body of the delete module. * */ #include __FBSDID("$FreeBSD$"); #include #include "lib.h" #include "delete.h" static int pkg_do(char *); static void sanity_check(char *); static void undepend(char *, char *); static char LogDir[FILENAME_MAX]; int pkg_perform(char **pkgs) { char **matched, **rb, **rbtmp; int errcode, i, j; int err_cnt = 0; struct reqr_by_entry *rb_entry; struct reqr_by_head *rb_list; if (MatchType != MATCH_EXACT) { matched = matchinstalled(MatchType, pkgs, &errcode); if (errcode != 0) return 1; /* Not reached */ /* * Copy matched[] into pkgs[], because we'll need to use * matchinstalled() later on. */ if (matched != NULL) { pkgs = NULL; for (i = 0; matched[i] != NULL; i++) { pkgs = realloc(pkgs, sizeof(*pkgs) * (i + 2)); pkgs[i] = strdup(matched[i]); } pkgs[i] = NULL; } else switch (MatchType) { case MATCH_GLOB: break; case MATCH_ALL: warnx("no packages installed"); return 0; case MATCH_EREGEX: case MATCH_REGEX: warnx("no packages match pattern(s)"); return 1; default: break; } } err_cnt += sortdeps(pkgs); for (i = 0; pkgs[i]; i++) { if (Recursive == TRUE) { errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE); if (errcode < 0) { err_cnt++; } else if (errcode > 0) { /* * Copy values from the rb_list queue into argv-like NULL * terminated list because requiredby() uses some static * storage, while pkg_do() below will call this function, * thus blowing our rb_list away. */ rbtmp = rb = alloca((errcode + 1) * sizeof(*rb)); if (rb == NULL) { warnx("%s(): alloca() failed", __func__); err_cnt++; continue; } STAILQ_FOREACH(rb_entry, rb_list, link) { *rbtmp = alloca(strlen(rb_entry->pkgname) + 1); if (*rbtmp == NULL) { warnx("%s(): alloca() failed", __func__); err_cnt++; continue; } strcpy(*rbtmp, rb_entry->pkgname); rbtmp++; } *rbtmp = NULL; err_cnt += sortdeps(rb); for (j = 0; rb[j]; j++) err_cnt += pkg_do(rb[j]); } } err_cnt += pkg_do(pkgs[i]); } return err_cnt; } static Package Plist; /* This is seriously ugly code following. Written very fast! */ static int pkg_do(char *pkg) { FILE *cfile; char *deporigin, **depnames, home[FILENAME_MAX]; PackingList p; int i, len; + int isinstalled; /* support for separate pre/post install scripts */ int new_m = 0; const char *pre_script = DEINSTALL_FNAME; const char *post_script, *pre_arg, *post_arg; struct reqr_by_entry *rb_entry; struct reqr_by_head *rb_list; if (!pkg || !(len = strlen(pkg))) return 1; if (pkg[len - 1] == '/') pkg[len - 1] = '\0'; /* Reset some state */ if (Plist.head) free_plist(&Plist); - if (!isinstalledpkg(pkg)) { + sprintf(LogDir, "%s/%s", LOG_DIR, pkg); + + isinstalled = isinstalledpkg(pkg); + if (isinstalled == 0) { warnx("no such package '%s' installed", pkg); return 1; + } else if (isinstalled < 0) { + warnx("the package info for package '%s' is corrupt%s", + pkg, Force ? " (but I'll delete it anyway)" : " (use -f to force removal)"); + if (!Force) + return 1; + if (!Fake) { + if (vsystem("%s -rf %s", REMOVE_CMD, LogDir)) { + warnx("couldn't remove log entry in %s, deinstall failed", LogDir); + } else { + warnx("couldn't completely deinstall package '%s',\n" + "only the log entry in %s was removed", pkg, LogDir); + } + } + return 0; } if (!getcwd(home, FILENAME_MAX)) { cleanup(0); errx(2, "%s: unable to get current working directory!", __func__); } - sprintf(LogDir, "%s/%s", LOG_DIR, pkg); - if (chdir(LogDir) == FAIL) { warnx("unable to change directory to %s! deinstall failed", LogDir); return 1; } if (Interactive == TRUE) { int first, ch; (void)fprintf(stderr, "delete %s? ", pkg); (void)fflush(stderr); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (first != 'y' && first != 'Y') return 0; /* Not reached */ } if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0) return 1; if (!STAILQ_EMPTY(rb_list)) { warnx("package '%s' is required by these other packages\n" "and may not be deinstalled%s:", pkg, Force ? " (but I'll delete it anyway)" : ""); STAILQ_FOREACH(rb_entry, rb_list, link) fprintf(stderr, "%s\n", rb_entry->pkgname); if (!Force) return 1; } sanity_check(LogDir); cfile = fopen(CONTENTS_FNAME, "r"); if (!cfile) { warnx("unable to open '%s' file", CONTENTS_FNAME); return 1; } /* If we have a prefix, add it now */ if (Prefix) add_plist(&Plist, PLIST_CWD, Prefix); read_plist(&Plist, cfile); fclose(cfile); p = find_plist(&Plist, PLIST_CWD); if (!p) { warnx("package '%s' doesn't have a prefix", pkg); return 1; } setenv(PKG_PREFIX_VNAME, p->name, 1); if (fexists(REQUIRE_FNAME)) { if (Verbose) printf("Executing 'require' script.\n"); vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) { warnx("package %s fails requirements %s", pkg, Force ? "" : "- not deleted"); if (!Force) return 1; } } /* * Test whether to use the old method of passing tokens to deinstallation * scripts, and set appropriate variables.. */ if (fexists(POST_DEINSTALL_FNAME)) { new_m = 1; post_script = POST_DEINSTALL_FNAME; pre_arg = post_arg = ""; } else if (fexists(DEINSTALL_FNAME)) { post_script = DEINSTALL_FNAME; pre_arg = "DEINSTALL"; post_arg = "POST-DEINSTALL"; } else { post_script = pre_arg = post_arg = NULL; } if (!NoDeInstall && pre_script != NULL && fexists(pre_script)) { if (Fake) printf("Would execute de-install script at this point.\n"); else { vsystem("chmod +x %s", pre_script); /* make sure */ if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) { warnx("deinstall script returned error status"); if (!Force) return 1; } } } + for (p = Plist.head; p ; p = p->next) { + if (p->type != PLIST_PKGDEP) + continue; + deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : + NULL; + if (Verbose) { + printf("Trying to remove dependency on package '%s'", p->name); + if (deporigin != NULL) + printf(" with '%s' origin", deporigin); + printf(".\n"); + } + if (!Fake) { + depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) : + NULL; + if (depnames == NULL) { + depnames = alloca(sizeof(*depnames) * 2); + depnames[0] = p->name; + depnames[1] = NULL; + } + for (i = 0; depnames[i] != NULL; i++) + undepend(depnames[i], pkg); + } + } + if (chdir(home) == FAIL) { cleanup(0); errx(2, "%s: unable to return to working directory %s!", __func__, home); } /* * Some packages aren't packed right, so we need to just ignore * delete_package()'s status. Ugh! :-( */ if (delete_package(FALSE, CleanDirs, &Plist) == FAIL) warnx( "couldn't entirely delete package (perhaps the packing list is\n" "incorrectly specified?)"); if (chdir(LogDir) == FAIL) { warnx("unable to change directory to %s! deinstall failed", LogDir); return 1; } if (!NoDeInstall && post_script != NULL && fexists(post_script)) { if (Fake) printf("Would execute post-deinstall script at this point.\n"); else { vsystem("chmod +x %s", post_script); /* make sure */ if (vsystem("./%s %s %s", post_script, pkg, post_arg)) { warnx("post-deinstall script returned error status"); if (!Force) return 1; } } } if (chdir(home) == FAIL) { cleanup(0); errx(2, "%s: unable to return to working directory %s!", __func__, home); } if (!Fake) { if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) { warnx("couldn't remove log entry in %s, deinstall failed", LogDir); if (!Force) return 1; - } - } - - for (p = Plist.head; p ; p = p->next) { - if (p->type != PLIST_PKGDEP) - continue; - deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : - NULL; - if (Verbose) { - printf("Trying to remove dependency on package '%s'", p->name); - if (deporigin != NULL) - printf(" with '%s' origin", deporigin); - printf(".\n"); - } - if (!Fake) { - depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) : - NULL; - if (depnames == NULL) { - depnames = alloca(sizeof(*depnames) * 2); - depnames[0] = p->name; - depnames[1] = NULL; - } - for (i = 0; depnames[i] != NULL; i++) - undepend(depnames[i], pkg); } } return 0; } static void sanity_check(char *pkg) { if (!fexists(CONTENTS_FNAME)) { cleanup(0); errx(2, "%s: installed package %s has no %s file!", __func__, pkg, CONTENTS_FNAME); } } void cleanup(int sig) { if (sig) exit(1); } static void undepend(char *p, char *pkgname) { char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; FILE *fpwr; int s; struct reqr_by_entry *rb_entry; struct reqr_by_head *rb_list; if (requiredby(p, &rb_list, Verbose, FALSE) <= 0) return; snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p, REQUIRED_BY_FNAME); snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname); s = mkstemp(ftmp); if (s == -1) { warnx("couldn't open temp file '%s'", ftmp); return; } fpwr = fdopen(s, "w"); if (fpwr == NULL) { close(s); warnx("couldn't fdopen temp file '%s'", ftmp); goto cleanexit; } STAILQ_FOREACH(rb_entry, rb_list, link) if (strcmp(rb_entry->pkgname, pkgname)) /* no match */ fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr); if (fchmod(s, 0644) == FAIL) { warnx("error changing permission of temp file '%s'", ftmp); fclose(fpwr); goto cleanexit; } if (fclose(fpwr) == EOF) { warnx("error closing temp file '%s'", ftmp); goto cleanexit; } if (rename(ftmp, fname) == -1) warnx("error renaming '%s' to '%s'", ftmp, fname); cleanexit: remove(ftmp); return; } Index: head/usr.sbin/pkg_install/info/main.c =================================================================== --- head/usr.sbin/pkg_install/info/main.c (revision 131279) +++ head/usr.sbin/pkg_install/info/main.c (revision 131280) @@ -1,267 +1,271 @@ /* * * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 18 July 1993 * * This is the info module. * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include "info.h" #include -static char Options[] = "abcdDe:EfgGhiIkl:LmoO:pPqQrRst:vVW:xX"; +static char Options[] = "abcdDe:EfgGhiIjkl:LmoO:pPqQrRst:vVW:xX"; int Flags = 0; match_t MatchType = MATCH_GLOB; Boolean Quiet = FALSE; Boolean QUIET = FALSE; Boolean UseBlkSz = FALSE; char *InfoPrefix = (char *)(uintptr_t)""; char PlayPen[FILENAME_MAX]; char *CheckPkg = NULL; char *LookUpOrigin = NULL; struct which_head *whead; static void usage __P((void)); int main(int argc, char **argv) { int ch; char **pkgs, **start; char *pkgs_split; whead = malloc(sizeof(struct which_head)); if (whead == NULL) err(2, NULL); TAILQ_INIT(whead); pkgs = start = argv; if (argc == 1) { MatchType = MATCH_ALL; Flags = SHOW_INDEX; } else while ((ch = getopt(argc, argv, Options)) != -1) { switch(ch) { case 'a': MatchType = MATCH_ALL; break; case 'b': UseBlkSz = TRUE; break; case 'v': Verbose = TRUE; /* Reasonable definition of 'everything' */ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL | SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE; break; case 'E': Flags |= SHOW_PKGNAME; break; case 'I': Flags |= SHOW_INDEX; break; case 'p': Flags |= SHOW_PREFIX; break; case 'c': Flags |= SHOW_COMMENT; break; case 'd': Flags |= SHOW_DESC; break; case 'D': Flags |= SHOW_DISPLAY; break; case 'f': Flags |= SHOW_PLIST; break; case 'g': Flags |= SHOW_CKSUM; break; case 'G': MatchType = MATCH_EXACT; break; case 'i': Flags |= SHOW_INSTALL; break; + case 'j': + Flags |= SHOW_REQUIRE; + break; + case 'k': Flags |= SHOW_DEINSTALL; break; case 'r': - Flags |= SHOW_REQUIRE; + Flags |= SHOW_DEPEND; break; case 'R': Flags |= SHOW_REQBY; break; case 'L': Flags |= SHOW_FILES; break; case 'm': Flags |= SHOW_MTREE; break; case 's': Flags |= SHOW_SIZE; break; case 'o': Flags |= SHOW_ORIGIN; break; case 'O': LookUpOrigin = strdup(optarg); if (LookUpOrigin == NULL) err(2, NULL); break; case 'V': Flags |= SHOW_FMTREV; break; case 'l': InfoPrefix = optarg; break; case 'q': Quiet = TRUE; break; case 'Q': Quiet = TRUE; QUIET = TRUE; break; case 't': strlcpy(PlayPen, optarg, sizeof(PlayPen)); break; case 'x': MatchType = MATCH_REGEX; break; case 'X': MatchType = MATCH_EREGEX; break; case 'e': CheckPkg = optarg; break; case 'W': { struct which_entry *entp; entp = calloc(1, sizeof(struct which_entry)); if (entp == NULL) err(2, NULL); strlcpy(entp->file, optarg, PATH_MAX); entp->skip = FALSE; TAILQ_INSERT_TAIL(whead, entp, next); break; } case 'P': Flags = SHOW_PTREV; break; case 'h': case '?': default: usage(); break; } } argc -= optind; argv += optind; if (Flags & SHOW_PTREV) { if (!Quiet) printf("Package tools revision: "); printf("%d\n", PKG_INSTALL_VERSION); exit(0); } /* Set some reasonable defaults */ if (!Flags) Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY; /* Get all the remaining package names, if any */ while (*argv) { /* * Don't try to apply heuristics if arguments are regexs or if * the argument refers to an existing file. */ if (MatchType != MATCH_REGEX && MatchType != MATCH_EREGEX && !isfile(*argv)) while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) { *pkgs_split++ = '\0'; /* * If character after the '/' is alphanumeric or shell * metachar, then we've found the package name. Otherwise * we've come across a trailing '/' and need to continue our * quest. */ if (isalpha(*pkgs_split) || ((MatchType == MATCH_GLOB) && \ strpbrk(pkgs_split, "*?[]") != NULL)) { *argv = pkgs_split; break; } } *pkgs++ = *argv++; } /* If no packages, yelp */ if (pkgs == start && MatchType != MATCH_ALL && !CheckPkg && TAILQ_EMPTY(whead) && LookUpOrigin == NULL) warnx("missing package name(s)"), usage(); *pkgs = NULL; return pkg_perform(start); } static void usage() { fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", - "usage: pkg_info [-bcdDEfgGiIjLmopPqQrRsvVxX] [-e package] [-l prefix]", + "usage: pkg_info [-bcdDEfgGiIjkLmopPqQrRsvVxX] [-e package] [-l prefix]", " [-t template] -a | pkg-name ...", " pkg_info [-qQ] -W filename", " pkg_info [-qQ] -O origin", " pkg_info"); exit(1); } Index: head/usr.sbin/pkg_install/info/perform.c =================================================================== --- head/usr.sbin/pkg_install/info/perform.c (revision 131279) +++ head/usr.sbin/pkg_install/info/perform.c (revision 131280) @@ -1,481 +1,473 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 23 Aug 1993 * * This is the main body of the info module. * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include "info.h" #include #include static int pkg_do(char *); static int find_pkg(struct which_head *); static int cmp_path(const char *, const char *, const char *); static char *abspath(const char *); static int find_pkgs_by_origin(const char *); static int matched_packages(char **pkgs); int pkg_perform(char **pkgs) { char **matched; int err_cnt = 0; int i, errcode; signal(SIGINT, cleanup); /* Overriding action? */ if (Flags & SHOW_PKGNAME) { return matched_packages(pkgs); } else if (CheckPkg) { - return isinstalledpkg(CheckPkg) == TRUE ? 0 : 1; + return isinstalledpkg(CheckPkg) > 0 ? 0 : 1; /* Not reached */ } else if (!TAILQ_EMPTY(whead)) { return find_pkg(whead); } else if (LookUpOrigin != NULL) { return find_pkgs_by_origin(LookUpOrigin); } if (MatchType != MATCH_EXACT) { matched = matchinstalled(MatchType, pkgs, &errcode); if (errcode != 0) return 1; /* Not reached */ if (matched != NULL) pkgs = matched; else switch (MatchType) { case MATCH_GLOB: break; case MATCH_ALL: warnx("no packages installed"); return 0; /* Not reached */ case MATCH_REGEX: case MATCH_EREGEX: warnx("no packages match pattern(s)"); return 1; /* Not reached */ default: break; } } for (i = 0; pkgs[i]; i++) err_cnt += pkg_do(pkgs[i]); return err_cnt; } static char *Home; static int pkg_do(char *pkg) { Boolean installed = FALSE, isTMP = FALSE; char log_dir[FILENAME_MAX]; - char fname[FILENAME_MAX], extrlist[FILENAME_MAX]; + char fname[FILENAME_MAX]; Package plist; FILE *fp; struct stat sb; char *cp = NULL; int code = 0; if (isURL(pkg)) { if ((cp = fileGetURL(NULL, pkg)) != NULL) { strcpy(fname, cp); isTMP = TRUE; } } else if (fexists(pkg) && isfile(pkg)) { int len; if (*pkg != '/') { if (!getcwd(fname, FILENAME_MAX)) upchuck("getcwd"); len = strlen(fname); snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg); } else strcpy(fname, pkg); cp = fname; } else { if ((cp = fileFindByPath(NULL, pkg)) != NULL) strncpy(fname, cp, FILENAME_MAX); } if (cp) { /* * Apply a crude heuristic to see how much space the package will * take up once it's unpacked. I've noticed that most packages * compress an average of 75%, but we're only unpacking the + files so * be very optimistic. */ if (stat(fname, &sb) == FAIL) { warnx("can't stat package file '%s'", fname); code = 1; goto bail; } Home = make_playpen(PlayPen, sb.st_size / 2); - snprintf(extrlist, sizeof(extrlist), "--fast-read %s %s %s", - CONTENTS_FNAME, COMMENT_FNAME, DESC_FNAME); - if (Flags & SHOW_DISPLAY) - snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist, - DISPLAY_FNAME); - if (Flags & SHOW_INSTALL) - snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist, - INSTALL_FNAME, POST_INSTALL_FNAME); - if (Flags & SHOW_DEINSTALL) - snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist, - DEINSTALL_FNAME, POST_DEINSTALL_FNAME); - if (Flags & SHOW_MTREE) - snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist, - MTREE_FNAME); - if (unpack(fname, extrlist)) { + if (unpack(fname, "'+*'")) { warnx("error during unpacking, no info for '%s' available", pkg); code = 1; goto bail; } } - /* It's not an ininstalled package, try and find it among the installed */ + /* It's not an uninstalled package, try and find it among the installed */ else { - if (!isinstalledpkg(pkg)) { + int isinstalled = isinstalledpkg(pkg); + if (isinstalled < 0) { + warnx("the package info for package '%s' is corrupt", pkg); + return 1; + } else if (isinstalled == 0) { warnx("can't find package '%s' installed or in a file!", pkg); return 1; } sprintf(log_dir, "%s/%s", LOG_DIR, pkg); if (chdir(log_dir) == FAIL) { warnx("can't change directory to '%s'!", log_dir); return 1; } installed = TRUE; } /* Suck in the contents list */ plist.head = plist.tail = NULL; fp = fopen(CONTENTS_FNAME, "r"); if (!fp) { warnx("unable to open %s file", CONTENTS_FNAME); code = 1; goto bail; } /* If we have a prefix, add it now */ read_plist(&plist, fp); fclose(fp); /* * Index is special info type that has to override all others to make * any sense. */ if (Flags & SHOW_INDEX) { char tmp[FILENAME_MAX]; snprintf(tmp, FILENAME_MAX, "%-19s ", pkg); show_index(tmp, COMMENT_FNAME); } else { /* Start showing the package contents */ if (!Quiet) printf("%sInformation for %s:\n\n", InfoPrefix, pkg); else if (QUIET) printf("%s%s:", InfoPrefix, pkg); if (Flags & SHOW_COMMENT) show_file("Comment:\n", COMMENT_FNAME); - if (Flags & SHOW_REQUIRE) + if (Flags & SHOW_DEPEND) show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE); if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME)) show_file("Required by:\n", REQUIRED_BY_FNAME); if (Flags & SHOW_DESC) show_file("Description:\n", DESC_FNAME); if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME)) show_file("Install notice:\n", DISPLAY_FNAME); if (Flags & SHOW_PLIST) show_plist("Packing list:\n", &plist, (plist_t)0, TRUE); + if (Flags & SHOW_REQUIRE && fexists(REQUIRE_FNAME)) + show_file("Requirements script:\n", REQUIRE_FNAME); if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME)) show_file("Install script:\n", INSTALL_FNAME); if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME)) show_file("Post-Install script:\n", POST_INSTALL_FNAME); if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME)) show_file("De-Install script:\n", DEINSTALL_FNAME); if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME)) show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME); if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME)) show_file("mtree file:\n", MTREE_FNAME); if (Flags & SHOW_PREFIX) show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE); if (Flags & SHOW_FILES) show_files("Files:\n", &plist); if ((Flags & SHOW_SIZE) && installed) show_size("Package Size:\n", &plist); if ((Flags & SHOW_CKSUM) && installed) show_cksum("Mismatched Checksums:\n", &plist); if (Flags & SHOW_ORIGIN) show_origin("Origin:\n", &plist); if (Flags & SHOW_FMTREV) show_fmtrev("Packing list format revision:\n", &plist); if (!Quiet) puts(InfoPrefix); } free_plist(&plist); bail: leave_playpen(); if (isTMP) unlink(fname); return code; } void cleanup(int sig) { static int in_cleanup = 0; if (!in_cleanup) { in_cleanup = 1; leave_playpen(); } if (sig) exit(1); } /* * Return an absolute path, additionally removing all .'s, ..'s, and extraneous * /'s, as realpath() would, but without resolving symlinks, because that can * potentially screw up our comparisons later. */ static char * abspath(const char *pathname) { char *tmp, *tmp1, *resolved_path; char *cwd = NULL; int len; if (pathname[0] != '/') { cwd = getcwd(NULL, MAXPATHLEN); asprintf(&resolved_path, "%s/%s/", cwd, pathname); } else asprintf(&resolved_path, "%s/", pathname); if (resolved_path == NULL) errx(2, NULL); if (cwd != NULL) free(cwd); while ((tmp = strstr(resolved_path, "//")) != NULL) strcpy(tmp, tmp + 1); while ((tmp = strstr(resolved_path, "/./")) != NULL) strcpy(tmp, tmp + 2); while ((tmp = strstr(resolved_path, "/../")) != NULL) { *tmp = '\0'; if ((tmp1 = strrchr(resolved_path, '/')) == NULL) tmp1 = resolved_path; strcpy(tmp1, tmp + 3); } len = strlen(resolved_path); if (len > 1 && resolved_path[len - 1] == '/') resolved_path[len - 1] = '\0'; return resolved_path; } /* * Comparison to see if the path we're on matches the * one we are looking for. */ static int cmp_path(const char *target, const char *current, const char *cwd) { char *resolved, *temp; int rval; asprintf(&temp, "%s/%s", cwd, current); if (temp == NULL) errx(2, NULL); /* * Make sure there's no multiple /'s or other weird things in the PLIST, * since some plists seem to have them and it could screw up our strncmp. */ resolved = abspath(temp); if (strcmp(target, resolved) == 0) rval = 1; else rval = 0; free(temp); free(resolved); return rval; } /* * Look through package dbs in LOG_DIR and find which * packages installed the files in which_list. */ static int find_pkg(struct which_head *which_list) { char **installed; int errcode, i; struct which_entry *wp; TAILQ_FOREACH(wp, which_list, next) { const char *msg = "file cannot be found"; char *tmp; wp->skip = TRUE; /* If it's not a file, we'll see if it's an executable. */ if (isfile(wp->file) == FALSE) { if (strchr(wp->file, '/') == NULL) { tmp = vpipe("/usr/bin/which %s", wp->file); if (tmp != NULL) { strlcpy(wp->file, tmp, PATH_MAX); wp->skip = FALSE; free(tmp); } else msg = "file is not in PATH"; } } else { tmp = abspath(wp->file); if (isfile(tmp)) { strlcpy(wp->file, tmp, PATH_MAX); wp->skip = FALSE; } free(tmp); } if (wp->skip == TRUE) warnx("%s: %s", wp->file, msg); } installed = matchinstalled(MATCH_ALL, NULL, &errcode); if (installed == NULL) return errcode; for (i = 0; installed[i] != NULL; i++) { FILE *fp; Package pkg; PackingList itr; char *cwd = NULL; char tmp[PATH_MAX]; snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i], CONTENTS_FNAME); fp = fopen(tmp, "r"); if (fp == NULL) { warn("%s", tmp); return 1; } pkg.head = pkg.tail = NULL; read_plist(&pkg, fp); fclose(fp); for (itr = pkg.head; itr != pkg.tail; itr = itr->next) { if (itr->type == PLIST_CWD) { cwd = itr->name; } else if (itr->type == PLIST_FILE) { TAILQ_FOREACH(wp, which_list, next) { if (wp->skip == TRUE) continue; if (!cmp_path(wp->file, itr->name, cwd)) continue; if (wp->package[0] != '\0') { warnx("both %s and %s claim to have installed %s\n", wp->package, installed[i], wp->file); } else { strlcpy(wp->package, installed[i], PATH_MAX); } } } } free_plist(&pkg); } TAILQ_FOREACH(wp, which_list, next) { if (wp->package[0] != '\0') { if (Quiet) puts(wp->package); else printf("%s was installed by package %s\n", \ wp->file, wp->package); } } while (!TAILQ_EMPTY(which_list)) { wp = TAILQ_FIRST(which_list); TAILQ_REMOVE(which_list, wp, next); free(wp); } free(which_list); return 0; } /* * Look through package dbs in LOG_DIR and find which * packages have the given origin. Don't use read_plist() * because this increases time necessary for lookup by 40 * times, as we don't really have to parse all plist to * get origin. */ static int find_pkgs_by_origin(const char *origin) { char **matched; int errcode, i; if (!Quiet) printf("The following installed package(s) has %s origin:\n", origin); matched = matchbyorigin(origin, &errcode); if (matched == NULL) return errcode; for (i = 0; matched[i] != NULL; i++) puts(matched[i]); return 0; } /* * List only the matching package names. * Mainly intended for scripts. */ static int matched_packages(char **pkgs) { char **matched; int i, errcode; matched = matchinstalled(MatchType == MATCH_GLOB ? MATCH_NGLOB : MatchType, pkgs, &errcode); if (errcode != 0 || matched == NULL) return 1; for (i = 0; matched[i]; i++) if (!Quiet) printf("%s\n", matched[i]); else if (QUIET) printf("%s%s\n", InfoPrefix, matched[i]); return 0; } Index: head/usr.sbin/pkg_install/info/pkg_info.1 =================================================================== --- head/usr.sbin/pkg_install/info/pkg_info.1 (revision 131279) +++ head/usr.sbin/pkg_install/info/pkg_info.1 (revision 131280) @@ -1,261 +1,263 @@ .\" .\" FreeBSD install - a package for the installation and maintenance .\" of non-core utilities. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" Jordan K. Hubbard .\" .\" .\" @(#)pkg_info.1 .\" $FreeBSD$ .\" -.Dd February 8, 2001 +.Dd June 29, 2004 .Dt PKG_INFO 1 .Os .Sh NAME .Nm pkg_info .Nd a utility for displaying information on software packages .Sh SYNOPSIS .Nm -.Op Fl bcdDEfgGiIkLmopPqQrRsvVxX +.Op Fl bcdDEfgGijIkLmopPqQrRsvVxX .Op Fl e Ar package .Op Fl l Ar prefix .Op Fl t Ar template .Fl a | Ar pkg-name ... .Nm .Op Fl qQ .Fl W Ar filename .Nm .Op Fl qQ .Fl O Ar origin .Nm .Sh DESCRIPTION The .Nm command is used to dump out information for packages, either packed up in files with the .Xr pkg_create 1 command or already installed on the system with the .Xr pkg_add 1 command. .Sh OPTIONS The following command line options are supported: .Bl -tag -width indent .It Ar pkg-name ... The named packages are described. A package name may either be the name of an installed package, the pathname to a package distribution file or a URL to an FTP available package. Package version numbers can also be matched in a relational manner using the .Pa \*[Ge], \*[Le], \*[Gt] and .Pa \*[Lt] operators. For example, .Pa pkg_info 'portupgrade\*[Ge]20030723' will match versions 20030723 and later of the .Pa portupgrade package. .It Fl a Show all currently installed packages. .It Fl b Use the BLOCKSIZE environment variable for output even when the .Fl q or .Fl Q flag is present. .It Fl v Turn on verbose output. .It Fl p Show the installation prefix for each package. .It Fl q Be .Dq quiet in emitting report headers and such, just dump the raw info (basically, assume a non-human reading). .It Fl Q Be .Dq quiet as above but print preface output with the package name. .It Fl c Show the (one line) comment field for each package. .It Fl d Show the long description field for each package. .It Fl D Show the install-message file for each package. .It Fl f Show the packing list instructions for each package. .It Fl g Show files that don't match the recorded checksum. .It Fl i Show the install script (if any) for each package. .It Fl I Show an index line for each package. This option takes precedence over all other package formatting options. +.It Fl j +Show the requirements script (if any) for each package. .It Fl k Show the de-install script (if any) for each package. .It Fl r -Show the requirements script (if any) for each package. +Show the list of packages on which each package depends. .It Fl R Show the list of installed packages which require each package. .It Fl m Show the mtree file (if any) for each package. .It Fl L Show the files within each package. This is different from just viewing the packing list, since full pathnames for everything are generated. .It Fl s Show the total size occupied by files installed within each package. .It Fl o Show the .Dq origin path recorded on package generation. This path intended to give an idea as to where the underlying port, from which package was generated, is located in the .Fx .Em "Ports Collection" . .It Fl G Do not try to expand shell glob patterns in the .Ar pkg-name when selecting packages to be displayed (by default .Nm automatically expands shell glob patterns in the .Ar pkg-name ) . .It Fl W For the specified .Ar filename argument show which package it belongs to. If the file is not in the current directory, and does not have an absolute path, then the .Ev PATH is searched using .Xr which 1 . .It Fl O For the specified .Ar origin argument list all packages having this origin. .It Fl x Treat the .Ar pkg-name as a regular expression and display information only for packages whose names match that regular expression. Multiple regular expressions could be provided, in that case .Nm displays information about all packages that match at least one regular expression from the list. .It Fl X Like .Fl x , but treats the .Ar pkg-name as an extended regular expression. .It Fl e Ar pkg-name If the package identified by .Ar pkg-name is currently installed, return 0, otherwise return 1. This option allows you to easily test for the presence of another (perhaps prerequisite) package from a script. .It Fl E Show only matching package names. This option takes precedence over all other package formatting options. If any packages match, return 0, otherwise return 1. .It Fl l Ar str Prefix each information category header (see .Fl q ) shown with .Ar str . This is primarily of use to front-end programs who want to request a lot of different information fields at once for a package, but don't necessary want the output intermingled in such a way that they can't organize it. This lets you add a special token to the start of each field. .It Fl t Ar template Use .Ar template as the input to .Xr mktemp 3 when creating a .Dq staging area . By default, this is the string .Pa /tmp/instmp.XXXXXX , but it may be necessary to override it in the situation where space in your .Pa /tmp directory is limited. Be sure to leave some number of `X' characters for .Xr mktemp 3 to fill in with a unique ID. .Bd -ragged -offset indent -compact Note: This should really not be necessary with .Nm , since very little information is extracted from each package and one would have to have a very small .Pa /tmp indeed to overflow it. .Ed .It Fl V Show revision number of the packing list format. .It Fl P Show revision number of package tools. .El .Sh TECHNICAL DETAILS Package info is either extracted from package files named on the command line, or from already installed package information in .Pa /var/db/pkg/ Ns Aq Ar pkg-name . .Sh ENVIRONMENT .Bl -tag -width PKG_TMPDIR .It Ev BLOCKSIZE If the environment variable .Ev BLOCKSIZE is set the block counts will be displayed in units of that size block. .It Ev PKG_TMPDIR Points to the directory where .Nm creates its temporary files. If this variable is not set, .Ev TMPDIR is used. If both are unset, the builtin defaults are used. .It Ev PKG_DBDIR Specifies an alternative location for the installed package database. .El .Sh FILES .Bl -tag -width /var/db/pkg -compact .It Pa /var/tmp Used if the environment variables .Ev PKG_TMPDIR and .Ev TMPDIR are not set, or if the directories named have insufficient space. .It Pa /tmp The next choice if .Pa /var/tmp does not exist or has insufficient space. .It Pa /usr/tmp The last choice if .Pa /tmp is unsuitable. .It Pa /var/db/pkg Default location of the installed package database. .El .Sh SEE ALSO .Xr pkg_add 1 , .Xr pkg_create 1 , .Xr pkg_delete 1 , .Xr pkg_version 1 , .Xr mktemp 3 , .Xr mtree 8 .Sh AUTHORS .An Jordan Hubbard .Sh CONTRIBUTORS .An John Kohl Aq jtk@rational.com , .An Oliver Eikemeier Aq eik@FreeBSD.org .Sh BUGS Sure to be some. Index: head/usr.sbin/pkg_install/lib/deps.c =================================================================== --- head/usr.sbin/pkg_install/lib/deps.c (revision 131279) +++ head/usr.sbin/pkg_install/lib/deps.c (revision 131280) @@ -1,195 +1,195 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Maxim Sobolev * 14 March 2001 * * Routines used to do various operations with dependencies * among installed packages. * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include #include /* * Sort given NULL-terminated list of installed packages (pkgs) in * such a way that if package A depends on package B then after * sorting A will be listed before B no matter how they were * originally positioned in the list. */ int sortdeps(char **pkgs) { char *tmp; int i, j, loop_cnt; int err_cnt = 0; if (pkgs[0] == NULL || pkgs[1] == NULL) return (0); for (i = 0; pkgs[i + 1]; i++) { /* * Check to see if any other package in pkgs[i+1:] depends * on pkgs[i] and swap those two packages if so. */ loop_cnt = 0; for (j = i + 1; pkgs[j]; j++) { if (chkifdepends(pkgs[j], pkgs[i]) == 1) { /* * Try to avoid deadlock if package A depends on B which in * turn depends on C and C due to an error depends on A. * Use ugly but simple method, becase it Should Never * Happen[tm] in the real life anyway. */ if (loop_cnt > 4096) { warnx("dependency loop detected for package %s", pkgs[j]); err_cnt++; break; } loop_cnt++; tmp = pkgs[i]; pkgs[i] = pkgs[j]; pkgs[j] = tmp; /* * Another iteration requred to check if new pkgs[i] * itself has any packages that depend on it */ j = i + 1; } } } return err_cnt; } /* * Check to see if pkgname1 depends on pkgname2. * Returns 1 if depends, 0 if not, and -1 if error occured. */ int chkifdepends(const char *pkgname1, const char *pkgname2) { char *cp1, *cp2; int errcode; struct reqr_by_entry *rb_entry; struct reqr_by_head *rb_list; cp2 = strchr(pkgname2, ':'); if (cp2 != NULL) *cp2 = '\0'; cp1 = strchr(pkgname1, ':'); if (cp1 != NULL) *cp1 = '\0'; errcode = 0; /* Check that pkgname2 is actually installed */ - if (!isinstalledpkg(pkgname2)) + if (isinstalledpkg(pkgname2) <= 0) goto exit; errcode = requiredby(pkgname2, &rb_list, FALSE, TRUE); if (errcode < 0) goto exit; errcode = 0; STAILQ_FOREACH(rb_entry, rb_list, link) { if (strcmp(rb_entry->pkgname, pkgname1) == 0) { /* match */ errcode = 1; break; } } exit: if (cp1 != NULL) *cp1 = ':'; if (cp2 != NULL) *cp2 = ':'; return errcode; } /* * Load +REQUIRED_BY file and return a list with names of * packages that require package reffered to by `pkgname'. * * Optionally check that packages listed there are actually * installed and filter out those that don't (filter == TRUE). * * strict argument controls whether the caller want warnings * to be emitted when there are some non-fatal conditions, * i.e. package doesn't have +REQUIRED_BY file or some packages * listed in +REQUIRED_BY don't exist. * * Result returned in the **list, while return value is equal * to the number of entries in the resulting list. Print error * message and return -1 on error. */ int requiredby(const char *pkgname, struct reqr_by_head **list, Boolean strict, Boolean filter) { FILE *fp; char fbuf[FILENAME_MAX], fname[FILENAME_MAX]; int retval; struct reqr_by_entry *rb_entry; static struct reqr_by_head rb_list = STAILQ_HEAD_INITIALIZER(rb_list); *list = &rb_list; /* Deallocate any previously allocated space */ while (!STAILQ_EMPTY(&rb_list)) { rb_entry = STAILQ_FIRST(&rb_list); STAILQ_REMOVE_HEAD(&rb_list, link); free(rb_entry); } - if (!isinstalledpkg(pkgname)) { + if (isinstalledpkg(pkgname) <= 0) { if (strict == TRUE) warnx("no such package '%s' installed", pkgname); return -1; } snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, pkgname, REQUIRED_BY_FNAME); fp = fopen(fname, "r"); if (fp == NULL) { /* Probably pkgname doesn't have any packages that depend on it */ if (strict == TRUE) warnx("couldn't open dependency file '%s'", fname); return 0; } retval = 0; while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { if (fbuf[strlen(fbuf) - 1] == '\n') fbuf[strlen(fbuf) - 1] = '\0'; - if (filter == TRUE && !isinstalledpkg(fbuf)) { + if (filter == TRUE && isinstalledpkg(fbuf) <= 0) { if (strict == TRUE) warnx("package '%s' is recorded in the '%s' but isn't " "actually installed", fbuf, fname); continue; } retval++; rb_entry = malloc(sizeof(*rb_entry)); if (rb_entry == NULL) { warnx("%s(): malloc() failed", __func__); retval = -1; break; } strlcpy(rb_entry->pkgname, fbuf, sizeof(rb_entry->pkgname)); STAILQ_INSERT_TAIL(&rb_list, rb_entry, link); } fclose(fp); return retval; } Index: head/usr.sbin/pkg_install/lib/file.c =================================================================== --- head/usr.sbin/pkg_install/lib/file.c (revision 131279) +++ head/usr.sbin/pkg_install/lib/file.c (revision 131280) @@ -1,426 +1,428 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 18 July 1993 * * Miscellaneous file access utilities. * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include #include #include #include /* Quick check to see if a file exists */ Boolean fexists(const char *fname) { struct stat dummy; if (!lstat(fname, &dummy)) return TRUE; return FALSE; } /* Quick check to see if something is a directory or symlink to a directory */ Boolean isdir(const char *fname) { struct stat sb; if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) return TRUE; else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode)) return TRUE; else return FALSE; } /* Check to see if file is a dir or symlink to a dir, and is empty */ Boolean isemptydir(const char *fname) { if (isdir(fname)) { DIR *dirp; struct dirent *dp; dirp = opendir(fname); if (!dirp) return FALSE; /* no perms, leave it alone */ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { closedir(dirp); return FALSE; } } (void)closedir(dirp); return TRUE; } return FALSE; } /* * Returns TRUE if file is a regular file or symlink pointing to a regular * file */ Boolean isfile(const char *fname) { struct stat sb; if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) return TRUE; return FALSE; } /* * Check to see if file is a file or symlink pointing to a file and is empty. * If nonexistent or not a file, say "it's empty", otherwise return TRUE if * zero sized. */ Boolean isemptyfile(const char *fname) { struct stat sb; if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) { if (sb.st_size != 0) return FALSE; } return TRUE; } /* Returns TRUE if file is a symbolic link. */ Boolean issymlink(const char *fname) { struct stat sb; if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) return TRUE; return FALSE; } /* Returns TRUE if file is a URL specification */ Boolean isURL(const char *fname) { /* * I'm sure there are other types of URL specifications that I could * also be looking for here, but for now I'll just be happy to get ftp * and http working. */ if (!fname) return FALSE; while (isspace(*fname)) ++fname; - if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7)) + if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7) || + !strncmp(fname, "https://", 8) || !strncmp(fname, "file://", 7)) return TRUE; return FALSE; } char * fileFindByPath(const char *base, const char *fname) { static char tmp[FILENAME_MAX]; char *cp; const char *suffixes[] = {".tbz", ".tgz", ".tar", NULL}; int i; if (fexists(fname) && isfile(fname)) { strcpy(tmp, fname); return tmp; } if (base) { strcpy(tmp, base); cp = strrchr(tmp, '/'); if (cp) { *cp = '\0'; /* chop name */ cp = strrchr(tmp, '/'); } if (cp) for (i = 0; suffixes[i] != NULL; i++) { *(cp + 1) = '\0'; strcat(cp, "All/"); strcat(cp, fname); strcat(cp, suffixes[i]); if (fexists(tmp)) return tmp; } } cp = getenv("PKG_PATH"); while (cp) { char *cp2 = strsep(&cp, ":"); for (i = 0; suffixes[i] != NULL; i++) { snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]); if (fexists(tmp) && isfile(tmp)) return tmp; } } return NULL; } char * fileGetContents(const char *fname) { char *contents; struct stat sb; int fd; if (stat(fname, &sb) == FAIL) { cleanup(0); errx(2, "%s: can't stat '%s'", __func__, fname); } contents = (char *)malloc(sb.st_size + 1); fd = open(fname, O_RDONLY, 0); if (fd == FAIL) { cleanup(0); errx(2, "%s: unable to open '%s' for reading", __func__, fname); } if (read(fd, contents, sb.st_size) != sb.st_size) { cleanup(0); errx(2, "%s: short read on '%s' - did not get %qd bytes", __func__, fname, (long long)sb.st_size); } close(fd); contents[sb.st_size] = '\0'; return contents; } /* * Takes a filename and package name, returning (in "try") the * canonical "preserve" name for it. */ Boolean make_preserve_name(char *try, int max, const char *name, const char *file) { int len, i; if ((len = strlen(file)) == 0) return FALSE; else i = len - 1; strncpy(try, file, max); if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */ --i; for (; i; i--) { if (try[i] == '/') { try[i + 1]= '.'; strncpy(&try[i + 2], &file[i + 1], max - i - 2); break; } } if (!i) { try[0] = '.'; strncpy(try + 1, file, max - 1); } /* I should probably be called rude names for these inline assignments */ strncat(try, ".", max -= strlen(try)); strncat(try, name, max -= strlen(name)); strncat(try, ".", max--); strncat(try, "backup", max -= 6); return TRUE; } /* Write the contents of "str" to a file */ void write_file(const char *name, const char *str) { FILE *fp; size_t len; fp = fopen(name, "w"); if (!fp) { cleanup(0); errx(2, "%s: cannot fopen '%s' for writing", __func__, name); } len = strlen(str); if (fwrite(str, 1, len, fp) != len) { cleanup(0); errx(2, "%s: short fwrite on '%s', tried to write %ld bytes", __func__, name, (long)len); } if (fclose(fp)) { cleanup(0); errx(2, "%s: failure to fclose '%s'", __func__, name); } } void copy_file(const char *dir, const char *fname, const char *to) { char cmd[FILENAME_MAX]; if (fname[0] == '/') snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to); else snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to); if (vsystem(cmd)) { cleanup(0); errx(2, "%s: could not perform '%s'", __func__, cmd); } } void move_file(const char *dir, const char *fname, const char *to) { char cmd[FILENAME_MAX]; if (fname[0] == '/') snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to); else snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to); if (vsystem(cmd)) { cleanup(0); errx(2, "%s: could not perform '%s'", __func__, cmd); } } /* * Copy a hierarchy (possibly from dir) to the current directory, or * if "to" is TRUE, from the current directory to a location someplace * else. * * Though slower, using tar to copy preserves symlinks and everything * without me having to write some big hairy routine to do it. */ void copy_hierarchy(const char *dir, const char *fname, Boolean to) { char cmd[FILENAME_MAX * 3]; if (!to) { /* If absolute path, use it */ if (*fname == '/') dir = "/"; snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -", dir, fname); } else snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s", fname, dir); #ifdef DEBUG printf("Using '%s' to copy trees.\n", cmd); #endif if (system(cmd)) { cleanup(0); errx(2, "%s: could not perform '%s'", __func__, cmd); } } /* Unpack a tar file */ int unpack(const char *pkg, const char *flist) { - char *comp, suff[80], *cp; + const char *comp, *cp; + char suff[80]; comp = ""; /* * Figure out by a crude heuristic whether this or not this is probably * compressed and whichever compression utility was used (gzip or bzip2). */ if (strcmp(pkg, "-")) { cp = strrchr(pkg, '.'); if (cp) { strcpy(suff, cp + 1); if (strchr(suff, 'z') || strchr(suff, 'Z')) { if (strchr(suff, 'b')) comp = "-j"; else comp = "-z"; } } } else /* XXX: need to handle .tgz also */ comp = "-j"; if (vsystem("tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) { warnx("tar extract of %s failed!", pkg); return 1; } return 0; } /* * Using fmt, replace all instances of: * * %F With the parameter "name" * %D With the parameter "dir" * %B Return the directory part ("base") of %D/%F * %f Return the filename part of %D/%F * * Does not check for overflow - caution! * */ void format_cmd(char *buf, int max, const char *fmt, const char *dir, const char *name) { char *cp, scratch[FILENAME_MAX * 2]; int l; while (*fmt && max > 0) { if (*fmt == '%') { switch (*++fmt) { case 'F': strncpy(buf, name, max); l = strlen(name); buf += l, max -= l; break; case 'D': strncpy(buf, dir, max); l = strlen(dir); buf += l, max -= l; break; case 'B': snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name); cp = &scratch[strlen(scratch) - 1]; while (cp != scratch && *cp != '/') --cp; *cp = '\0'; strncpy(buf, scratch, max); l = strlen(scratch); buf += l, max -= l; break; case 'f': snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name); cp = &scratch[strlen(scratch) - 1]; while (cp != scratch && *(cp - 1) != '/') --cp; strncpy(buf, cp, max); l = strlen(cp); buf += l, max -= l; break; default: *buf++ = *fmt; --max; break; } ++fmt; } else { *buf++ = *fmt++; --max; } } *buf = '\0'; } Index: head/usr.sbin/pkg_install/lib/lib.h =================================================================== --- head/usr.sbin/pkg_install/lib/lib.h (revision 131279) +++ head/usr.sbin/pkg_install/lib/lib.h (revision 131280) @@ -1,226 +1,226 @@ /* $FreeBSD$ */ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jordan K. Hubbard * 18 July 1993 * * Include and define various things wanted by the library routines. * */ #ifndef _INST_LIB_LIB_H_ #define _INST_LIB_LIB_H_ /* Includes */ #include #include #include #include #include #include #include #include #include #include #include /* Macros */ #define SUCCESS (0) #define FAIL (-1) #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #define YES 2 #define NO 1 /* Usually "rm", but often "echo" during debugging! */ #define REMOVE_CMD "rm" /* Usually "rm", but often "echo" during debugging! */ #define RMDIR_CMD "rmdir" /* Where we put logging information by default, else ${PKG_DBDIR} if set */ #define DEF_LOG_DIR "/var/db/pkg" /* just in case we change the environment variable name */ #define PKG_DBDIR "PKG_DBDIR" /* macro to get name of directory where we put logging information */ #define LOG_DIR (getenv(PKG_DBDIR) ? getenv(PKG_DBDIR) : DEF_LOG_DIR) /* The names of our "special" files */ #define CONTENTS_FNAME "+CONTENTS" #define COMMENT_FNAME "+COMMENT" #define DESC_FNAME "+DESC" #define INSTALL_FNAME "+INSTALL" #define POST_INSTALL_FNAME "+POST-INSTALL" #define DEINSTALL_FNAME "+DEINSTALL" #define POST_DEINSTALL_FNAME "+POST-DEINSTALL" #define REQUIRE_FNAME "+REQUIRE" #define REQUIRED_BY_FNAME "+REQUIRED_BY" #define DISPLAY_FNAME "+DISPLAY" #define MTREE_FNAME "+MTREE_DIRS" #define CMD_CHAR '@' /* prefix for extended PLIST cmd */ /* The name of the "prefix" environment variable given to scripts */ #define PKG_PREFIX_VNAME "PKG_PREFIX" /* * Version of the package tools - increase only when some * functionality used by bsd.port.mk is changed, added or removed */ #define PKG_INSTALL_VERSION 20030417 #define PKG_WRAPCONF_FNAME "/var/db/pkg_install.conf" #define main(argc, argv) real_main(argc, argv) /* Version numbers to assist with changes in package file format */ #define PLIST_FMT_VER_MAJOR 1 #define PLIST_FMT_VER_MINOR 1 enum _plist_t { PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD, PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, PLIST_IGNORE, PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY, PLIST_PKGDEP, PLIST_CONFLICTS, PLIST_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST, PLIST_OPTION, PLIST_ORIGIN, PLIST_DEPORIGIN }; typedef enum _plist_t plist_t; enum _match_t { MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_NGLOB, MATCH_EREGEX, MATCH_REGEX }; typedef enum _match_t match_t; /* Types */ typedef unsigned int Boolean; struct _plist { struct _plist *prev, *next; char *name; Boolean marked; plist_t type; }; typedef struct _plist *PackingList; struct _pack { struct _plist *head, *tail; - char *name; - char *origin; + const char *name; + const char *origin; int fmtver_maj, fmtver_mnr; }; typedef struct _pack Package; struct reqr_by_entry { STAILQ_ENTRY(reqr_by_entry) link; char pkgname[PATH_MAX]; }; STAILQ_HEAD(reqr_by_head, reqr_by_entry); /* Prototypes */ /* Misc */ int vsystem(const char *, ...); char *vpipe(const char *, ...); void cleanup(int); char *make_playpen(char *, off_t); char *where_playpen(void); void leave_playpen(void); off_t min_free(const char *); /* String */ char *get_dash_string(char **); char *copy_string(const char *); char *copy_string_adds_newline(const char *); Boolean suffix(const char *, const char *); void nuke_suffix(char *); void str_lowercase(char *); char *strconcat(const char *, const char *); char *get_string(char *, int, FILE *); /* File */ Boolean fexists(const char *); Boolean isdir(const char *); Boolean isemptydir(const char *fname); Boolean isemptyfile(const char *fname); Boolean isfile(const char *); Boolean isempty(const char *); Boolean issymlink(const char *); Boolean isURL(const char *); char *fileGetURL(const char *, const char *); char *fileFindByPath(const char *, const char *); char *fileGetContents(const char *); void write_file(const char *, const char *); void copy_file(const char *, const char *, const char *); void move_file(const char *, const char *, const char *); void copy_hierarchy(const char *, const char *, Boolean); int delete_hierarchy(const char *, Boolean, Boolean); int unpack(const char *, const char *); void format_cmd(char *, int, const char *, const char *, const char *); /* Msg */ void upchuck(const char *); void barf(const char *, ...); void whinge(const char *, ...); Boolean y_or_n(Boolean, const char *, ...); /* Packing list */ PackingList new_plist_entry(void); PackingList last_plist(Package *); PackingList find_plist(Package *, plist_t); char *find_plist_option(Package *, const char *name); void plist_delete(Package *, Boolean, plist_t, const char *); void free_plist(Package *); void mark_plist(Package *); void csum_plist_entry(char *, PackingList); void add_plist(Package *, plist_t, const char *); void add_plist_top(Package *, plist_t, const char *); void delete_plist(Package *pkg, Boolean all, plist_t type, const char *name); void write_plist(Package *, FILE *); void read_plist(Package *, FILE *); int plist_cmd(const char *, char **); int delete_package(Boolean, Boolean, Package *); Boolean make_preserve_name(char *, int, const char *, const char *); /* For all */ int pkg_perform(char **); int real_main(int, char **); /* Query installed packages */ char **matchinstalled(match_t, char **, int *); char **matchbyorigin(const char *, int *); int isinstalledpkg(const char *name); int pattern_match(match_t MatchType, char *pattern, const char *pkgname); /* Dependencies */ int sortdeps(char **); int chkifdepends(const char *, const char *); int requiredby(const char *, struct reqr_by_head **, Boolean, Boolean); /* Version */ int verscmp(Package *, int, int); int version_cmp(const char *, const char *); /* Externs */ extern Boolean Verbose; extern Boolean Fake; extern Boolean Force; extern int AutoAnswer; #endif /* _INST_LIB_LIB_H_ */ Index: head/usr.sbin/pkg_install/lib/match.c =================================================================== --- head/usr.sbin/pkg_install/lib/match.c (revision 131279) +++ head/usr.sbin/pkg_install/lib/match.c (revision 131280) @@ -1,515 +1,516 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Maxim Sobolev * 24 February 2001 * * Routines used to query installed packages. * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include #include #include #include /* * Simple structure representing argv-like * NULL-terminated list. */ struct store { int currlen; int used; char **store; }; static int rex_match(const char *, const char *, int); static int csh_match(const char *, const char *, int); struct store *storecreate(struct store *); static int storeappend(struct store *, const char *); static int fname_cmp(const FTSENT * const *, const FTSENT * const *); /* * Function to query names of installed packages. * MatchType - one of MATCH_ALL, MATCH_EREGEX, MATCH_REGEX, MATCH_GLOB, MATCH_NGLOB; * patterns - NULL-terminated list of glob or regex patterns * (could be NULL for MATCH_ALL); * retval - return value (could be NULL if you don't want/need * return value). * Returns NULL-terminated list with matching names. * Names in list returned are dynamically allocated and should * not be altered by the caller. */ char ** matchinstalled(match_t MatchType, char **patterns, int *retval) { int i, errcode, len; char *matched; const char *paths[2] = {LOG_DIR, NULL}; static struct store *store = NULL; FTS *ftsp; FTSENT *f; Boolean *lmatched; store = storecreate(store); if (store == NULL) { if (retval != NULL) *retval = 1; return NULL; } if (retval != NULL) *retval = 0; if (!isdir(paths[0])) { if (retval != NULL) *retval = 1; return NULL; /* Not reached */ } /* Count number of patterns */ if (patterns != NULL) { for (len = 0; patterns[len]; len++) {} lmatched = alloca(sizeof(*lmatched) * len); if (lmatched == NULL) { warnx("%s(): alloca() failed", __func__); if (retval != NULL) *retval = 1; return NULL; } } else len = 0; for (i = 0; i < len; i++) lmatched[i] = FALSE; ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp); if (ftsp != NULL) { while ((f = fts_read(ftsp)) != NULL) { if (f->fts_info == FTS_D && f->fts_level == 1) { fts_set(ftsp, f, FTS_SKIP); matched = NULL; errcode = 0; if (MatchType == MATCH_ALL) matched = f->fts_name; else for (i = 0; patterns[i]; i++) { errcode = pattern_match(MatchType, patterns[i], f->fts_name); if (errcode == 1) { matched = f->fts_name; lmatched[i] = TRUE; errcode = 0; } if (matched != NULL || errcode != 0) break; } if (errcode == 0 && matched != NULL) errcode = storeappend(store, matched); if (errcode != 0) { if (retval != NULL) *retval = 1; return NULL; /* Not reached */ } } } fts_close(ftsp); } if (MatchType == MATCH_GLOB) { for (i = 0; i < len; i++) if (lmatched[i] == FALSE) storeappend(store, patterns[i]); } if (store->used == 0) return NULL; else return store->store; } int pattern_match(match_t MatchType, char *pattern, const char *pkgname) { int errcode = 0; const char *fname = pkgname; char basefname[PATH_MAX]; char condchar = '\0'; char *condition; /* do we have an appended condition? */ condition = strpbrk(pattern, "<>="); if (condition) { const char *ch; /* yes, isolate the pattern from the condition ... */ if (condition > pattern && condition[-1] == '!') condition--; condchar = *condition; *condition = '\0'; /* ... and compare the name without version */ ch = strrchr(fname, '-'); if (ch && ch - fname < PATH_MAX) { strlcpy(basefname, fname, ch - fname + 1); fname = basefname; } } switch (MatchType) { case MATCH_EREGEX: case MATCH_REGEX: errcode = rex_match(pattern, fname, MatchType == MATCH_EREGEX ? 1 : 0); break; case MATCH_NGLOB: case MATCH_GLOB: errcode = (csh_match(pattern, fname, 0) == 0) ? 1 : 0; break; case MATCH_EXACT: errcode = (strcmp(pattern, fname) == 0) ? 1 : 0; break; case MATCH_ALL: errcode = 1; break; default: break; } /* loop over all appended conditions */ while (condition) { /* restore the pattern */ *condition = condchar; /* parse the condition (fun with bits) */ if (errcode == 1) { char *nextcondition; /* compare version numbers */ int match = 0; if (*++condition == '=') { match = 2; condition++; } switch(condchar) { case '<': match |= 1; break; case '>': match |= 4; break; case '=': match |= 2; break; case '!': match = 5; break; } /* isolate the version number from the next condition ... */ nextcondition = strpbrk(condition, "<>=!"); if (nextcondition) { condchar = *nextcondition; *nextcondition = '\0'; } /* and compare the versions (version_cmp removes the filename for us) */ if ((match & (1 << (version_cmp(pkgname, condition) + 1))) == 0) errcode = 0; condition = nextcondition; } else { break; } } return errcode; } /* * Synopsis is similar to matchinstalled(), but use origin * as a key for matching packages. */ char ** matchbyorigin(const char *origin, int *retval) { char **installed; int i; static struct store *store = NULL; store = storecreate(store); if (store == NULL) { if (retval != NULL) *retval = 1; return NULL; } if (retval != NULL) *retval = 0; installed = matchinstalled(MATCH_ALL, NULL, retval); if (installed == NULL) return NULL; for (i = 0; installed[i] != NULL; i++) { FILE *fp; char *cp, tmp[PATH_MAX]; int cmd; snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]); /* * SPECIAL CASE: ignore empty dirs, since we can can see them * during port installation. */ if (isemptydir(tmp)) continue; snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME); fp = fopen(tmp, "r"); if (fp == NULL) { warnx("the package info for package '%s' is corrupt", installed[i]); continue; } cmd = -1; while (fgets(tmp, sizeof(tmp), fp)) { int len = strlen(tmp); while (len && isspace(tmp[len - 1])) tmp[--len] = '\0'; if (!len) continue; cp = tmp; if (tmp[0] != CMD_CHAR) continue; cmd = plist_cmd(tmp + 1, &cp); if (cmd == PLIST_ORIGIN) { if (csh_match(origin, cp, FNM_PATHNAME) == 0) storeappend(store, installed[i]); break; } } if (cmd != PLIST_ORIGIN) warnx("package %s has no origin recorded", installed[i]); fclose(fp); } if (store->used == 0) return NULL; else return store->store; } /* - * Return TRUE if the specified package is installed, - * or FALSE otherwise. + * + * Return 1 if the specified package is installed, + * 0 if not, and -1 if an error occured. */ int isinstalledpkg(const char *name) { char buf[FILENAME_MAX]; char buf2[FILENAME_MAX]; snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name); if (!isdir(buf) || access(buf, R_OK) == FAIL) - return FALSE; + return 0; snprintf(buf2, sizeof(buf2), "%s/%s", buf, CONTENTS_FNAME); if (!isfile(buf2) || access(buf2, R_OK) == FAIL) - return FALSE; + return -1; - return TRUE; + return 1; } /* * Returns 1 if specified pkgname matches RE pattern. * Otherwise returns 0 if doesn't match or -1 if RE * engine reported an error (usually invalid syntax). */ static int rex_match(const char *pattern, const char *pkgname, int extended) { char errbuf[128]; int errcode; int retval; regex_t rex; retval = 0; errcode = regcomp(&rex, pattern, (extended ? REG_EXTENDED : REG_BASIC) | REG_NOSUB); if (errcode == 0) errcode = regexec(&rex, pkgname, 0, NULL, 0); if (errcode == 0) { retval = 1; } else if (errcode != REG_NOMATCH) { regerror(errcode, &rex, errbuf, sizeof(errbuf)); warnx("%s: %s", pattern, errbuf); retval = -1; } regfree(&rex); return retval; } /* * Match string by a csh-style glob pattern. Returns 0 on * match and FNM_NOMATCH otherwise, to be compatible with * fnmatch(3). */ static int csh_match(const char *pattern, const char *string, int flags) { int ret = FNM_NOMATCH; const char *nextchoice = pattern; const char *current = NULL; int prefixlen = -1; int currentlen = 0; int level = 0; do { const char *pos = nextchoice; const char *postfix = NULL; Boolean quoted = FALSE; nextchoice = NULL; do { const char *eb; if (!*pos) { postfix = pos; } else if (quoted) { quoted = FALSE; } else { switch (*pos) { case '{': ++level; if (level == 1) { current = pos+1; prefixlen = pos-pattern; } break; case ',': if (level == 1 && !nextchoice) { nextchoice = pos+1; currentlen = pos-current; } break; case '}': if (level == 1) { postfix = pos+1; if (!nextchoice) currentlen = pos-current; } level--; break; case '[': eb = pos+1; if (*eb == '!' || *eb == '^') eb++; if (*eb == ']') eb++; while(*eb && *eb != ']') eb++; if (*eb) pos=eb; break; case '\\': quoted = TRUE; break; default: ; } } pos++; } while (!postfix); if (current) { char buf[FILENAME_MAX]; snprintf(buf, sizeof(buf), "%.*s%.*s%s", prefixlen, pattern, currentlen, current, postfix); ret = csh_match(buf, string, flags); if (ret) { current = nextchoice; level = 1; } else current = NULL; } else ret = fnmatch(pattern, string, flags); } while (current); return ret; } /* * Create an empty store, optionally deallocating * any previously allocated space if store != NULL. */ struct store * storecreate(struct store *store) { int i; if (store == NULL) { store = malloc(sizeof *store); if (store == NULL) { warnx("%s(): malloc() failed", __func__); return NULL; } store->currlen = 0; store->store = NULL; } else if (store->store != NULL) { /* Free previously allocated memory */ for (i = 0; store->store[i] != NULL; i++) free(store->store[i]); store->store[0] = NULL; } store->used = 0; return store; } /* * Append specified element to the provided store. */ static int storeappend(struct store *store, const char *item) { if (store->used + 2 > store->currlen) { store->currlen += 16; store->store = reallocf(store->store, store->currlen * sizeof(*(store->store))); if (store->store == NULL) { store->currlen = 0; warnx("%s(): reallocf() failed", __func__); return 1; } } asprintf(&(store->store[store->used]), "%s", item); if (store->store[store->used] == NULL) { warnx("%s(): malloc() failed", __func__); return 1; } store->used++; store->store[store->used] = NULL; return 0; } static int fname_cmp(const FTSENT * const *a, const FTSENT * const *b) { return strcmp((*a)->fts_name, (*b)->fts_name); } Index: head/usr.sbin/pkg_install/sign/Makefile =================================================================== --- head/usr.sbin/pkg_install/sign/Makefile (revision 131279) +++ head/usr.sbin/pkg_install/sign/Makefile (revision 131280) @@ -1,15 +1,19 @@ # $FreeBSD$ # $OpenBSD: Makefile.bsd-wrapper,v 1.2 1999/10/07 16:30:32 espie Exp $ PROG= pkg_sign LINKS= ${BINDIR}/pkg_sign ${BINDIR}/pkg_check MLINKS= pkg_sign.1 pkg_check.1 SRCS= main.c check.c common.c gzip.c pgp_check.c pgp_sign.c \ sha1.c sign.c stand.c x509.c +CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib + +WARNS?= 0 + DISTRIBUTION= crypto -DPADD= ${LIBINSTALL} ${LIBCRYPTO} -LDADD= ${LIBINSTALL} -lcrypto +DPADD= ${LIBINSTALL} ${LIBMD} ${LIBCRYPTO} +LDADD= ${LIBINSTALL} -lmd -lcrypto .include Index: head/usr.sbin/pkg_install/sign/sha1.c =================================================================== --- head/usr.sbin/pkg_install/sign/sha1.c (revision 131279) +++ head/usr.sbin/pkg_install/sign/sha1.c (revision 131280) @@ -1,224 +1,231 @@ /* $OpenBSD: sha1.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Marc Espie for the OpenBSD * Project. * * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include -#include +#include #include "stand.h" #include "gzip.h" #include "extern.h" /* private context for sha1 signature checker */ struct sha1_checker { SHA_CTX context; const char *id; const char *filename; }; #define SHA1_TEMPLATE "SHA1 (%s) = " #define BUFSIZE (MAXID+sizeof(SHA1_TEMPLATE)+2*SHA_DIGEST_LENGTH+1) /* * Finalize SHA1 checksum for our sha1_context into result * (size at least BUFSIZE). Returns the length of the checksum * marker, e.g., SHA1 (id) = xxxxxxxxx * ^here * Return 0 for errors. */ size_t sha1_build_checksum(result, n) char *result; struct sha1_checker *n; { size_t length; - sprintf(result, "SHA1 (%s) = ", n->id); + snprintf(result, BUFSIZE-2*SHA_DIGEST_LENGTH-1, SHA1_TEMPLATE, n->id); length = strlen(result); - SHA1_Final(result + length, &n->context); + SHA1_End(&n->context, result + length); strcat(result, "\n"); free(n); return length; } void * new_sha1_checker(h, sign, userid, envp, filename) struct mygzip_header *h; struct signature *sign; const char *userid; char *envp[]; /*@observer@*/const char *filename; { struct sha1_checker *n; assert(sign->type == TAG_SHA1); /* make sure data conforms to what we can handle */ if (sign->length > MAXID || sign->data[sign->length-1] != '\0') { warnx("Corrupted SHA1 header in %s", filename); return 0; } n = malloc(sizeof *n); if (n == NULL) { warnx("Can't allocate sha1_checker"); return NULL; } SHA1_Init(&n->context); n->id = sign->data; n->filename = filename; /* copy header, as this is a checksum, we don't strip our own marker */ if (gzip_copy_header(h, sign, sha1_add, n) == 0) { warnx("Unexpected header in %s", filename); free(n); return 0; } return n; } void sha1_add(arg, buffer, length) void *arg; const char *buffer; size_t length; { struct sha1_checker *n = arg; SHA1_Update(&n->context, buffer, length); } int sha1_sign_ok(arg) void *arg; { struct sha1_checker *n = arg; char buffer[BUFSIZE]; char scan[BUFSIZE]; size_t length; FILE *f; int tag_found; length = sha1_build_checksum(buffer, n); f= fopen(SHA1_DB_NAME, "r"); tag_found = 0; if (f == NULL) { warn("Can't access checksum file %s", SHA1_DB_NAME); return PKG_BADSIG; } while (fgets(scan, sizeof(scan), f) != NULL) { if (strcmp(scan, buffer) == 0) { fprintf(stderr, "Checksum ok\n"); return PKG_GOODSIG; } if (strncmp(scan, buffer, length) == 0) tag_found = 1; } if (tag_found) { warnx("Checksum incorrect for %s (%s)", n->filename, n->id); return PKG_BADSIG; } else { warnx("No checksum found for %s (%s)", n->filename, n->id); return PKG_SIGUNKNOWN; } } int retrieve_sha1_marker(filename, sign, userid) const char *filename; struct signature **sign; const char *userid; { struct signature *n; struct mygzip_header h; FILE *f; char buffer[1024]; char result[BUFSIZE]; - ssize_t length; + ssize_t length = -1; struct sha1_checker *checker; struct signature *old; *sign = NULL; if (userid == NULL) return 0; /* * Create a blank signature and fill it with the userid. */ n = malloc(sizeof *n); if (n == NULL) return 0; - n->data = (char *)userid; - n->length = strlen(n->data)+1; + n->length = strlen(userid)+1; + n->data = malloc(n->length); + if (n->data == NULL) { + free(n); + return 0; + } + memcpy(n->data, userid, n->length); n->type = TAG_SHA1; memcpy(n->tag, sha1tag, sizeof sha1tag); sign_fill_tag(n); /* * Read the gzip header and add our "userid" signature to it. */ f = fopen(filename, "r"); if (f == NULL) { free(n); return 0; } if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) { warnx("File %s is not a gzip file\n", filename); fclose(f); free(n); return 0; } n->next = *sign; *sign = n; /* * Calculate the SHA1 of the remaining data and write it to stderr. */ checker = new_sha1_checker(&h, *sign, NULL, NULL, filename); - while ((length = fread(buffer, 1, sizeof buffer, f)) > 0) - sha1_add(checker, buffer, length); + if (checker) + while ((length = fread(buffer, 1, sizeof buffer, f)) > 0) + sha1_add(checker, buffer, length); if (fclose(f) != 0 || length == -1) { warn("Problem checksumming %s", filename); *sign = n->next; free(n); return 0; } (void)sha1_build_checksum(result, checker); fputs(result, stderr); return 1; } Index: head/usr.sbin/pkg_install/version/perform.c =================================================================== --- head/usr.sbin/pkg_install/version/perform.c (revision 131279) +++ head/usr.sbin/pkg_install/version/perform.c (revision 131280) @@ -1,391 +1,391 @@ /* * FreeBSD install - a package for the installation and maintainance * of non-core utilities. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Jeremy D. Lea. * 11 May 2002 * * This is the version module. Based on pkg_version.pl by Bruce A. Mah. * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include "version.h" #include #include #include FILE *IndexFile; struct index_head Index = SLIST_HEAD_INITIALIZER(Index); static int pkg_do(char *); static void show_version(const char *, const char *, const char *); /* * This is the traditional pkg_perform, except that the argument is _not_ * a list of packages. It is the index file from the command line. * * We loop over the installed packages, matching them with the -s flag * if needed and calling pkg_do(). Before hand we set up a few things, * and after we tear them down... */ int pkg_perform(char **indexarg) { char tmp[PATH_MAX], **pkgs, *pat[2], **patterns; struct index_entry *ie; int i, err_cnt = 0; int MatchType; /* * Try to find and open the INDEX. We only check IndexFile != NULL * later, if we actually need the INDEX. * XXX This should not be hard-coded to INDEX-5. */ if (*indexarg == NULL) snprintf(tmp, PATH_MAX, "%s/INDEX-5", PORTS_DIR); else strlcpy(tmp, *indexarg, PATH_MAX); if (isURL(tmp)) IndexFile = fetchGetURL(tmp, ""); else IndexFile = fopen(tmp, "r"); /* Get either a list of matching or all packages */ if (MatchName != NULL) { pat[0] = MatchName; pat[1] = NULL; MatchType = RegexExtended ? MATCH_EREGEX : MATCH_REGEX; patterns = pat; } else { MatchType = MATCH_ALL; patterns = NULL; } pkgs = matchinstalled(MatchType, patterns, &err_cnt); if (err_cnt != 0) errx(2, "Unable to find package database directory!"); if (pkgs == NULL) { switch (MatchType) { case MATCH_ALL: warnx("no packages installed"); return (0); case MATCH_EREGEX: case MATCH_REGEX: warnx("no packages match pattern"); return (1); default: break; } } for (i = 0; pkgs[i] != NULL; i++) err_cnt += pkg_do(pkgs[i]); /* If we opened the INDEX in pkg_do(), clean up. */ while (!SLIST_EMPTY(&Index)) { ie = SLIST_FIRST(&Index); SLIST_REMOVE_HEAD(&Index, next); if (ie->name != NULL) free(ie->name); if (ie->origin != NULL) free(ie->origin); free(ie); } if (IndexFile != NULL) fclose(IndexFile); return err_cnt; } /* * Traditional pkg_do(). We take the package name we are passed and * first slurp in the CONTENTS file, getting name and origin, then * we look for it's corresponding Makefile. If that fails we pull in * the INDEX, and check there. */ static int pkg_do(char *pkg) { char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL; Package plist; struct index_entry *ie; FILE *fp; size_t len; /* Suck in the contents list. */ plist.head = plist.tail = NULL; plist.name = plist.origin = NULL; snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME); fp = fopen(tmp, "r"); if (!fp) { - warnx("unable to open %s file", CONTENTS_FNAME); + warnx("the package info for package '%s' is corrupt", pkg); return 1; } read_plist(&plist, fp); fclose(fp); if (plist.name == NULL) { warnx("%s does not appear to be a valid package!", pkg); return 1; } /* * First we check if the installed package has an origin, and try * looking for it's Makefile. If we find the Makefile we get the * latest version from there. If we fail, we start looking in the * INDEX, first matching the origin and then the package name. */ if (plist.origin != NULL) { snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin); if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) { if ((latest = vpipe("make -V PKGNAME", tmp)) == NULL) warnx("Failed to get PKGNAME from %s/Makefile!", tmp); else show_version(plist.name, latest, "port"); } } if (latest == NULL) { /* We only pull in the INDEX once, if needed. */ if (SLIST_EMPTY(&Index)) { if (!IndexFile) errx(2, "Unable to open INDEX in %s.", __func__); while ((ch = fgetln(IndexFile, &len)) != NULL) { /* * Don't use strlcpy() because fgetln() doesn't * return a valid C string. */ strncpy(tmp, ch, MIN(len, PATH_MAX)); tmp[PATH_MAX-1] = '\0'; /* The INDEX has pkgname|portdir|... */ if ((ch = strchr(tmp, '|')) != NULL) ch[0] = '\0'; if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL) ch[0] = '\0'; /* Look backwards for the last two dirs = origin */ while (ch != NULL && *--ch != '/') if (ch[0] == '\0') ch = NULL; while (ch != NULL && *--ch != '/') if (ch[0] == '\0') ch = NULL; if (ch == NULL) errx(2, "The INDEX does not appear to be valid!"); if ((ie = malloc(sizeof(struct index_entry))) == NULL) errx(2, "Unable to allocate memory in %s.", __func__); bzero(ie, sizeof(struct index_entry)); ie->name = strdup(tmp); ie->origin = strdup(&ch[1]); /* Who really cares if we reverse the index... */ SLIST_INSERT_HEAD(&Index, ie, next); } } /* Now that we've slurped in the INDEX... */ SLIST_FOREACH(ie, &Index, next) { if (plist.origin != NULL) { if (strcmp(plist.origin, ie->origin) == 0) latest = strdup(ie->name); } else { strlcpy(tmp, ie->name, PATH_MAX); strlcpy(tmp2, plist.name, PATH_MAX); /* Chop off the versions and compare. */ if ((ch = strrchr(tmp, '-')) == NULL) errx(2, "The INDEX does not appear to be valid!"); ch[0] = '\0'; if ((ch = strrchr(tmp2, '-')) == NULL) warnx("%s is not a valid package!", plist.name); else ch[0] = '\0'; if (strcmp(tmp2, tmp) == 0) { if (latest != NULL) { /* Multiple matches */ snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name); free(latest); latest = strdup(tmp); } else latest = strdup(ie->name); } } } if (latest == NULL) show_version(plist.name, NULL, plist.origin); else show_version(plist.name, latest, "index"); } if (latest != NULL) free(latest); free_plist(&plist); return 0; } #define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \ (LimitChars != NULL && strchr(LimitChars, (c))) || \ (PreventChars == NULL && LimitChars == NULL)) /* * Do the work of comparing and outputing. Ugly, but well that's what * You get when you try to match perl output in C ;-). */ void show_version(const char *installed, const char *latest, const char *source) { char *ch, tmp[PATH_MAX]; const char *ver; int cmp = 0; if (!installed || strlen(installed) == 0) return; strlcpy(tmp, installed, PATH_MAX); if (!Verbose) { if ((ch = strrchr(tmp, '-')) != NULL) ch[0] = '\0'; } if (latest == NULL) { if (source == NULL && OUTPUT('!')) { printf("%-34s !", tmp); if (Verbose) printf(" Comparison failed"); printf("\n"); } else if (source != NULL && OUTPUT('?')) { printf("%-34s ?", tmp); if (Verbose) printf(" orphaned: %s", source); printf("\n"); } } else if (strchr(latest,'|') != NULL) { if (OUTPUT('*')) { printf("%-34s *", tmp); if (Verbose) { strlcpy(tmp, latest, PATH_MAX); ch = strchr(tmp, '|'); ch[0] = '\0'; ver = strrchr(tmp, '-'); ver = ver ? &ver[1] : tmp; printf(" multiple versions (index has %s", ver); do { ver = strrchr(&ch[1], '-'); ver = ver ? &ver[1] : &ch[1]; if ((ch = strchr(&ch[1], '|')) != NULL) ch[0] = '\0'; printf(", %s", ver); } while (ch != NULL); printf(")"); } printf("\n"); } } else { cmp = version_cmp(installed, latest); ver = strrchr(latest, '-'); ver = ver ? &ver[1] : latest; if (cmp < 0 && OUTPUT('<')) { printf("%-34s <", tmp); if (Verbose) printf(" needs updating (%s has %s)", source, ver); printf("\n"); } else if (cmp == 0 && OUTPUT('=')) { printf("%-34s =", tmp); if (Verbose) printf(" up-to-date with %s", source); printf("\n"); } else if (cmp > 0 && OUTPUT('>')) { printf("%-34s >", tmp); if (Verbose) printf(" succeeds %s (%s has %s)", source, source, ver); printf("\n"); } } } int version_match(char *pattern, const char *pkgname) { int ret = 0; int matchstream = 0; FILE *fp = NULL; Boolean isTMP = FALSE; if (isURL(pkgname)) { fp = fetchGetURL(pkgname, ""); isTMP = TRUE; matchstream = 1; if (fp == NULL) errx(2, "Unable to open %s.", pkgname); } else if (pkgname[0] == '/') { fp = fopen(pkgname, "r"); isTMP = TRUE; matchstream = 1; if (fp == NULL) errx(2, "Unable to open %s.", pkgname); } else if (strcmp(pkgname, "-") == 0) { fp = stdin; matchstream = 1; } else if (isURL(pattern)) { fp = fetchGetURL(pattern, ""); isTMP = TRUE; matchstream = -1; if (fp == NULL) errx(2, "Unable to open %s.", pattern); } else if (pattern[0] == '/') { fp = fopen(pattern, "r"); isTMP = TRUE; matchstream = -1; if (fp == NULL) errx(2, "Unable to open %s.", pattern); } else if (strcmp(pattern, "-") == 0) { fp = stdin; matchstream = -1; } else { ret = pattern_match(MATCH_GLOB, pattern, pkgname); } if (fp != NULL) { size_t len; char *line; while ((line = fgetln(fp, &len)) != NULL) { int match; char *ch, ln[2048]; size_t lnlen; if (len > 0 && line[len-1] == '\n') len --; lnlen = len; if (lnlen > sizeof(ln)-1) lnlen = sizeof(ln)-1; memcpy(ln, line, lnlen); ln[lnlen] = '\0'; if ((ch = strchr(ln, '|')) != NULL) ch[0] = '\0'; if (matchstream > 0) match = pattern_match(MATCH_GLOB, pattern, ln); else match = pattern_match(MATCH_GLOB, ln, pkgname); if (match == 1) { ret = 1; printf("%.*s\n", (int)len, line); } } if (isTMP) fclose(fp); } return ret; } void cleanup(int sig) { if (sig) exit(1); }