Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / pkg_install / add / 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  * 18 July 1993
16  *
17  * This is the main body of the add module.
18  *
19  */
20
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD: src/usr.sbin/pkg_install/add/perform.c,v 1.57.2.15 2002/09/25 23:22:13 bmah Exp $");
23
24 #include <err.h>
25 #include <paths.h>
26 #include "lib.h"
27 #include "add.h"
28
29 #include <libgen.h>
30 #include <signal.h>
31 #include <sys/wait.h>
32
33 static int pkg_do(char *);
34 static int sanity_check(char *);
35 static char LogDir[FILENAME_MAX];
36 static int zapLogDir;           /* Should we delete LogDir? */
37
38 int
39 pkg_perform(char **pkgs)
40 {
41     int i, err_cnt = 0;
42
43     signal(SIGINT, cleanup);
44     signal(SIGHUP, cleanup);
45
46     if (AddMode == SLAVE)
47         err_cnt = pkg_do(NULL);
48     else {
49         for (i = 0; pkgs[i]; i++)
50             err_cnt += pkg_do(pkgs[i]);
51     }
52     return err_cnt;
53 }
54
55 static Package Plist;
56 static char *Home;
57
58 /*
59  * This is seriously ugly code following.  Written very fast!
60  * [And subsequently made even worse..  Sigh!  This code was just born
61  * to be hacked, I guess.. :) -jkh]
62  */
63 static int
64 pkg_do(char *pkg)
65 {
66     char pkg_fullname[FILENAME_MAX];
67     char playpen[FILENAME_MAX];
68     char extract_contents[FILENAME_MAX];
69     char *where_to, *extract;
70     FILE *cfile;
71     int code;
72     PackingList p;
73     struct stat sb;
74     int inPlace;
75     /* support for separate pre/post install scripts */
76     int new_m = 0;
77     char pre_script[FILENAME_MAX] = INSTALL_FNAME;
78     char post_script[FILENAME_MAX];
79     char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX];
80
81     code = 0;
82     zapLogDir = 0;
83     LogDir[0] = '\0';
84     strcpy(playpen, FirstPen);
85     inPlace = 0;
86
87     /* Are we coming in for a second pass, everything already extracted? */
88     if (!pkg) {
89         fgets(playpen, FILENAME_MAX, stdin);
90         playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */
91         if (chdir(playpen) == FAIL) {
92             warnx("pkg_add in SLAVE mode can't chdir to %s", playpen);
93             return 1;
94         }
95         read_plist(&Plist, stdin);
96         where_to = playpen;
97     }
98     /* Nope - do it now */
99     else {
100         /* Is it an ftp://foo.bar.baz/file.t[bg]z specification? */
101         if (isURL(pkg)) {
102             if (!(Home = fileGetURL(NULL, pkg))) {
103                 warnx("unable to fetch '%s' by URL", pkg);
104                 return 1;
105             }
106             where_to = Home;
107             strcpy(pkg_fullname, pkg);
108             cfile = fopen(CONTENTS_FNAME, "r");
109             if (!cfile) {
110                 warnx(
111                 "unable to open table of contents file '%s' - not a package?",
112                 CONTENTS_FNAME);
113                 goto bomb;
114             }
115             read_plist(&Plist, cfile);
116             fclose(cfile);
117         }
118         else {
119             strcpy(pkg_fullname, pkg);          /*
120                                                  * Copy for sanity's sake,
121                                                  * could remove pkg_fullname
122                                                  */
123             if (strcmp(pkg, "-")) {
124                 if (stat(pkg_fullname, &sb) == FAIL) {
125                     warnx("can't stat package file '%s'", pkg_fullname);
126                     goto bomb;
127                 }
128                 sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME);
129                 extract = extract_contents;
130             }
131             else {
132                 extract = NULL;
133                 sb.st_size = 100000;    /* Make up a plausible average size */
134             }
135             Home = make_playpen(playpen, sb.st_size * 4);
136             if (!Home)
137                 errx(1, "unable to make playpen for %qd bytes", (long long)sb.st_size * 4);
138             where_to = Home;
139             /* Since we can call ourselves recursively, keep notes on where we came from */
140             if (!getenv("_TOP"))
141                 setenv("_TOP", Home, 1);
142             if (unpack(pkg_fullname, extract)) {
143                 warnx(
144         "unable to extract table of contents file from '%s' - not a package?",
145                 pkg_fullname);
146                 goto bomb;
147             }
148             cfile = fopen(CONTENTS_FNAME, "r");
149             if (!cfile) {
150                 warnx(
151         "unable to open table of contents file '%s' - not a package?",
152                 CONTENTS_FNAME);
153                 goto bomb;
154             }
155             read_plist(&Plist, cfile);
156             fclose(cfile);
157
158             /* Extract directly rather than moving?  Oh goodie! */
159             if (find_plist_option(&Plist, "extract-in-place")) {
160                 if (Verbose)
161                     printf("Doing in-place extraction for %s\n", pkg_fullname);
162                 p = find_plist(&Plist, PLIST_CWD);
163                 if (p) {
164                     if (!isdir(p->name) && !Fake) {
165                         if (Verbose)
166                             printf("Desired prefix of %s does not exist, creating..\n", p->name);
167                         vsystem("mkdir -p %s", p->name);
168                         if (chdir(p->name) == -1) {
169                             warn("unable to change directory to '%s'", p->name);
170                             goto bomb;
171                         }
172                     }
173                     where_to = p->name;
174                     inPlace = 1;
175                 }
176                 else {
177                     warnx(
178                 "no prefix specified in '%s' - this is a bad package!",
179                         pkg_fullname);
180                     goto bomb;
181                 }
182             }
183
184             /*
185              * Apply a crude heuristic to see how much space the package will
186              * take up once it's unpacked.  I've noticed that most packages
187              * compress an average of 75%, so multiply by 4 for good measure.
188              */
189
190             if (!extract && !inPlace && min_free(playpen) < sb.st_size * 4) {
191                 warnx("projected size of %qd exceeds available free space.\n"
192 "Please set your PKG_TMPDIR variable to point to a location with more\n"
193                        "free space and try again", (long long)sb.st_size * 4);
194                 warnx("not extracting %s\ninto %s, sorry!",
195                         pkg_fullname, where_to);
196                 goto bomb;
197             }
198
199             /* If this is a direct extract and we didn't want it, stop now */
200             if (inPlace && Fake)
201                 goto success;
202
203             /* Finally unpack the whole mess.  If extract is null we
204                already + did so so don't bother doing it again. */
205             if (extract && unpack(pkg_fullname, NULL)) {
206                 warnx("unable to extract '%s'!", pkg_fullname);
207                 goto bomb;
208             }
209         }
210
211         /* Check for sanity and dependencies */
212         if (sanity_check(pkg))
213             goto bomb;
214
215         /* If we're running in MASTER mode, just output the plist and return */
216         if (AddMode == MASTER) {
217             printf("%s\n", where_playpen());
218             write_plist(&Plist, stdout);
219             return 0;
220         }
221     }
222
223     /*
224      * If we have a prefix, delete the first one we see and add this
225      * one in place of it.
226      */
227     if (Prefix) {
228         delete_plist(&Plist, FALSE, PLIST_CWD, NULL);
229         add_plist_top(&Plist, PLIST_CWD, Prefix);
230     }
231
232     setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1);
233     /* Protect against old packages with bogus @name and origin fields */
234     if (Plist.name == NULL)
235         Plist.name = "anonymous";
236     if (Plist.origin == NULL)
237         Plist.origin = "anonymous/anonymous";
238
239     /*
240      * See if we're already registered either with the same name (the same
241      * version) or some other version with the same origin.
242      */
243     if ((isinstalledpkg(Plist.name) ||
244          matchbyorigin(Plist.origin, NULL) != NULL) && !Force) {
245         warnx("package '%s' or its older version already installed",
246               Plist.name);
247         code = 1;
248         goto success;   /* close enough for government work */
249     }
250
251     /* Now check the packing list for dependencies */
252     for (p = Plist.head; p ; p = p->next) {
253         char *deporigin;
254
255         if (p->type != PLIST_PKGDEP)
256             continue;
257         deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : NULL;
258         if (Verbose) {
259             printf("Package '%s' depends on '%s'", Plist.name, p->name);
260             if (deporigin != NULL)
261                 printf(" with '%s' origin", deporigin);
262             printf(".\n");
263         }
264         if (!isinstalledpkg(p->name) &&
265             !(deporigin != NULL && matchbyorigin(deporigin, NULL) != NULL)) {
266             char path[FILENAME_MAX], *cp = NULL;
267
268             if (!Fake) {
269                 if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) {
270                     const char *ext;
271
272                     ext = strrchr(pkg_fullname, '.');
273                     if (ext == NULL)
274                         ext = ".tgz";
275                     snprintf(path, FILENAME_MAX, "%s/%s%s", getenv("_TOP"), p->name, ext);
276                     if (fexists(path))
277                         cp = path;
278                     else
279                         cp = fileFindByPath(pkg, p->name);
280                     if (cp) {
281                         if (Verbose)
282                             printf("Loading it from %s.\n", cp);
283                         if (vsystem("pkg_add %s'%s'", Verbose ? "-v " : "", cp)) {
284                             warnx("autoload of dependency '%s' failed%s",
285                                 cp, Force ? " (proceeding anyway)" : "!");
286                             if (!Force)
287                                 ++code;
288                         }
289                     }
290                     else {
291                         warnx("could not find package %s %s",
292                               p->name, Force ? " (proceeding anyway)" : "!");
293                         if (!Force)
294                             ++code;
295                     }
296                 }
297                 else if ((cp = fileGetURL(pkg, p->name)) != NULL) {
298                     if (Verbose)
299                         printf("Finished loading %s over FTP.\n", p->name);
300                     if (!fexists("+CONTENTS")) {
301                         warnx("autoloaded package %s has no +CONTENTS file?",
302                                 p->name);
303                         if (!Force)
304                             ++code;
305                     }
306                     else if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) {
307                         warnx("pkg_add of dependency '%s' failed%s",
308                                 p->name, Force ? " (proceeding anyway)" : "!");
309                         if (!Force)
310                             ++code;
311                     }
312                     else if (Verbose)
313                         printf("\t'%s' loaded successfully.\n", p->name);
314                     /* Nuke the temporary playpen */
315                     leave_playpen();
316                 }
317             }
318             else {
319                 if (Verbose)
320                     printf("and was not found%s.\n", Force ? " (proceeding anyway)" : "");
321                 else
322                     printf("Package dependency %s for %s not found%s\n", p->name, pkg,
323                            Force ? " (proceeding anyway)" : "!");
324                 if (!Force)
325                     ++code;
326             }
327         }
328         else if (Verbose)
329             printf(" - already installed.\n");
330     }
331
332     if (code != 0)
333         goto bomb;
334
335     /* Look for the requirements file */
336     if (fexists(REQUIRE_FNAME)) {
337         vsystem("chmod +x %s", REQUIRE_FNAME);  /* be sure */
338         if (Verbose)
339             printf("Running requirements file first for %s..\n", Plist.name);
340         if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, Plist.name)) {
341             warnx("package %s fails requirements %s", pkg_fullname,
342                    Force ? "installing anyway" : "- not installed");
343             if (!Force) {
344                 code = 1;
345                 goto success;   /* close enough for government work */
346             }
347         }
348     }
349
350     /*
351      * Test whether to use the old method of passing tokens to installation
352      * scripts, and set appropriate variables..
353      */
354
355     if (fexists(POST_INSTALL_FNAME)) {
356         new_m = 1;
357         sprintf(post_script, "%s", POST_INSTALL_FNAME);
358         pre_arg[0] = '\0';
359         post_arg[0] = '\0';
360     } else {
361         if (fexists(INSTALL_FNAME)) {
362             sprintf(post_script, "%s", INSTALL_FNAME);
363             sprintf(pre_arg, "PRE-INSTALL");
364             sprintf(post_arg, "POST-INSTALL");
365         }
366     }
367
368     /* If we're really installing, and have an installation file, run it */
369     if (!NoInstall && fexists(pre_script)) {
370         vsystem("chmod +x %s", pre_script);     /* make sure */
371         if (Verbose)
372             printf("Running pre-install for %s..\n", Plist.name);
373         if (!Fake && vsystem("./%s %s %s", pre_script, Plist.name, pre_arg)) {
374             warnx("install script returned error status");
375             unlink(pre_script);
376             code = 1;
377             goto success;               /* nothing to uninstall yet */
378         }
379     }
380
381     /* Now finally extract the entire show if we're not going direct */
382     if (!inPlace && !Fake)
383         extract_plist(".", &Plist);
384
385     if (!Fake && fexists(MTREE_FNAME)) {
386         if (Verbose)
387             printf("Running mtree for %s..\n", Plist.name);
388         p = find_plist(&Plist, PLIST_CWD);
389         if (Verbose)
390             printf("mtree -U -f %s -d -e -p %s >%s\n", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL);
391         if (!Fake) {
392             if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s >%s", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL))
393                 warnx("mtree returned a non-zero status - continuing");
394         }
395     }
396
397     /* Run the installation script one last time? */
398     if (!NoInstall && fexists(post_script)) {
399         vsystem("chmod +x %s", post_script);    /* make sure */
400         if (Verbose)
401             printf("Running post-install for %s..\n", Plist.name);
402         if (!Fake && vsystem("./%s %s %s", post_script, Plist.name, post_arg)) {
403             warnx("install script returned error status");
404             unlink(post_script);
405             code = 1;
406             goto fail;
407         }
408     }
409
410     /* Time to record the deed? */
411     if (!NoRecord && !Fake) {
412         char contents[FILENAME_MAX];
413         FILE *contfile;
414
415         if (getuid() != 0)
416             warnx("not running as root - trying to record install anyway");
417         sprintf(LogDir, "%s/%s", LOG_DIR, Plist.name);
418         zapLogDir = 1;
419         if (Verbose)
420             printf("Attempting to record package into %s..\n", LogDir);
421         if (make_hierarchy(LogDir)) {
422             warnx("can't record package into '%s', you're on your own!",
423                    LogDir);
424             bzero(LogDir, FILENAME_MAX);
425             code = 1;
426             goto success;       /* close enough for government work */
427         }
428         /* Make sure pkg_info can read the entry */
429         vsystem("chmod a+rx %s", LogDir);
430         move_file(".", DESC_FNAME, LogDir);
431         move_file(".", COMMENT_FNAME, LogDir);
432         if (fexists(INSTALL_FNAME))
433             move_file(".", INSTALL_FNAME, LogDir);
434         if (fexists(POST_INSTALL_FNAME))
435             move_file(".", POST_INSTALL_FNAME, LogDir);
436         if (fexists(DEINSTALL_FNAME))
437             move_file(".", DEINSTALL_FNAME, LogDir);
438         if (fexists(POST_DEINSTALL_FNAME))
439             move_file(".", POST_DEINSTALL_FNAME, LogDir);
440         if (fexists(REQUIRE_FNAME))
441             move_file(".", REQUIRE_FNAME, LogDir);
442         if (fexists(DISPLAY_FNAME))
443             move_file(".", DISPLAY_FNAME, LogDir);
444         if (fexists(MTREE_FNAME))
445             move_file(".", MTREE_FNAME, LogDir);
446         sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
447         contfile = fopen(contents, "w");
448         if (!contfile) {
449             warnx("can't open new contents file '%s'! can't register pkg",
450                 contents);
451             goto success; /* can't log, but still keep pkg */
452         }
453         write_plist(&Plist, contfile);
454         fclose(contfile);
455         for (p = Plist.head; p ; p = p->next) {
456             char *deporigin, **depnames;
457             int i;
458
459             if (p->type != PLIST_PKGDEP)
460                 continue;
461             deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name :
462                                                              NULL;
463             if (Verbose) {
464                 printf("Trying to record dependency on package '%s'", p->name);
465                 if (deporigin != NULL)
466                     printf(" with '%s' origin", deporigin);
467                 printf(".\n");
468             }
469
470             depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
471                                              NULL;
472             if (depnames == NULL) {
473                 depnames = alloca(sizeof(*depnames) * 2);
474                 depnames[0] = p->name;
475                 depnames[1] = NULL;
476             }
477             for (i = 0; depnames[i] != NULL; i++) {
478                 sprintf(contents, "%s/%s/%s", LOG_DIR, depnames[i],
479                         REQUIRED_BY_FNAME);
480                 if (strcmp(p->name, depnames[i]) != 0)
481                     warnx("warning: package '%s' requires '%s', but '%s' "
482                           "is installed", Plist.name, p->name, depnames[i]);
483                 contfile = fopen(contents, "a");
484                 if (!contfile)
485                     warnx("can't open dependency file '%s'!\n"
486                           "dependency registration is incomplete", contents);
487                 else {
488                     fprintf(contfile, "%s\n", Plist.name);
489                     if (fclose(contfile) == EOF)
490                         warnx("cannot properly close file %s", contents);
491                 }
492             }
493         }
494         if (Verbose)
495             printf("Package %s registered in %s\n", Plist.name, LogDir);
496     }
497
498     if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) {
499         FILE *fp;
500         char buf[BUFSIZ];
501
502         snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name);
503         fp = fopen(buf, "r");
504         if (fp) {
505             putc('\n', stdout);
506             while (fgets(buf, sizeof(buf), fp))
507                 fputs(buf, stdout);
508             putc('\n', stdout);
509             (void) fclose(fp);
510         } else
511             warnx("cannot open %s as display file", buf);
512     }
513
514     goto success;
515
516  bomb:
517     code = 1;
518     goto success;
519
520  fail:
521     /* Nuke the whole (installed) show, XXX but don't clean directories */
522     if (!Fake)
523         delete_package(FALSE, FALSE, &Plist);
524
525  success:
526     /* delete the packing list contents */
527     free_plist(&Plist);
528     leave_playpen();
529     return code;
530 }
531
532 static int
533 sanity_check(char *pkg)
534 {
535     int code = 0;
536
537     if (!fexists(CONTENTS_FNAME)) {
538         warnx("package %s has no CONTENTS file!", pkg);
539         code = 1;
540     }
541     else if (!fexists(COMMENT_FNAME)) {
542         warnx("package %s has no COMMENT file!", pkg);
543         code = 1;
544     }
545     else if (!fexists(DESC_FNAME)) {
546         warnx("package %s has no DESC file!", pkg);
547         code = 1;
548     }
549     return code;
550 }
551
552 void
553 cleanup(int sig)
554 {
555     static int in_cleanup = 0;
556
557     if (!in_cleanup) {
558         in_cleanup = 1;
559         if (sig)
560             printf("Signal %d received, cleaning up..\n", sig);
561         if (!Fake && zapLogDir && LogDir[0])
562             vsystem("%s -rf %s", REMOVE_CMD, LogDir);
563         leave_playpen();
564     }
565     if (sig)
566         exit(1);
567 }