* s/cpu/CPU/
[dragonfly.git] / usr.sbin / pkg_install / delete / 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 delete module.
18  *
19  * $FreeBSD: src/usr.sbin/pkg_install/delete/perform.c,v 1.41 2004/06/29 19:06:41 eik Exp $
20  * $DragonFly: src/usr.sbin/pkg_install/delete/Attic/perform.c,v 1.4 2004/12/18 22:48:04 swildner Exp $
21  */
22
23 #include <err.h>
24 #include "lib.h"
25 #include "delete.h"
26
27 static int pkg_do(char *);
28 static void sanity_check(char *);
29 static void undepend(char *, char *);
30 static char LogDir[FILENAME_MAX];
31
32
33 int
34 pkg_perform(char **pkgs)
35 {
36     char **matched, **rb, **rbtmp;
37     int errcode, i, j;
38     int err_cnt = 0;
39     struct reqr_by_entry *rb_entry;
40     struct reqr_by_head *rb_list;
41
42     if (MatchType != MATCH_EXACT) {
43         matched = matchinstalled(MatchType, pkgs, &errcode);
44         if (errcode != 0)
45             return 1;
46             /* Not reached */
47
48         /*
49          * Copy matched[] into pkgs[], because we'll need to use
50          * matchinstalled() later on.
51          */
52         if (matched != NULL) {
53             pkgs = NULL;
54             for (i = 0; matched[i] != NULL; i++) {
55                 pkgs = realloc(pkgs, sizeof(*pkgs) * (i + 2));
56                 pkgs[i] = strdup(matched[i]);
57             }
58             pkgs[i] = NULL;
59         }
60         else switch (MatchType) {
61             case MATCH_GLOB:
62                 break;
63             case MATCH_ALL:
64                 warnx("no packages installed");
65                 return 0;
66             case MATCH_EREGEX:
67             case MATCH_REGEX:
68                 warnx("no packages match pattern(s)");
69                 return 1;
70             default:
71                 break;
72         }
73     }
74
75     err_cnt += sortdeps(pkgs);
76     for (i = 0; pkgs[i]; i++) {
77         if (Recursive == TRUE) {
78             errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE);
79             if (errcode < 0) {
80                 err_cnt++;
81             } else if (errcode > 0) {
82                 /*
83                  * Copy values from the rb_list queue into argv-like NULL
84                  * terminated list because requiredby() uses some static
85                  * storage, while pkg_do() below will call this function,
86                  * thus blowing our rb_list away.
87                  */
88                 rbtmp = rb = alloca((errcode + 1) * sizeof(*rb));
89                 if (rb == NULL) {
90                     warnx("%s(): alloca() failed", __func__);
91                     err_cnt++;
92                     continue;
93                 }
94                 STAILQ_FOREACH(rb_entry, rb_list, link) {
95                     *rbtmp = alloca(strlen(rb_entry->pkgname) + 1);
96                     if (*rbtmp == NULL) {
97                         warnx("%s(): alloca() failed", __func__);
98                         err_cnt++;
99                         continue;
100                     }
101                     strcpy(*rbtmp, rb_entry->pkgname);
102                     rbtmp++;
103                 }
104                 *rbtmp = NULL;
105
106                 err_cnt += sortdeps(rb);
107                 for (j = 0; rb[j]; j++)
108                     err_cnt += pkg_do(rb[j]);
109             }
110         }
111         err_cnt += pkg_do(pkgs[i]);
112     }
113
114     return err_cnt;
115 }
116
117 static Package Plist;
118
119 /* This is seriously ugly code following.  Written very fast! */
120 static int
121 pkg_do(char *pkg)
122 {
123     FILE *cfile;
124     char *deporigin, **depnames, home[FILENAME_MAX];
125     PackingList p;
126     int i, len;
127     int isinstalled;
128     /* support for separate pre/post install scripts */
129     int new_m = 0;
130     const char *pre_script = DEINSTALL_FNAME;
131     const char *post_script, *pre_arg, *post_arg;
132     struct reqr_by_entry *rb_entry;
133     struct reqr_by_head *rb_list;
134
135     if (!pkg || !(len = strlen(pkg)))
136         return 1;
137     if (pkg[len - 1] == '/')
138         pkg[len - 1] = '\0';
139
140     /* Reset some state */
141     if (Plist.head)
142         free_plist(&Plist);
143
144     sprintf(LogDir, "%s/%s", LOG_DIR, pkg);
145
146     isinstalled = isinstalledpkg(pkg);
147     if (isinstalled == 0) {
148         warnx("no such package '%s' installed", pkg);
149         return 1;
150     } else if (isinstalled < 0) {
151         warnx("the package info for package '%s' is corrupt%s",
152               pkg, Force ? " (but I'll delete it anyway)" : " (use -f to force removal)");
153         if (!Force)
154             return 1;
155         if (!Fake) {
156             if (vsystem("%s -rf %s", REMOVE_CMD, LogDir)) {
157                 warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
158             } else {
159                 warnx("couldn't completely deinstall package '%s',\n"
160                       "only the log entry in %s was removed", pkg, LogDir);
161             }
162         }
163         return 0;
164     }
165
166     if (!getcwd(home, FILENAME_MAX)) {
167         cleanup(0);
168         errx(2, "%s: unable to get current working directory!", __func__);
169     }
170
171     if (chdir(LogDir) == FAIL) {
172         warnx("unable to change directory to %s! deinstall failed", LogDir);
173         return 1;
174     }
175
176     if (Interactive == TRUE) {
177         int first, ch;
178
179         fprintf(stderr, "delete %s? ", pkg);
180         fflush(stderr);
181         first = ch = getchar();
182         while (ch != '\n' && ch != EOF)
183             ch = getchar();
184         if (first != 'y' && first != 'Y')
185             return 0;
186             /* Not reached */
187     }
188
189     if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0)
190         return 1;
191     if (!STAILQ_EMPTY(rb_list)) {
192         warnx("package '%s' is required by these other packages\n"
193               "and may not be deinstalled%s:",
194               pkg, Force ? " (but I'll delete it anyway)" : "");
195         STAILQ_FOREACH(rb_entry, rb_list, link)
196             fprintf(stderr, "%s\n", rb_entry->pkgname);
197         if (!Force)
198             return 1;
199     }
200
201     sanity_check(LogDir);
202     cfile = fopen(CONTENTS_FNAME, "r");
203
204     if (!cfile) {
205         warnx("unable to open '%s' file", CONTENTS_FNAME);
206         return 1;
207     }
208
209     /* If we have a prefix, add it now */
210     if (Prefix)
211         add_plist(&Plist, PLIST_CWD, Prefix);
212     read_plist(&Plist, cfile);
213     fclose(cfile);
214     p = find_plist(&Plist, PLIST_CWD);
215
216     if (!p) {
217         warnx("package '%s' doesn't have a prefix", pkg);
218         return 1;
219     }
220
221     setenv(PKG_PREFIX_VNAME, p->name, 1);
222
223     if (fexists(REQUIRE_FNAME)) {
224         if (Verbose)
225             printf("Executing 'require' script.\n");
226         vsystem("/bin/chmod +x %s", REQUIRE_FNAME);     /* be sure */
227         if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
228             warnx("package %s fails requirements %s", pkg,
229                    Force ? "" : "- not deleted");
230             if (!Force)
231                 return 1;
232         }
233     }
234
235     /*
236      * Test whether to use the old method of passing tokens to deinstallation
237      * scripts, and set appropriate variables..
238      */
239
240     if (fexists(POST_DEINSTALL_FNAME)) {
241         new_m = 1;
242         post_script = POST_DEINSTALL_FNAME;
243         pre_arg = post_arg = "";
244     } else if (fexists(DEINSTALL_FNAME)) {
245         post_script = DEINSTALL_FNAME;
246         pre_arg = "DEINSTALL";
247         post_arg = "POST-DEINSTALL";
248     } else {
249         post_script = pre_arg = post_arg = NULL;
250     }
251
252     if (!NoDeInstall && pre_script != NULL && fexists(pre_script)) {
253         if (Fake)
254             printf("Would execute de-install script at this point.\n");
255         else {
256             vsystem("/bin/chmod +x %s", pre_script);    /* make sure */
257             if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) {
258                 warnx("deinstall script returned error status");
259                 if (!Force)
260                     return 1;
261             }
262         }
263     }
264
265     for (p = Plist.head; p ; p = p->next) {
266         if (p->type != PLIST_PKGDEP)
267             continue;
268         deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name :
269                                                          NULL;
270         if (Verbose) {
271             printf("Trying to remove dependency on package '%s'", p->name);
272             if (deporigin != NULL)
273                 printf(" with '%s' origin", deporigin);
274             printf(".\n");
275         }
276         if (!Fake) {
277             depnames = (deporigin != NULL) ? matchbyorigin(deporigin, NULL) :
278                                              NULL;
279             if (depnames == NULL) {
280                 depnames = alloca(sizeof(*depnames) * 2);
281                 depnames[0] = p->name;
282                 depnames[1] = NULL;
283             }
284             for (i = 0; depnames[i] != NULL; i++)
285                 undepend(depnames[i], pkg);
286         }
287     }
288
289     if (chdir(home) == FAIL) {
290         cleanup(0);
291         errx(2, "%s: unable to return to working directory %s!", __func__,
292             home);
293     }
294
295     /*
296      * Some packages aren't packed right, so we need to just ignore
297      * delete_package()'s status.  Ugh! :-(
298      */
299     if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
300         warnx(
301         "couldn't entirely delete package (perhaps the packing list is\n"
302         "incorrectly specified?)");
303
304     if (chdir(LogDir) == FAIL) {
305         warnx("unable to change directory to %s! deinstall failed", LogDir);
306         return 1;
307     }
308
309     if (!NoDeInstall && post_script != NULL && fexists(post_script)) {
310         if (Fake)
311             printf("Would execute post-deinstall script at this point.\n");
312         else {
313             vsystem("/bin/chmod +x %s", post_script);   /* make sure */
314             if (vsystem("./%s %s %s", post_script, pkg, post_arg)) {
315                 warnx("post-deinstall script returned error status");
316                 if (!Force)
317                     return 1;
318             }
319         }
320     }
321
322     if (chdir(home) == FAIL) {
323         cleanup(0);
324         errx(2, "%s: unable to return to working directory %s!", __func__,
325             home);
326     }
327
328     if (!Fake) {
329         if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) {
330             warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
331             if (!Force)
332                 return 1;
333         }
334     }
335     return 0;
336 }
337
338 static void
339 sanity_check(char *pkg)
340 {
341     if (!fexists(CONTENTS_FNAME)) {
342         cleanup(0);
343         errx(2, "%s: installed package %s has no %s file!", __func__,
344             pkg, CONTENTS_FNAME);
345     }
346 }
347
348 void
349 cleanup(int sig)
350 {
351     if (sig)
352         exit(1);
353 }
354
355 static void
356 undepend(char *p, char *pkgname)
357 {
358     char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
359     FILE *fpwr;
360     int s;
361     struct reqr_by_entry *rb_entry;
362     struct reqr_by_head *rb_list;
363
364
365     if (requiredby(p, &rb_list, Verbose, FALSE) <= 0)
366         return;
367     snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p, REQUIRED_BY_FNAME);
368     snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname);
369     s = mkstemp(ftmp);
370     if (s == -1) {
371         warnx("couldn't open temp file '%s'", ftmp);
372         return;
373     }
374     fpwr = fdopen(s, "w");
375     if (fpwr == NULL) {
376         close(s);
377         warnx("couldn't fdopen temp file '%s'", ftmp);
378         goto cleanexit;
379     }
380     STAILQ_FOREACH(rb_entry, rb_list, link)
381         if (strcmp(rb_entry->pkgname, pkgname))         /* no match */
382             fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr);
383     if (fchmod(s, 0644) == FAIL) {
384         warnx("error changing permission of temp file '%s'", ftmp);
385         fclose(fpwr);
386         goto cleanexit;
387     }
388     if (fclose(fpwr) == EOF) {
389         warnx("error closing temp file '%s'", ftmp);
390         goto cleanexit;
391     }
392     if (rename(ftmp, fname) == -1)
393         warnx("error renaming '%s' to '%s'", ftmp, fname);
394 cleanexit:
395     remove(ftmp);
396     return;
397 }