perform.c revision 74809
113397Solagneau#ifndef lint
213397Solagneaustatic const char rcsid[] =
313397Solagneau  "$FreeBSD: head/usr.sbin/pkg_install/info/perform.c 74809 2001-03-26 09:57:26Z sobomax $";
413397Solagneau#endif
513397Solagneau
613397Solagneau/*
713397Solagneau * FreeBSD install - a package for the installation and maintainance
813397Solagneau * of non-core utilities.
913397Solagneau *
1013397Solagneau * Redistribution and use in source and binary forms, with or without
1113397Solagneau * modification, are permitted provided that the following conditions
1213397Solagneau * are met:
1313397Solagneau * 1. Redistributions of source code must retain the above copyright
1413397Solagneau *    notice, this list of conditions and the following disclaimer.
1513397Solagneau * 2. Redistributions in binary form must reproduce the above copyright
1613397Solagneau *    notice, this list of conditions and the following disclaimer in the
1713397Solagneau *    documentation and/or other materials provided with the distribution.
1813397Solagneau *
1913397Solagneau * Jordan K. Hubbard
2013397Solagneau * 23 Aug 1993
2113397Solagneau *
2213397Solagneau * This is the main body of the info module.
2313397Solagneau *
2413397Solagneau */
2513397Solagneau
2613397Solagneau#include "lib.h"
2713397Solagneau#include "info.h"
2813397Solagneau#include <err.h>
2913397Solagneau#include <signal.h>
3013397Solagneau
3113397Solagneaustatic int pkg_do(char *);
3213397Solagneaustatic int find_pkg(char *, struct which_head *);
3313397Solagneaustatic int cmp_path(const char *, const char *, const char *);
3413397Solagneau
3513397Solagneauint
3613397Solagneaupkg_perform(char **pkgs)
3713397Solagneau{
3813397Solagneau    char **matched;
3913397Solagneau    char *tmp;
4013397Solagneau    int err_cnt = 0;
4113397Solagneau    int i, errcode;
4213397Solagneau
4313397Solagneau    signal(SIGINT, cleanup);
4413397Solagneau
4513397Solagneau    tmp = getenv(PKG_DBDIR);
4613397Solagneau    if (!tmp)
4713397Solagneau	tmp = DEF_LOG_DIR;
4813397Solagneau
4913397Solagneau    /* Overriding action? */
5013397Solagneau    if (CheckPkg) {
5113397Solagneau	char buf[FILENAME_MAX];
5213397Solagneau
5313397Solagneau	snprintf(buf, FILENAME_MAX, "%s/%s", tmp, CheckPkg);
5413397Solagneau	return abs(access(buf, R_OK));
5513397Solagneau	/* Not reached */
5613397Solagneau    } else if (!TAILQ_EMPTY(whead)) {
5713397Solagneau	return find_pkg(tmp, whead);
5813397Solagneau    }
5913397Solagneau
6013397Solagneau    if (MatchType != MATCH_EXACT) {
6113397Solagneau	matched = matchinstalled(MatchType, pkgs, &errcode);
6213397Solagneau	if (errcode != 0)
6313397Solagneau	    return 1;
6413397Solagneau	    /* Not reached */
6513397Solagneau
6613397Solagneau	if (matched != NULL)
6713397Solagneau	    pkgs = matched;
6813397Solagneau	else switch (MatchType) {
6913397Solagneau	    case MATCH_GLOB:
7013397Solagneau		break;
7113397Solagneau	    case MATCH_ALL:
7213397Solagneau		warnx("no packages installed");
7313397Solagneau		return 0;
7413397Solagneau		/* Not reached */
7513397Solagneau	    case MATCH_REGEX:
7613397Solagneau		warnx("no packages match pattern(s)");
7713397Solagneau		return 1;
7813397Solagneau		/* Not reached */
7913397Solagneau	    default:
8013397Solagneau		break;
8113397Solagneau	}
8213397Solagneau    }
8313397Solagneau
8413397Solagneau    for (i = 0; pkgs[i]; i++)
8513397Solagneau	err_cnt += pkg_do(pkgs[i]);
8613397Solagneau
8713397Solagneau    return err_cnt;
8813397Solagneau}
8913397Solagneau
9013397Solagneaustatic char *Home;
9113397Solagneau
9213397Solagneaustatic int
9313397Solagneaupkg_do(char *pkg)
9413397Solagneau{
9513397Solagneau    Boolean installed = FALSE, isTMP = FALSE;
9613397Solagneau    char log_dir[FILENAME_MAX];
9713397Solagneau    char fname[FILENAME_MAX];
9813397Solagneau    Package plist;
9913397Solagneau    FILE *fp;
10013397Solagneau    struct stat sb;
10113397Solagneau    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