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