Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / pkg_install / info / perform.c
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: src/usr.sbin/pkg_install/info/perform.c,v 1.29.2.14 2002/10/11 14:23:43 sobomax Exp $");
23
24 #include "lib.h"
25 #include "info.h"
26 #include <err.h>
27 #include <signal.h>
28
29 static int pkg_do(char *);
30 static int find_pkg(struct which_head *);
31 static int cmp_path(const char *, const char *, const char *);
32 static char *abspath(const char *);
33 static int find_pkgs_by_origin(const char *);
34
35 int
36 pkg_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
84 static char *Home;
85
86 static int
87 pkg_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         if (Flags & SHOW_COMMENT)
195             show_file("Comment:\n", COMMENT_FNAME);
196         if (Flags & SHOW_REQUIRE)
197             show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE);
198         if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
199             show_file("Required by:\n", REQUIRED_BY_FNAME);
200         if (Flags & SHOW_DESC)
201             show_file("Description:\n", DESC_FNAME);
202         if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
203             show_file("Install notice:\n", DISPLAY_FNAME);
204         if (Flags & SHOW_PLIST)
205             show_plist("Packing list:\n", &plist, (plist_t)0, TRUE);
206         if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
207             show_file("Install script:\n", INSTALL_FNAME);
208         if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME))
209             show_file("Post-Install script:\n", POST_INSTALL_FNAME);
210         if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
211             show_file("De-Install script:\n", DEINSTALL_FNAME);
212         if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME))
213             show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME);
214         if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
215             show_file("mtree file:\n", MTREE_FNAME);
216         if (Flags & SHOW_PREFIX)
217             show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE);
218         if (Flags & SHOW_FILES)
219             show_files("Files:\n", &plist);
220         if ((Flags & SHOW_SIZE) && installed)
221             show_size("Package Size:\n", &plist);
222         if ((Flags & SHOW_CKSUM) && installed)
223             show_cksum("Mismatched Checksums:\n", &plist);
224         if (Flags & SHOW_ORIGIN)
225             show_origin("Origin:\n", &plist);
226         if (Flags & SHOW_FMTREV)
227             show_fmtrev("Packing list format revision:\n", &plist);
228         if (!Quiet)
229             puts(InfoPrefix);
230     }
231     free_plist(&plist);
232  bail:
233     leave_playpen();
234     if (isTMP)
235         unlink(fname);
236     return code;
237 }
238
239 void
240 cleanup(int sig)
241 {
242     static int in_cleanup = 0;
243
244     if (!in_cleanup) {
245         in_cleanup = 1;
246         leave_playpen();
247     }
248     if (sig)
249         exit(1);
250 }
251
252 /*
253  * Return an absolute path, additionally removing all .'s, ..'s, and extraneous
254  * /'s, as realpath() would, but without resolving symlinks, because that can
255  * potentially screw up our comparisons later.
256  */
257 static char *
258 abspath(const char *pathname)
259 {
260     char *tmp, *tmp1, *resolved_path;
261     char *cwd = NULL;
262     int len;
263
264     if (pathname[0] != '/') {
265         cwd = getcwd(NULL, MAXPATHLEN);
266         asprintf(&resolved_path, "%s/%s/", cwd, pathname);
267     } else
268         asprintf(&resolved_path, "%s/", pathname);
269
270     if (resolved_path == NULL)
271         errx(2, NULL);
272
273     if (cwd != NULL)
274         free(cwd);    
275
276     while ((tmp = strstr(resolved_path, "//")) != NULL)
277         strcpy(tmp, tmp + 1);
278  
279     while ((tmp = strstr(resolved_path, "/./")) != NULL)
280         strcpy(tmp, tmp + 2);
281  
282     while ((tmp = strstr(resolved_path, "/../")) != NULL) {
283         *tmp = '\0';
284         if ((tmp1 = strrchr(resolved_path, '/')) == NULL)
285            tmp1 = resolved_path;
286         strcpy(tmp1, tmp + 3);
287     }
288
289     len = strlen(resolved_path);
290     if (len > 1 && resolved_path[len - 1] == '/')
291         resolved_path[len - 1] = '\0';
292
293     return resolved_path;
294 }
295
296 /*
297  * Comparison to see if the path we're on matches the
298  * one we are looking for.
299  */
300 static int
301 cmp_path(const char *target, const char *current, const char *cwd) 
302 {
303     char *resolved, *temp;
304     int rval;
305
306     asprintf(&temp, "%s/%s", cwd, current);
307     if (temp == NULL)
308         errx(2, NULL);
309
310     /*
311      * Make sure there's no multiple /'s or other weird things in the PLIST,
312      * since some plists seem to have them and it could screw up our strncmp.
313      */
314     resolved = abspath(temp);
315
316     if (strcmp(target, resolved) == 0)
317         rval = 1;
318     else
319         rval = 0;
320
321     free(temp);
322     free(resolved);
323     return rval;
324 }
325
326 /* 
327  * Look through package dbs in LOG_DIR and find which
328  * packages installed the files in which_list.
329  */
330 static int 
331 find_pkg(struct which_head *which_list)
332 {
333     char **installed;
334     int errcode, i;
335     struct which_entry *wp;
336
337     TAILQ_FOREACH(wp, which_list, next) {
338         const char *msg = "file cannot be found";
339         char *tmp;
340
341         wp->skip = TRUE;
342         /* If it's not a file, we'll see if it's an executable. */
343         if (isfile(wp->file) == FALSE) {
344             if (strchr(wp->file, '/') == NULL) {
345                 tmp = vpipe("/usr/bin/which %s", wp->file);
346                 if (tmp != NULL) {
347                     strlcpy(wp->file, tmp, PATH_MAX);
348                     wp->skip = FALSE;
349                     free(tmp);
350                 } else
351                     msg = "file is not in PATH";
352             }
353         } else {
354             tmp = abspath(wp->file);
355             if (isfile(tmp)) {
356                 strlcpy(wp->file, tmp, PATH_MAX);
357                 wp->skip = FALSE;
358             }
359             free(tmp);
360         }
361         if (wp->skip == TRUE)
362             warnx("%s: %s", wp->file, msg);
363     }
364
365     installed = matchinstalled(MATCH_ALL, NULL, &errcode);
366     if (installed == NULL)
367         return errcode;
368  
369     for (i = 0; installed[i] != NULL; i++) {
370         FILE *fp;
371         Package pkg;
372         PackingList itr;
373         char *cwd = NULL;
374         char tmp[PATH_MAX];
375
376         snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i],
377                  CONTENTS_FNAME);
378         fp = fopen(tmp, "r");
379         if (fp == NULL) {
380             warn("%s", tmp);
381             return 1;
382         }
383
384         pkg.head = pkg.tail = NULL;
385         read_plist(&pkg, fp);
386         fclose(fp);
387         for (itr = pkg.head; itr != pkg.tail; itr = itr->next) {
388             if (itr->type == PLIST_CWD) {
389                 cwd = itr->name;
390             } else if (itr->type == PLIST_FILE) {
391                 TAILQ_FOREACH(wp, which_list, next) {
392                     if (wp->skip == TRUE)
393                         continue;
394                     if (!cmp_path(wp->file, itr->name, cwd))
395                         continue;
396                     if (wp->package[0] != '\0') {
397                         warnx("both %s and %s claim to have installed %s\n",
398                               wp->package, installed[i], wp->file);
399                     } else {
400                         strlcpy(wp->package, installed[i], PATH_MAX);
401                     }
402                 }
403             }
404         }
405         free_plist(&pkg);
406     }
407
408     TAILQ_FOREACH(wp, which_list, next) {
409         if (wp->package[0] != '\0') {
410             if (Quiet)
411                 puts(wp->package);
412             else
413                 printf("%s was installed by package %s\n", \
414                        wp->file, wp->package);
415         }
416     }
417     while (!TAILQ_EMPTY(which_list)) {
418         wp = TAILQ_FIRST(which_list);
419         TAILQ_REMOVE(which_list, wp, next);
420         free(wp);
421     }
422
423     free(which_list);
424     return 0;
425 }
426
427 /* 
428  * Look through package dbs in LOG_DIR and find which
429  * packages have the given origin. Don't use read_plist()
430  * because this increases time necessary for lookup by 40
431  * times, as we don't really have to parse all plist to
432  * get origin.
433  */
434 static int 
435 find_pkgs_by_origin(const char *origin)
436 {
437     char **matched;
438     int errcode, i;
439
440     if (!Quiet)
441         printf("The following installed package(s) has %s origin:\n", origin);
442
443     matched = matchbyorigin(origin, &errcode);
444     if (matched == NULL)
445         return errcode;
446
447     for (i = 0; matched[i] != NULL; i++)
448         puts(matched[i]);
449
450     return 0;
451 }