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