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