perform.c revision 112572
1/*
2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * Jordan K. Hubbard
15 * 23 Aug 1993
16 *
17 * This is the main body of the info module.
18 *
19 */
20
21#include <sys/cdefs.h>
22__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/info/perform.c 112572 2003-03-25 00:51:41Z mdodd $");
23
24#include "lib.h"
25#include "info.h"
26#include <err.h>
27#include <signal.h>
28
29static int pkg_do(char *);
30static int find_pkg(struct which_head *);
31static int cmp_path(const char *, const char *, const char *);
32static char *abspath(const char *);
33static int find_pkgs_by_origin(const char *);
34
35int
36pkg_perform(char **pkgs)
37{
38    char **matched;
39    int err_cnt = 0;
40    int i, errcode;
41
42    signal(SIGINT, cleanup);
43
44    /* Overriding action? */
45    if (CheckPkg) {
46	return isinstalledpkg(CheckPkg) == TRUE ? 0 : 1;
47	/* Not reached */
48    } else if (!TAILQ_EMPTY(whead)) {
49	return find_pkg(whead);
50    } else if (LookUpOrigin != NULL) {
51	return find_pkgs_by_origin(LookUpOrigin);
52    }
53
54    if (MatchType != MATCH_EXACT) {
55	matched = matchinstalled(MatchType, pkgs, &errcode);
56	if (errcode != 0)
57	    return 1;
58	    /* Not reached */
59
60	if (matched != NULL)
61	    pkgs = matched;
62	else switch (MatchType) {
63	    case MATCH_GLOB:
64		break;
65	    case MATCH_ALL:
66		warnx("no packages installed");
67		return 0;
68		/* Not reached */
69	    case MATCH_REGEX:
70		warnx("no packages match pattern(s)");
71		return 1;
72		/* Not reached */
73	    default:
74		break;
75	}
76    }
77
78    for (i = 0; pkgs[i]; i++)
79	err_cnt += pkg_do(pkgs[i]);
80
81    return err_cnt;
82}
83
84static char *Home;
85
86static int
87pkg_do(char *pkg)
88{
89    Boolean installed = FALSE, isTMP = FALSE;
90    char log_dir[FILENAME_MAX];
91    char fname[FILENAME_MAX], extrlist[FILENAME_MAX];
92    Package plist;
93    FILE *fp;
94    struct stat sb;
95    char *cp = NULL;
96    int code = 0;
97
98    if (isURL(pkg)) {
99	if ((cp = fileGetURL(NULL, pkg)) != NULL) {
100	    strcpy(fname, cp);
101	    isTMP = TRUE;
102	}
103    }
104    else if (fexists(pkg) && isfile(pkg)) {
105	int len;
106
107	if (*pkg != '/') {
108	    if (!getcwd(fname, FILENAME_MAX))
109		upchuck("getcwd");
110	    len = strlen(fname);
111	    snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg);
112	}
113	else
114	    strcpy(fname, pkg);
115	cp = fname;
116    }
117    else {
118	if ((cp = fileFindByPath(NULL, pkg)) != NULL)
119	    strncpy(fname, cp, FILENAME_MAX);
120    }
121    if (cp) {
122	/*
123	 * Apply a crude heuristic to see how much space the package will
124	 * take up once it's unpacked.  I've noticed that most packages
125	 * compress an average of 75%, but we're only unpacking the + files so
126	 * be very optimistic.
127	 */
128	if (stat(fname, &sb) == FAIL) {
129	    warnx("can't stat package file '%s'", fname);
130	    code = 1;
131	    goto bail;
132	}
133	Home = make_playpen(PlayPen, sb.st_size / 2);
134	snprintf(extrlist, sizeof(extrlist), "--fast-read %s %s %s",
135		 CONTENTS_FNAME, COMMENT_FNAME, DESC_FNAME);
136	if (Flags & SHOW_DISPLAY)
137	    snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist,
138		     DISPLAY_FNAME);
139	if (Flags & SHOW_INSTALL)
140	    snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist,
141		     INSTALL_FNAME, POST_INSTALL_FNAME);
142	if (Flags & SHOW_DEINSTALL)
143	    snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist,
144		     DEINSTALL_FNAME, POST_DEINSTALL_FNAME);
145	if (Flags & SHOW_MTREE)
146	    snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist,
147		     MTREE_FNAME);
148	if (unpack(fname, extrlist)) {
149	    warnx("error during unpacking, no info for '%s' available", pkg);
150	    code = 1;
151	    goto bail;
152	}
153    }
154    /* It's not an ininstalled package, try and find it among the installed */
155    else {
156	if (!isinstalledpkg(pkg)) {
157	    warnx("can't find package '%s' installed or in a file!", pkg);
158	    return 1;
159	}
160	sprintf(log_dir, "%s/%s", LOG_DIR, pkg);
161	if (chdir(log_dir) == FAIL) {
162	    warnx("can't change directory to '%s'!", log_dir);
163	    return 1;
164	}
165	installed = TRUE;
166    }
167
168    /* Suck in the contents list */
169    plist.head = plist.tail = NULL;
170    fp = fopen(CONTENTS_FNAME, "r");
171    if (!fp) {
172	warnx("unable to open %s file", CONTENTS_FNAME);
173	code = 1;
174	goto bail;
175    }
176    /* If we have a prefix, add it now */
177    read_plist(&plist, fp);
178    fclose(fp);
179
180    /*
181     * Index is special info type that has to override all others to make
182     * any sense.
183     */
184    if (Flags & SHOW_INDEX) {
185	char tmp[FILENAME_MAX];
186
187	snprintf(tmp, FILENAME_MAX, "%-19s ", pkg);
188	show_index(tmp, COMMENT_FNAME);
189    }
190    else {
191	/* Start showing the package contents */
192	if (!Quiet)
193	    printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
194	else if (QUIET)
195	    printf("%s%s:", InfoPrefix, pkg);
196	if (Flags & SHOW_COMMENT)
197	    show_file("Comment:\n", COMMENT_FNAME);
198	if (Flags & SHOW_REQUIRE)
199	    show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE);
200	if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
201	    show_file("Required by:\n", REQUIRED_BY_FNAME);
202	if (Flags & SHOW_DESC)
203	    show_file("Description:\n", DESC_FNAME);
204	if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
205	    show_file("Install notice:\n", DISPLAY_FNAME);
206	if (Flags & SHOW_PLIST)
207	    show_plist("Packing list:\n", &plist, (plist_t)0, TRUE);
208	if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
209	    show_file("Install script:\n", INSTALL_FNAME);
210	if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME))
211	    show_file("Post-Install script:\n", POST_INSTALL_FNAME);
212	if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
213	    show_file("De-Install script:\n", DEINSTALL_FNAME);
214	if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME))
215	    show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME);
216	if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
217	    show_file("mtree file:\n", MTREE_FNAME);
218	if (Flags & SHOW_PREFIX)
219	    show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE);
220	if (Flags & SHOW_FILES)
221	    show_files("Files:\n", &plist);
222	if ((Flags & SHOW_SIZE) && installed)
223	    show_size("Package Size:\n", &plist);
224	if ((Flags & SHOW_CKSUM) && installed)
225	    show_cksum("Mismatched Checksums:\n", &plist);
226	if (Flags & SHOW_ORIGIN)
227	    show_origin("Origin:\n", &plist);
228	if (Flags & SHOW_FMTREV)
229	    show_fmtrev("Packing list format revision:\n", &plist);
230	if (!Quiet)
231	    puts(InfoPrefix);
232    }
233    free_plist(&plist);
234 bail:
235    leave_playpen();
236    if (isTMP)
237	unlink(fname);
238    return code;
239}
240
241void
242cleanup(int sig)
243{
244    static int in_cleanup = 0;
245
246    if (!in_cleanup) {
247	in_cleanup = 1;
248	leave_playpen();
249    }
250    if (sig)
251	exit(1);
252}
253
254/*
255 * Return an absolute path, additionally removing all .'s, ..'s, and extraneous
256 * /'s, as realpath() would, but without resolving symlinks, because that can
257 * potentially screw up our comparisons later.
258 */
259static char *
260abspath(const char *pathname)
261{
262    char *tmp, *tmp1, *resolved_path;
263    char *cwd = NULL;
264    int len;
265
266    if (pathname[0] != '/') {
267	cwd = getcwd(NULL, MAXPATHLEN);
268	asprintf(&resolved_path, "%s/%s/", cwd, pathname);
269    } else
270	asprintf(&resolved_path, "%s/", pathname);
271
272    if (resolved_path == NULL)
273	errx(2, NULL);
274
275    if (cwd != NULL)
276	free(cwd);
277
278    while ((tmp = strstr(resolved_path, "//")) != NULL)
279	strcpy(tmp, tmp + 1);
280
281    while ((tmp = strstr(resolved_path, "/./")) != NULL)
282	strcpy(tmp, tmp + 2);
283
284    while ((tmp = strstr(resolved_path, "/../")) != NULL) {
285	*tmp = '\0';
286	if ((tmp1 = strrchr(resolved_path, '/')) == NULL)
287	   tmp1 = resolved_path;
288	strcpy(tmp1, tmp + 3);
289    }
290
291    len = strlen(resolved_path);
292    if (len > 1 && resolved_path[len - 1] == '/')
293	resolved_path[len - 1] = '\0';
294
295    return resolved_path;
296}
297
298/*
299 * Comparison to see if the path we're on matches the
300 * one we are looking for.
301 */
302static int
303cmp_path(const char *target, const char *current, const char *cwd)
304{
305    char *resolved, *temp;
306    int rval;
307
308    asprintf(&temp, "%s/%s", cwd, current);
309    if (temp == NULL)
310        errx(2, NULL);
311
312    /*
313     * Make sure there's no multiple /'s or other weird things in the PLIST,
314     * since some plists seem to have them and it could screw up our strncmp.
315     */
316    resolved = abspath(temp);
317
318    if (strcmp(target, resolved) == 0)
319	rval = 1;
320    else
321	rval = 0;
322
323    free(temp);
324    free(resolved);
325    return rval;
326}
327
328/*
329 * Look through package dbs in LOG_DIR and find which
330 * packages installed the files in which_list.
331 */
332static int
333find_pkg(struct which_head *which_list)
334{
335    char **installed;
336    int errcode, i;
337    struct which_entry *wp;
338
339    TAILQ_FOREACH(wp, which_list, next) {
340	const char *msg = "file cannot be found";
341	char *tmp;
342
343	wp->skip = TRUE;
344	/* If it's not a file, we'll see if it's an executable. */
345	if (isfile(wp->file) == FALSE) {
346	    if (strchr(wp->file, '/') == NULL) {
347		tmp = vpipe("/usr/bin/which %s", wp->file);
348		if (tmp != NULL) {
349		    strlcpy(wp->file, tmp, PATH_MAX);
350		    wp->skip = FALSE;
351		    free(tmp);
352		} else
353		    msg = "file is not in PATH";
354	    }
355	} else {
356	    tmp = abspath(wp->file);
357	    if (isfile(tmp)) {
358	    	strlcpy(wp->file, tmp, PATH_MAX);
359	    	wp->skip = FALSE;
360	    }
361	    free(tmp);
362	}
363	if (wp->skip == TRUE)
364	    warnx("%s: %s", wp->file, msg);
365    }
366
367    installed = matchinstalled(MATCH_ALL, NULL, &errcode);
368    if (installed == NULL)
369        return errcode;
370
371    for (i = 0; installed[i] != NULL; i++) {
372     	FILE *fp;
373     	Package pkg;
374     	PackingList itr;
375     	char *cwd = NULL;
376     	char tmp[PATH_MAX];
377
378	snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i],
379		 CONTENTS_FNAME);
380	fp = fopen(tmp, "r");
381	if (fp == NULL) {
382	    warn("%s", tmp);
383	    return 1;
384	}
385
386	pkg.head = pkg.tail = NULL;
387	read_plist(&pkg, fp);
388	fclose(fp);
389	for (itr = pkg.head; itr != pkg.tail; itr = itr->next) {
390	    if (itr->type == PLIST_CWD) {
391		cwd = itr->name;
392	    } else if (itr->type == PLIST_FILE) {
393		TAILQ_FOREACH(wp, which_list, next) {
394		    if (wp->skip == TRUE)
395			continue;
396		    if (!cmp_path(wp->file, itr->name, cwd))
397			continue;
398		    if (wp->package[0] != '\0') {
399			warnx("both %s and %s claim to have installed %s\n",
400			      wp->package, installed[i], wp->file);
401		    } else {
402			strlcpy(wp->package, installed[i], PATH_MAX);
403		    }
404		}
405	    }
406	}
407	free_plist(&pkg);
408    }
409
410    TAILQ_FOREACH(wp, which_list, next) {
411	if (wp->package[0] != '\0') {
412	    if (Quiet)
413		puts(wp->package);
414	    else
415		printf("%s was installed by package %s\n", \
416		       wp->file, wp->package);
417	}
418    }
419    while (!TAILQ_EMPTY(which_list)) {
420	wp = TAILQ_FIRST(which_list);
421	TAILQ_REMOVE(which_list, wp, next);
422	free(wp);
423    }
424
425    free(which_list);
426    return 0;
427}
428
429/*
430 * Look through package dbs in LOG_DIR and find which
431 * packages have the given origin. Don't use read_plist()
432 * because this increases time necessary for lookup by 40
433 * times, as we don't really have to parse all plist to
434 * get origin.
435 */
436static int
437find_pkgs_by_origin(const char *origin)
438{
439    char **matched;
440    int errcode, i;
441
442    if (!Quiet)
443	printf("The following installed package(s) has %s origin:\n", origin);
444
445    matched = matchbyorigin(origin, &errcode);
446    if (matched == NULL)
447	return errcode;
448
449    for (i = 0; matched[i] != NULL; i++)
450	puts(matched[i]);
451
452    return 0;
453}
454