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