diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h index 2356a7ee4a57..8db99eda8bbb 100644 --- a/usr.sbin/pkg_install/info/info.h +++ b/usr.sbin/pkg_install/info/info.h @@ -23,6 +23,8 @@ #ifndef _INST_INFO_H_INCLUDE #define _INST_INFO_H_INCLUDE +#include + #ifndef MAXINDEXSIZE #define MAXINDEXSIZE 59 #endif @@ -47,12 +49,21 @@ #define SHOW_ORIGIN 0x2000 #define SHOW_CKSUM 0x4000 +struct which_entry { + TAILQ_ENTRY(which_entry) next; + char file[PATH_MAX]; + char package[PATH_MAX]; + Boolean skip; +}; +TAILQ_HEAD(which_head, which_entry); + extern int Flags; extern Boolean Quiet; extern char *InfoPrefix; extern char PlayPen[]; extern char *CheckPkg; extern match_t MatchType; +extern struct which_head *whead; extern void show_file(char *, char *); extern void show_plist(char *, Package *, plist_t); diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c index aa39a5f4c599..ac9958444b72 100644 --- a/usr.sbin/pkg_install/info/main.c +++ b/usr.sbin/pkg_install/info/main.c @@ -19,16 +19,16 @@ * */ -#include #include "lib.h" #include "info.h" +#include #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif -static char Options[] = "acdDe:fgGhiIkl:LmopqrRst:vx"; +static char Options[] = "acdDe:fgGhiIkl:LmopqrRst:vW:x"; int Flags = 0; match_t MatchType = MATCH_GLOB; @@ -36,6 +36,7 @@ Boolean Quiet = FALSE; char *InfoPrefix = ""; char PlayPen[FILENAME_MAX]; char *CheckPkg = NULL; +struct which_head *whead; static void usage __P((void)); @@ -46,6 +47,11 @@ main(int argc, char **argv) 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; @@ -148,6 +154,20 @@ main(int argc, char **argv) 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 'h': case '?': default: @@ -185,7 +205,8 @@ main(int argc, char **argv) } /* If no packages, yelp */ - if (pkgs == start && MatchType != MATCH_ALL && !CheckPkg) + if (pkgs == start && MatchType != MATCH_ALL && !CheckPkg && + TAILQ_EMPTY(whead)) warnx("missing package name(s)"), usage(); *pkgs = NULL; return pkg_perform(start); @@ -196,7 +217,7 @@ usage() { fprintf(stderr, "%s\n%s\n%s\n", "usage: pkg_info [-cdDfGiIkLmopqrRsvx] [-e package] [-l prefix]", - " [-t template] [pkg-name ...]", + " [-t template] [-W filename] [pkg-name ...]", " pkg_info -a [flags]"); exit(1); } diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c index 4d3d50e6e437..6b111a59e1b8 100644 --- a/usr.sbin/pkg_install/info/perform.c +++ b/usr.sbin/pkg_install/info/perform.c @@ -25,12 +25,12 @@ static const char rcsid[] = #include "lib.h" #include "info.h" - -#include #include #include static int pkg_do(char *); +static int find_pkg(char *, struct which_head *); +static int cmp_path(const char *, const char *, const char *); int pkg_perform(char **pkgs) @@ -42,16 +42,19 @@ pkg_perform(char **pkgs) signal(SIGINT, cleanup); + tmp = getenv(PKG_DBDIR); + if (!tmp) + tmp = DEF_LOG_DIR; + /* Overriding action? */ if (CheckPkg) { char buf[FILENAME_MAX]; - tmp = getenv(PKG_DBDIR); - if (!tmp) - tmp = DEF_LOG_DIR; snprintf(buf, FILENAME_MAX, "%s/%s", tmp, CheckPkg); return abs(access(buf, R_OK)); /* Not reached */ + } else if (!TAILQ_EMPTY(whead)) { + return find_pkg(tmp, whead); } if (MatchType != MATCH_EXACT) { @@ -239,3 +242,150 @@ cleanup(int sig) exit(1); } +/* + * 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 *loc, *temp; + int rval; + + asprintf(&temp, "%s/%s", cwd, current); + if (temp == NULL) + errx(2, NULL); + + /* + * Make sure there's no multiple /'s, since some plists + * seem to have them and it could screw up our strncmp. + */ + while ((loc = strstr(temp, "//")) != NULL) + strcpy(loc, loc + 1); + + if (strcmp(target, temp) == 0) + rval = 1; + else + rval = 0; + + free(temp); + return rval; +} + +/* + * Look through package dbs in db_dir and find which + * packages installed the files in which_list. + */ +static int +find_pkg(char *db_dir, struct which_head *which_list) +{ + char **installed; + int errcode, i; + struct which_entry *wp; + + TAILQ_FOREACH(wp, which_list, next) { + /* If it's not a file, we'll see if it's an executable. */ + if (isfile(wp->file) == FALSE) { + if (strchr(wp->file, '/') == NULL) { + char *tmp; + tmp = vpipe("/usr/bin/which %s", wp->file); + if (tmp == NULL) { + warnx("%s: file is not in PATH", wp->file); + wp->skip = TRUE; + } else + strlcpy(wp->file, tmp, PATH_MAX); + free(tmp); + } else { + warnx("%s: file cannot be found", wp->file); + wp->skip = TRUE; + } + } else if (wp->file[0] != '/') { + /* + * If it is a file, and it doesn't start with a /, then it's a + * relative path. in order to give us some chance of getting a + * successful match, tack the current working directory on the + * beginning. this won't work for filenames that include .. or . + * or extra /'s, but it's better than nothing). + */ + char *curdir, *tmp; + + curdir = getcwd(NULL, PATH_MAX); + if (curdir == NULL) + err(2, NULL); + + asprintf(&tmp, "%s/%s", curdir, wp->file); + if (tmp == NULL) + err(2, NULL); + + if (!isfile(tmp)) { + warnx("%s: file cannot be found", tmp); + wp->skip = TRUE; + } else + strlcpy(wp->file, tmp, PATH_MAX); + + free(tmp); + free(curdir); + } + } + + 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", db_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; +} diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1 index bda409cdbd32..7a13fc654796 100644 --- a/usr.sbin/pkg_install/info/pkg_info.1 +++ b/usr.sbin/pkg_install/info/pkg_info.1 @@ -29,6 +29,7 @@ .Op Fl e Ar package .Op Fl l Ar prefix .Op Fl t Ar template +.Op Fl W Ar filename .Op Ar pkg-name ... .Nm .Fl a @@ -105,6 +106,14 @@ 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 x Treat the .Ar pkg-name diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c index 463c1b6b3032..24800c4fe44d 100644 --- a/usr.sbin/pkg_install/info/show.c +++ b/usr.sbin/pkg_install/info/show.c @@ -25,7 +25,6 @@ static const char rcsid[] = #include "lib.h" #include "info.h" - #include #include #include diff --git a/usr.sbin/pkg_install/lib/deps.c b/usr.sbin/pkg_install/lib/deps.c index 4c06da8df1fa..84d5c474fc38 100644 --- a/usr.sbin/pkg_install/lib/deps.c +++ b/usr.sbin/pkg_install/lib/deps.c @@ -25,7 +25,6 @@ static const char rcsid[] = */ #include "lib.h" - #include #include diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c index 4ed452ebfbf5..dbb4625a0c47 100644 --- a/usr.sbin/pkg_install/lib/exec.c +++ b/usr.sbin/pkg_install/lib/exec.c @@ -23,8 +23,8 @@ static const char rcsid[] = * */ -#include #include "lib.h" +#include /* * Unusual system() substitute. Accepts format string and args, @@ -60,3 +60,50 @@ printf("Executing %s\n", cmd); return ret; } +char * +vpipe(const char *fmt, ...) +{ + FILE *fp; + char *cmd, *rp; + int maxargs; + va_list args; + + rp = malloc(MAXPATHLEN); + if (!rp) { + warnx("vpipe can't alloc buffer space"); + return NULL; + } + maxargs = sysconf(_SC_ARG_MAX); + maxargs -= 32; /* some slop for the sh -c */ + cmd = alloca(maxargs); + if (!cmd) { + warnx("vpipe can't alloc arg space"); + return NULL; + } + + va_start(args, fmt); + if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) { + warnx("vsystem args are too long"); + return NULL; + } +#ifdef DEBUG + fprintf(stderr, "Executing %s\n", cmd); +#endif + fflush(NULL); + fp = popen(cmd, "r"); + if (fp == NULL) { + warnx("popen() failed"); + return NULL; + } + get_string(rp, MAXPATHLEN, fp); +#ifdef DEBUG + fprintf(stderr, "Returned %s\n", rp); +#endif + va_end(args); + if (pclose(fp) || (strlen(rp) == 0)) { + free(rp); + return NULL; + } + return rp; +} + diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index 0ed689984366..3ab1b633ece2 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -112,6 +112,7 @@ typedef struct _pack Package; /* Prototypes */ /* Misc */ int vsystem(const char *, ...); +char *vpipe(const char *, ...); void cleanup(int); char *make_playpen(char *, size_t); char *where_playpen(void); @@ -126,6 +127,7 @@ void nuke_suffix(char *); void str_lowercase(char *); char *basename_of(char *); char *strconcat(char *, char *); +char *get_string(char *, int, FILE *); /* File */ Boolean fexists(char *); diff --git a/usr.sbin/pkg_install/lib/match.c b/usr.sbin/pkg_install/lib/match.c index 2bcae2a34fca..f3dc99901351 100644 --- a/usr.sbin/pkg_install/lib/match.c +++ b/usr.sbin/pkg_install/lib/match.c @@ -24,7 +24,6 @@ static const char rcsid[] = */ #include "lib.h" - #include #include #include @@ -97,14 +96,18 @@ matchinstalled(match_t MatchType, char **patterns, int *retval) } /* Count number of patterns */ - for (len = 0; patterns[len]; len++) {} - lmatched = alloca(sizeof(*lmatched) * len); - if (lmatched == NULL) { - warnx("%s(): alloca() failed", __FUNCTION__); - if (retval != NULL) - *retval = 1; - return NULL; - } + if (patterns != NULL) { + for (len = 0; patterns[len]; len++) {} + lmatched = alloca(sizeof(*lmatched) * len); + if (lmatched == NULL) { + warnx("%s(): alloca() failed", __FUNCTION__); + if (retval != NULL) + *retval = 1; + return NULL; + } + } else + len = 0; + for (i = 0; i < len; i++) lmatched[i] = FALSE; diff --git a/usr.sbin/pkg_install/lib/msg.c b/usr.sbin/pkg_install/lib/msg.c index 068d305a2e31..5f954cac1f31 100644 --- a/usr.sbin/pkg_install/lib/msg.c +++ b/usr.sbin/pkg_install/lib/msg.c @@ -24,9 +24,9 @@ static const char rcsid[] = * */ +#include "lib.h" #include #include -#include "lib.h" /* Die a relatively simple death */ void diff --git a/usr.sbin/pkg_install/lib/pen.c b/usr.sbin/pkg_install/lib/pen.c index 687d6bb49db6..62465bf3f902 100644 --- a/usr.sbin/pkg_install/lib/pen.c +++ b/usr.sbin/pkg_install/lib/pen.c @@ -23,8 +23,8 @@ static const char rcsid[] = * */ -#include #include "lib.h" +#include #include #include #include diff --git a/usr.sbin/pkg_install/lib/str.c b/usr.sbin/pkg_install/lib/str.c index 67a5e642d533..c5f11a290fc9 100644 --- a/usr.sbin/pkg_install/lib/str.c +++ b/usr.sbin/pkg_install/lib/str.c @@ -109,3 +109,22 @@ str_lowercase(char *str) ++str; } } + +char * +get_string(char *str, int max, FILE *fp) +{ + int len; + + if (!str) + return NULL; + str[0] = '\0'; + while (fgets(str, max, fp)) { + len = strlen(str); + while (len && isspace(str[len - 1])) + str[--len] = '\0'; + if (len) + return str; + } + return NULL; +} +