1327Sjkh/*
2327Sjkh * FreeBSD install - a package for the installation and maintainance
3327Sjkh * of non-core utilities.
4327Sjkh *
5327Sjkh * Redistribution and use in source and binary forms, with or without
6327Sjkh * modification, are permitted provided that the following conditions
7327Sjkh * are met:
8327Sjkh * 1. Redistributions of source code must retain the above copyright
9327Sjkh *    notice, this list of conditions and the following disclaimer.
10327Sjkh * 2. Redistributions in binary form must reproduce the above copyright
11327Sjkh *    notice, this list of conditions and the following disclaimer in the
12327Sjkh *    documentation and/or other materials provided with the distribution.
13327Sjkh *
14327Sjkh * Jordan K. Hubbard
15327Sjkh * 23 Aug 1993
16327Sjkh *
17327Sjkh * This is the main body of the info module.
18327Sjkh *
19327Sjkh */
20327Sjkh
2193520Sobrien#include <sys/cdefs.h>
2293520Sobrien__FBSDID("$FreeBSD$");
2393520Sobrien
24222035Sflz#include "lib.h"
25327Sjkh#include "info.h"
2672174Ssobomax#include <err.h>
27327Sjkh#include <signal.h>
28327Sjkh
29327Sjkhstatic int pkg_do(char *);
3096613Ssobomaxstatic int find_pkg(struct which_head *);
3174699Ssobomaxstatic int cmp_path(const char *, const char *, const char *);
3284745Ssobomaxstatic char *abspath(const char *);
3396613Ssobomaxstatic int find_pkgs_by_origin(const char *);
34131275Seikstatic int matched_packages(char **pkgs);
35327Sjkh
36327Sjkhint
37327Sjkhpkg_perform(char **pkgs)
38327Sjkh{
3973134Ssobomax    char **matched;
4073134Ssobomax    int err_cnt = 0;
4173134Ssobomax    int i, errcode;
42327Sjkh
43327Sjkh    signal(SIGINT, cleanup);
44327Sjkh
45327Sjkh    /* Overriding action? */
46131275Seik    if (Flags & SHOW_PKGNAME) {
47131275Seik	return matched_packages(pkgs);
48131275Seik    } else if (CheckPkg) {
49131280Seik	return isinstalledpkg(CheckPkg) > 0 ? 0 : 1;
5072174Ssobomax	/* Not reached */
5174699Ssobomax    } else if (!TAILQ_EMPTY(whead)) {
5296613Ssobomax	return find_pkg(whead);
5396030Ssobomax    } else if (LookUpOrigin != NULL) {
5496613Ssobomax	return find_pkgs_by_origin(LookUpOrigin);
5516404Sjkh    }
568857Srgrimes
5773134Ssobomax    if (MatchType != MATCH_EXACT) {
5873134Ssobomax	matched = matchinstalled(MatchType, pkgs, &errcode);
5973134Ssobomax	if (errcode != 0)
6073134Ssobomax	    return 1;
6173134Ssobomax	    /* Not reached */
6272174Ssobomax
6373134Ssobomax	if (matched != NULL)
6473134Ssobomax	    pkgs = matched;
6573134Ssobomax	else switch (MatchType) {
6673134Ssobomax	    case MATCH_GLOB:
6773134Ssobomax		break;
6873134Ssobomax	    case MATCH_ALL:
6973134Ssobomax		warnx("no packages installed");
7073134Ssobomax		return 0;
7173134Ssobomax		/* Not reached */
7273134Ssobomax	    case MATCH_REGEX:
73131275Seik	    case MATCH_EREGEX:
7473134Ssobomax		warnx("no packages match pattern(s)");
7572174Ssobomax		return 1;
7673134Ssobomax		/* Not reached */
7773134Ssobomax	    default:
7873134Ssobomax		break;
7916404Sjkh	}
8073134Ssobomax    }
8172174Ssobomax
8273134Ssobomax    for (i = 0; pkgs[i]; i++)
8373134Ssobomax	err_cnt += pkg_do(pkgs[i]);
8472174Ssobomax
85327Sjkh    return err_cnt;
86327Sjkh}
87327Sjkh
88327Sjkhstatic int
89327Sjkhpkg_do(char *pkg)
90327Sjkh{
918086Sjkh    Boolean installed = FALSE, isTMP = FALSE;
92327Sjkh    char log_dir[FILENAME_MAX];
93131280Seik    char fname[FILENAME_MAX];
94327Sjkh    Package plist;
95327Sjkh    FILE *fp;
968086Sjkh    struct stat sb;
97194497Sbrian    const char *cp = NULL;
988086Sjkh    int code = 0;
99327Sjkh
1008086Sjkh    if (isURL(pkg)) {
101154145Sflz	if ((cp = fileGetURL(NULL, pkg, KeepPackage)) != NULL) {
102178246Sflz	    if (!getcwd(fname, FILENAME_MAX))
103178246Sflz		upchuck("getcwd");
1048086Sjkh	    isTMP = TRUE;
105178246Sflz	} else {
106178246Sflz	    goto bail;
1078086Sjkh	}
1088086Sjkh    }
1099782Sache    else if (fexists(pkg) && isfile(pkg)) {
1108086Sjkh	int len;
111327Sjkh
1128423Sjkh	if (*pkg != '/') {
1138423Sjkh	    if (!getcwd(fname, FILENAME_MAX))
1148423Sjkh		upchuck("getcwd");
1158423Sjkh	    len = strlen(fname);
1168423Sjkh	    snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg);
1178423Sjkh	}
1188423Sjkh	else
1198423Sjkh	    strcpy(fname, pkg);
1208086Sjkh	cp = fname;
1218086Sjkh    }
1228086Sjkh    else {
12311780Sjkh	if ((cp = fileFindByPath(NULL, pkg)) != NULL)
1248086Sjkh	    strncpy(fname, cp, FILENAME_MAX);
1258086Sjkh    }
1268086Sjkh    if (cp) {
127178246Sflz	if (!isURL(pkg)) {
128178246Sflz	    /*
129178246Sflz	     * Apply a crude heuristic to see how much space the package will
130178246Sflz	     * take up once it's unpacked.  I've noticed that most packages
131178246Sflz	     * compress an average of 75%, but we're only unpacking the + files so
132178246Sflz	     * be very optimistic.
133178246Sflz	     */
134178246Sflz	    if (stat(fname, &sb) == FAIL) {
135178246Sflz	        warnx("can't stat package file '%s'", fname);
136178246Sflz	        code = 1;
137178246Sflz	        goto bail;
138178246Sflz	    }
139194497Sbrian	    make_playpen(PlayPen, sb.st_size / 2);
140178246Sflz	    if (unpack(fname, "'+*'")) {
141178246Sflz		warnx("error during unpacking, no info for '%s' available", pkg);
142178246Sflz		code = 1;
143178246Sflz		goto bail;
144178246Sflz	    }
1453364Sjkh	}
146327Sjkh    }
147131280Seik    /* It's not an uninstalled package, try and find it among the installed */
148327Sjkh    else {
149131280Seik	int isinstalled = isinstalledpkg(pkg);
150131280Seik	if (isinstalled < 0) {
151131280Seik	    warnx("the package info for package '%s' is corrupt", pkg);
152131280Seik	    return 1;
153131280Seik	} else if (isinstalled == 0) {
15481046Ssobomax	    warnx("can't find package '%s' installed or in a file!", pkg);
155327Sjkh	    return 1;
156327Sjkh	}
15796613Ssobomax	sprintf(log_dir, "%s/%s", LOG_DIR, pkg);
158327Sjkh	if (chdir(log_dir) == FAIL) {
15930221Scharnier	    warnx("can't change directory to '%s'!", log_dir);
160327Sjkh	    return 1;
161327Sjkh	}
162327Sjkh	installed = TRUE;
163327Sjkh    }
164327Sjkh
165327Sjkh    /* Suck in the contents list */
166327Sjkh    plist.head = plist.tail = NULL;
167327Sjkh    fp = fopen(CONTENTS_FNAME, "r");
168327Sjkh    if (!fp) {
16930221Scharnier	warnx("unable to open %s file", CONTENTS_FNAME);
1708086Sjkh	code = 1;
1718086Sjkh	goto bail;
172327Sjkh    }
173327Sjkh    /* If we have a prefix, add it now */
174327Sjkh    read_plist(&plist, fp);
175327Sjkh    fclose(fp);
176327Sjkh
177327Sjkh    /*
178327Sjkh     * Index is special info type that has to override all others to make
179327Sjkh     * any sense.
180327Sjkh     */
181327Sjkh    if (Flags & SHOW_INDEX) {
1828086Sjkh	char tmp[FILENAME_MAX];
183327Sjkh
1848086Sjkh	snprintf(tmp, FILENAME_MAX, "%-19s ", pkg);
1858086Sjkh	show_index(tmp, COMMENT_FNAME);
186327Sjkh    }
187327Sjkh    else {
188327Sjkh	/* Start showing the package contents */
189411Sjkh	if (!Quiet)
190411Sjkh	    printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
191112572Smdodd	else if (QUIET)
192112572Smdodd	    printf("%s%s:", InfoPrefix, pkg);
193327Sjkh	if (Flags & SHOW_COMMENT)
194379Sjkh	    show_file("Comment:\n", COMMENT_FNAME);
195131280Seik	if (Flags & SHOW_DEPEND)
19684745Ssobomax	    show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE);
1974996Sjkh	if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
1984996Sjkh	    show_file("Required by:\n", REQUIRED_BY_FNAME);
199327Sjkh	if (Flags & SHOW_DESC)
200379Sjkh	    show_file("Description:\n", DESC_FNAME);
2014996Sjkh	if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
2024996Sjkh	    show_file("Install notice:\n", DISPLAY_FNAME);
203327Sjkh	if (Flags & SHOW_PLIST)
20484745Ssobomax	    show_plist("Packing list:\n", &plist, (plist_t)0, TRUE);
205131280Seik	if (Flags & SHOW_REQUIRE && fexists(REQUIRE_FNAME))
206131280Seik	    show_file("Requirements script:\n", REQUIRE_FNAME);
207327Sjkh	if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
208379Sjkh	    show_file("Install script:\n", INSTALL_FNAME);
20941866Sjkh	if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME))
21041866Sjkh	    show_file("Post-Install script:\n", POST_INSTALL_FNAME);
211327Sjkh	if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
212379Sjkh	    show_file("De-Install script:\n", DEINSTALL_FNAME);
21341866Sjkh	if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME))
21441866Sjkh	    show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME);
2154996Sjkh	if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
2164996Sjkh	    show_file("mtree file:\n", MTREE_FNAME);
217327Sjkh	if (Flags & SHOW_PREFIX)
21884745Ssobomax	    show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE);
219411Sjkh	if (Flags & SHOW_FILES)
220411Sjkh	    show_files("Files:\n", &plist);
22162775Ssobomax	if ((Flags & SHOW_SIZE) && installed)
22262775Ssobomax	    show_size("Package Size:\n", &plist);
22371965Sjkh	if ((Flags & SHOW_CKSUM) && installed)
224241135Sbapt	    code += show_cksum("Mismatched Checksums:\n", &plist);
22567454Ssobomax	if (Flags & SHOW_ORIGIN)
22667454Ssobomax	    show_origin("Origin:\n", &plist);
22784750Ssobomax	if (Flags & SHOW_FMTREV)
22884750Ssobomax	    show_fmtrev("Packing list format revision:\n", &plist);
229411Sjkh	if (!Quiet)
230411Sjkh	    puts(InfoPrefix);
231327Sjkh    }
232327Sjkh    free_plist(&plist);
2338086Sjkh bail:
23433427Sjkh    leave_playpen();
2358086Sjkh    if (isTMP)
2368086Sjkh	unlink(fname);
237241135Sbapt    return (code ? 1 : 0);
238327Sjkh}
239327Sjkh
240327Sjkhvoid
241327Sjkhcleanup(int sig)
242327Sjkh{
24333427Sjkh    static int in_cleanup = 0;
24433427Sjkh
24533427Sjkh    if (!in_cleanup) {
24633427Sjkh	in_cleanup = 1;
24772174Ssobomax	leave_playpen();
24833427Sjkh    }
24939068Sjkh    if (sig)
25039068Sjkh	exit(1);
251327Sjkh}
25249300Sjdp
25374699Ssobomax/*
25474808Ssobomax * Return an absolute path, additionally removing all .'s, ..'s, and extraneous
25574808Ssobomax * /'s, as realpath() would, but without resolving symlinks, because that can
25674808Ssobomax * potentially screw up our comparisons later.
25774808Ssobomax */
25884745Ssobomaxstatic char *
25974808Ssobomaxabspath(const char *pathname)
26074808Ssobomax{
26174808Ssobomax    char *tmp, *tmp1, *resolved_path;
26274808Ssobomax    char *cwd = NULL;
26374808Ssobomax    int len;
26474808Ssobomax
26574808Ssobomax    if (pathname[0] != '/') {
26674808Ssobomax	cwd = getcwd(NULL, MAXPATHLEN);
26774808Ssobomax	asprintf(&resolved_path, "%s/%s/", cwd, pathname);
26874808Ssobomax    } else
26974808Ssobomax	asprintf(&resolved_path, "%s/", pathname);
27074808Ssobomax
27174808Ssobomax    if (resolved_path == NULL)
27274808Ssobomax	errx(2, NULL);
27374808Ssobomax
27474808Ssobomax    if (cwd != NULL)
27574808Ssobomax	free(cwd);
27674808Ssobomax
27774808Ssobomax    while ((tmp = strstr(resolved_path, "//")) != NULL)
27874808Ssobomax	strcpy(tmp, tmp + 1);
27974808Ssobomax
28074808Ssobomax    while ((tmp = strstr(resolved_path, "/./")) != NULL)
28174808Ssobomax	strcpy(tmp, tmp + 2);
28274808Ssobomax
28374808Ssobomax    while ((tmp = strstr(resolved_path, "/../")) != NULL) {
28474808Ssobomax	*tmp = '\0';
28574808Ssobomax	if ((tmp1 = strrchr(resolved_path, '/')) == NULL)
28674808Ssobomax	   tmp1 = resolved_path;
28774808Ssobomax	strcpy(tmp1, tmp + 3);
28874808Ssobomax    }
28974808Ssobomax
29074808Ssobomax    len = strlen(resolved_path);
29174808Ssobomax    if (len > 1 && resolved_path[len - 1] == '/')
29274808Ssobomax	resolved_path[len - 1] = '\0';
29374808Ssobomax
29474808Ssobomax    return resolved_path;
29574808Ssobomax}
29674808Ssobomax
29774808Ssobomax/*
29874699Ssobomax * Comparison to see if the path we're on matches the
29974699Ssobomax * one we are looking for.
30074699Ssobomax */
30174699Ssobomaxstatic int
30274699Ssobomaxcmp_path(const char *target, const char *current, const char *cwd)
30374699Ssobomax{
30474808Ssobomax    char *resolved, *temp;
30574699Ssobomax    int rval;
30674699Ssobomax
30774699Ssobomax    asprintf(&temp, "%s/%s", cwd, current);
30874699Ssobomax    if (temp == NULL)
30974699Ssobomax        errx(2, NULL);
31074699Ssobomax
31174699Ssobomax    /*
31274808Ssobomax     * Make sure there's no multiple /'s or other weird things in the PLIST,
31374808Ssobomax     * since some plists seem to have them and it could screw up our strncmp.
31474699Ssobomax     */
31574808Ssobomax    resolved = abspath(temp);
31674699Ssobomax
31774808Ssobomax    if (strcmp(target, resolved) == 0)
31874699Ssobomax	rval = 1;
31974699Ssobomax    else
32074699Ssobomax	rval = 0;
32174699Ssobomax
32274699Ssobomax    free(temp);
32374808Ssobomax    free(resolved);
32474699Ssobomax    return rval;
32574699Ssobomax}
32674699Ssobomax
32774699Ssobomax/*
32896613Ssobomax * Look through package dbs in LOG_DIR and find which
32974699Ssobomax * packages installed the files in which_list.
33074699Ssobomax */
33174699Ssobomaxstatic int
33296613Ssobomaxfind_pkg(struct which_head *which_list)
33374699Ssobomax{
33474699Ssobomax    char **installed;
33574699Ssobomax    int errcode, i;
33674699Ssobomax    struct which_entry *wp;
33774699Ssobomax
33874699Ssobomax    TAILQ_FOREACH(wp, which_list, next) {
33984745Ssobomax	const char *msg = "file cannot be found";
34074808Ssobomax	char *tmp;
34174808Ssobomax
34274808Ssobomax	wp->skip = TRUE;
34374699Ssobomax	/* If it's not a file, we'll see if it's an executable. */
34474699Ssobomax	if (isfile(wp->file) == FALSE) {
34574699Ssobomax	    if (strchr(wp->file, '/') == NULL) {
34674699Ssobomax		tmp = vpipe("/usr/bin/which %s", wp->file);
34774808Ssobomax		if (tmp != NULL) {
34874808Ssobomax		    strlcpy(wp->file, tmp, PATH_MAX);
34974808Ssobomax		    wp->skip = FALSE;
35074808Ssobomax		    free(tmp);
35174699Ssobomax		} else
35274808Ssobomax		    msg = "file is not in PATH";
35374699Ssobomax	    }
35474808Ssobomax	} else {
35574808Ssobomax	    tmp = abspath(wp->file);
35674808Ssobomax	    if (isfile(tmp)) {
35774808Ssobomax	    	strlcpy(wp->file, tmp, PATH_MAX);
35874808Ssobomax	    	wp->skip = FALSE;
35974808Ssobomax	    }
36074699Ssobomax	    free(tmp);
36174699Ssobomax	}
36274808Ssobomax	if (wp->skip == TRUE)
36374808Ssobomax	    warnx("%s: %s", wp->file, msg);
36474699Ssobomax    }
36574699Ssobomax
36674699Ssobomax    installed = matchinstalled(MATCH_ALL, NULL, &errcode);
36774699Ssobomax    if (installed == NULL)
36874699Ssobomax        return errcode;
36974699Ssobomax
37074699Ssobomax    for (i = 0; installed[i] != NULL; i++) {
37174699Ssobomax     	FILE *fp;
37274699Ssobomax     	Package pkg;
37374699Ssobomax     	PackingList itr;
37474699Ssobomax     	char *cwd = NULL;
37574699Ssobomax     	char tmp[PATH_MAX];
37674699Ssobomax
37796613Ssobomax	snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i],
37874699Ssobomax		 CONTENTS_FNAME);
37974699Ssobomax	fp = fopen(tmp, "r");
38074699Ssobomax	if (fp == NULL) {
38174699Ssobomax	    warn("%s", tmp);
38274699Ssobomax	    return 1;
38374699Ssobomax	}
38474699Ssobomax
38574699Ssobomax	pkg.head = pkg.tail = NULL;
38674699Ssobomax	read_plist(&pkg, fp);
38774699Ssobomax	fclose(fp);
38874699Ssobomax	for (itr = pkg.head; itr != pkg.tail; itr = itr->next) {
38974699Ssobomax	    if (itr->type == PLIST_CWD) {
39074699Ssobomax		cwd = itr->name;
39174699Ssobomax	    } else if (itr->type == PLIST_FILE) {
39274699Ssobomax		TAILQ_FOREACH(wp, which_list, next) {
39374699Ssobomax		    if (wp->skip == TRUE)
39474699Ssobomax			continue;
39574699Ssobomax		    if (!cmp_path(wp->file, itr->name, cwd))
39674699Ssobomax			continue;
39774699Ssobomax		    if (wp->package[0] != '\0') {
39874809Ssobomax			warnx("both %s and %s claim to have installed %s\n",
39974699Ssobomax			      wp->package, installed[i], wp->file);
40074699Ssobomax		    } else {
40174699Ssobomax			strlcpy(wp->package, installed[i], PATH_MAX);
40274699Ssobomax		    }
40374699Ssobomax		}
40474699Ssobomax	    }
40574699Ssobomax	}
40674699Ssobomax	free_plist(&pkg);
40774699Ssobomax    }
40874699Ssobomax
40974699Ssobomax    TAILQ_FOREACH(wp, which_list, next) {
41074699Ssobomax	if (wp->package[0] != '\0') {
41174699Ssobomax	    if (Quiet)
41274699Ssobomax		puts(wp->package);
41374699Ssobomax	    else
41474699Ssobomax		printf("%s was installed by package %s\n", \
41574699Ssobomax		       wp->file, wp->package);
41674699Ssobomax	}
41774699Ssobomax    }
41874699Ssobomax    while (!TAILQ_EMPTY(which_list)) {
41974699Ssobomax	wp = TAILQ_FIRST(which_list);
42074699Ssobomax	TAILQ_REMOVE(which_list, wp, next);
42174699Ssobomax	free(wp);
42274699Ssobomax    }
42374699Ssobomax
42474699Ssobomax    free(which_list);
42574699Ssobomax    return 0;
42674699Ssobomax}
42796030Ssobomax
42896030Ssobomax/*
42996613Ssobomax * Look through package dbs in LOG_DIR and find which
43096030Ssobomax * packages have the given origin. Don't use read_plist()
43196030Ssobomax * because this increases time necessary for lookup by 40
43296030Ssobomax * times, as we don't really have to parse all plist to
43396030Ssobomax * get origin.
43496030Ssobomax */
43596030Ssobomaxstatic int
43696613Ssobomaxfind_pkgs_by_origin(const char *origin)
43796030Ssobomax{
43896613Ssobomax    char **matched;
43996030Ssobomax    int errcode, i;
44096030Ssobomax
44196030Ssobomax    if (!Quiet)
44296030Ssobomax	printf("The following installed package(s) has %s origin:\n", origin);
44396030Ssobomax
44496613Ssobomax    matched = matchbyorigin(origin, &errcode);
44596613Ssobomax    if (matched == NULL)
44696613Ssobomax	return errcode;
44796030Ssobomax
44896613Ssobomax    for (i = 0; matched[i] != NULL; i++)
44996613Ssobomax	puts(matched[i]);
45096030Ssobomax
45196030Ssobomax    return 0;
45296030Ssobomax}
453131275Seik
454131275Seik/*
455131275Seik * List only the matching package names.
456131275Seik * Mainly intended for scripts.
457131275Seik */
458131275Seikstatic int
459131275Seikmatched_packages(char **pkgs)
460131275Seik{
461131275Seik    char **matched;
462131275Seik    int i, errcode;
463131275Seik
464131275Seik    matched = matchinstalled(MatchType == MATCH_GLOB ? MATCH_NGLOB : MatchType, pkgs, &errcode);
465131275Seik
466131275Seik    if (errcode != 0 || matched == NULL)
467131275Seik	return 1;
468131275Seik
469131275Seik    for (i = 0; matched[i]; i++)
470131275Seik	if (!Quiet)
471131275Seik	    printf("%s\n", matched[i]);
472131275Seik	else if (QUIET)
473131275Seik	    printf("%s%s\n", InfoPrefix, matched[i]);
474131275Seik
475131275Seik    return 0;
476131275Seik}
477