Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.sbin / pkg_install / lib / plist.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  * General packing list routines.
18  *
19  * $FreeBSD: src/usr.sbin/pkg_install/lib/plist.c,v 1.29.2.10 2002/08/31 19:38:55 obrien Exp $
20  * $DragonFly: src/usr.sbin/pkg_install/lib/Attic/plist.c,v 1.2 2003/06/17 04:29:59 dillon Exp $
21  */
22
23 #include "lib.h"
24 #include <err.h>
25 #include <md5.h>
26
27 /* Add an item to a packing list */
28 void
29 add_plist(Package *p, plist_t type, const char *arg)
30 {
31     PackingList tmp;
32
33     tmp = new_plist_entry();
34     tmp->name = copy_string(arg);
35     tmp->type = type;
36
37     if (!p->head)
38         p->head = p->tail = tmp;
39     else {
40         tmp->prev = p->tail;
41         p->tail->next = tmp;
42         p->tail = tmp;
43     }
44     switch (type) {
45     case PLIST_NAME:
46         p->name = tmp->name;
47         break;
48
49     case PLIST_ORIGIN:
50         p->origin = tmp->name;
51         break;
52
53     default:
54         break;
55     }
56 }
57
58 void
59 add_plist_top(Package *p, plist_t type, const char *arg)
60 {
61     PackingList tmp;
62
63     tmp = new_plist_entry();
64     tmp->name = copy_string(arg);
65     tmp->type = type;
66
67     if (!p->head)
68         p->head = p->tail = tmp;
69     else {
70         tmp->next = p->head;
71         p->head->prev = tmp;
72         p->head = tmp;
73     }
74 }
75
76 /* Return the last (most recent) entry in a packing list */
77 PackingList
78 last_plist(Package *p)
79 {
80     return p->tail;
81 }
82
83 /* Mark all items in a packing list to prevent iteration over them */
84 void
85 mark_plist(Package *pkg)
86 {
87     PackingList p = pkg->head;
88
89     while (p) {
90         p->marked = TRUE;
91         p = p->next;
92     }
93 }
94
95 /* Find a given item in a packing list and, if so, return it (else NULL) */
96 PackingList
97 find_plist(Package *pkg, plist_t type)
98 {
99     PackingList p = pkg->head;
100
101     while (p) {
102         if (p->type == type)
103             return p;
104         p = p->next;
105     }
106     return NULL;
107 }
108
109 /* Look for a specific boolean option argument in the list */
110 char *
111 find_plist_option(Package *pkg, const char *name)
112 {
113     PackingList p = pkg->head;
114
115     while (p) {
116         if (p->type == PLIST_OPTION && !strcmp(p->name, name))
117             return p->name;
118         p = p->next;
119     }
120     return NULL;
121 }
122
123 /*
124  * Delete plist item 'type' in the list (if 'name' is non-null, match it
125  * too.)  If 'all' is set, delete all items, not just the first occurance.
126  */
127 void
128 delete_plist(Package *pkg, Boolean all, plist_t type, const char *name)
129 {
130     PackingList p = pkg->head;
131
132     while (p) {
133         PackingList pnext = p->next;
134
135         if (p->type == type && (!name || !strcmp(name, p->name))) {
136             free(p->name);
137             if (p->prev)
138                 p->prev->next = pnext;
139             else
140                 pkg->head = pnext;
141             if (pnext)
142                 pnext->prev = p->prev;
143             else
144                 pkg->tail = p->prev;
145             free(p);
146             if (!all)
147                 return;
148             p = pnext;
149         }
150         else
151             p = p->next;
152     }
153 }
154
155 /* Allocate a new packing list entry */
156 PackingList
157 new_plist_entry(void)
158 {
159     PackingList ret;
160
161     ret = (PackingList)malloc(sizeof(struct _plist));
162     bzero(ret, sizeof(struct _plist));
163     return ret;
164 }
165
166 /* Free an entire packing list */
167 void
168 free_plist(Package *pkg)
169 {
170     PackingList p = pkg->head;
171
172     while (p) {
173         PackingList p1 = p->next;
174
175         free(p->name);
176         free(p);
177         p = p1;
178     }
179     pkg->head = pkg->tail = NULL;
180 }
181
182 /*
183  * For an ascii string denoting a plist command, return its code and
184  * optionally its argument(s)
185  */
186 int
187 plist_cmd(const char *s, char **arg)
188 {
189     char cmd[FILENAME_MAX + 20];        /* 20 == fudge for max cmd len */
190     char *cp;
191     const char *sp;
192
193     strcpy(cmd, s);
194     str_lowercase(cmd);
195     cp = cmd;
196     sp = s;
197     while (*cp) {
198         if (isspace(*cp)) {
199             *cp = '\0';
200             while (isspace(*sp)) /* Never sure if macro, increment later */
201                 ++sp;
202             break;
203         }
204         ++cp, ++sp;
205     }
206     if (arg)
207         (const char *)*arg = sp;
208     if (!strcmp(cmd, "cwd"))
209         return PLIST_CWD;
210     else if (!strcmp(cmd, "srcdir"))
211         return PLIST_SRC;
212     else if (!strcmp(cmd, "cd"))
213         return PLIST_CWD;
214     else if (!strcmp(cmd, "exec"))
215         return PLIST_CMD;
216     else if (!strcmp(cmd, "unexec"))
217         return PLIST_UNEXEC;
218     else if (!strcmp(cmd, "mode"))
219         return PLIST_CHMOD;
220     else if (!strcmp(cmd, "owner"))
221         return PLIST_CHOWN;
222     else if (!strcmp(cmd, "group"))
223         return PLIST_CHGRP;
224     else if (!strcmp(cmd, "comment")) {
225         if (!strncmp(*arg, "ORIGIN:", 7)) {
226             *arg += 7;
227             return PLIST_ORIGIN;
228         } else if (!strncmp(*arg, "DEPORIGIN:", 10)) {
229             *arg += 10;
230             return PLIST_DEPORIGIN;
231         }
232         return PLIST_COMMENT;
233     } else if (!strcmp(cmd, "ignore"))
234         return PLIST_IGNORE;
235     else if (!strcmp(cmd, "ignore_inst"))
236         return PLIST_IGNORE_INST;
237     else if (!strcmp(cmd, "name"))
238         return PLIST_NAME;
239     else if (!strcmp(cmd, "display"))
240         return PLIST_DISPLAY;
241     else if (!strcmp(cmd, "pkgdep"))
242         return PLIST_PKGDEP;
243     else if (!strcmp(cmd, "mtree"))
244         return PLIST_MTREE;
245     else if (!strcmp(cmd, "dirrm"))
246         return PLIST_DIR_RM;
247     else if (!strcmp(cmd, "option"))
248         return PLIST_OPTION;
249     else
250         return FAIL;
251 }
252
253 /* Read a packing list from a file */
254 void
255 read_plist(Package *pkg, FILE *fp)
256 {
257     char *cp, pline[FILENAME_MAX];
258     int cmd, major, minor;
259
260     pkg->fmtver_maj = 1;
261     pkg->fmtver_mnr = 0;
262     pkg->origin = NULL;
263     while (fgets(pline, FILENAME_MAX, fp)) {
264         int len = strlen(pline);
265
266         while (len && isspace(pline[len - 1]))
267             pline[--len] = '\0';
268         if (!len)
269             continue;
270         cp = pline;
271         if (pline[0] != CMD_CHAR) {
272             cmd = PLIST_FILE;
273             goto bottom;
274         }
275         cmd = plist_cmd(pline + 1, &cp);
276         if (cmd == FAIL) {
277             cleanup(0);
278             errx(2, "%s: bad command '%s'", __func__, pline);
279         }
280         if (*cp == '\0') {
281             cp = NULL;
282             goto bottom;
283         }
284         if (cmd == PLIST_COMMENT && sscanf(cp, "PKG_FORMAT_REVISION:%d.%d\n",
285                                            &major, &minor) == 2) {
286             pkg->fmtver_maj = major;
287             pkg->fmtver_mnr = minor;
288             if (verscmp(pkg, PLIST_FMT_VER_MAJOR, PLIST_FMT_VER_MINOR) <= 0)
289                 goto bottom;
290
291             warnx("plist format revision (%d.%d) is higher than supported"
292                   "(%d.%d)", pkg->fmtver_maj, pkg->fmtver_mnr,
293                   PLIST_FMT_VER_MAJOR, PLIST_FMT_VER_MINOR);
294             if (pkg->fmtver_maj > PLIST_FMT_VER_MAJOR) {
295                 cleanup(0);
296                 exit(2);
297             }
298         }
299 bottom:
300         add_plist(pkg, cmd, cp);
301     }
302 }
303
304 /* Write a packing list to a file, converting commands to ascii equivs */
305 void
306 write_plist(Package *pkg, FILE *fp)
307 {
308     PackingList plist = pkg->head;
309
310     while (plist) {
311         switch(plist->type) {
312         case PLIST_FILE:
313             fprintf(fp, "%s\n", plist->name);
314             break;
315
316         case PLIST_CWD:
317             fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
318             break;
319
320         case PLIST_SRC:
321             fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
322             break;
323
324         case PLIST_CMD:
325             fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
326             break;
327
328         case PLIST_UNEXEC:
329             fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
330             break;
331
332         case PLIST_CHMOD:
333             fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
334             break;
335
336         case PLIST_CHOWN:
337             fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
338             break;
339
340         case PLIST_CHGRP:
341             fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
342             break;
343
344         case PLIST_COMMENT:
345             fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
346             break;
347
348         case PLIST_IGNORE:
349         case PLIST_IGNORE_INST:         /* a one-time non-ignored file */
350             fprintf(fp, "%cignore\n", CMD_CHAR);
351             break;
352
353         case PLIST_NAME:
354             fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
355             break;
356
357         case PLIST_DISPLAY:
358             fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
359             break;
360
361         case PLIST_PKGDEP:
362             fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
363             break;
364
365         case PLIST_MTREE:
366             fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
367             break;
368
369         case PLIST_DIR_RM:
370             fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
371             break;
372
373         case PLIST_OPTION:
374             fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
375             break;
376
377         case PLIST_ORIGIN:
378             fprintf(fp, "%ccomment ORIGIN:%s\n", CMD_CHAR, plist->name);
379             break;
380
381         case PLIST_DEPORIGIN:
382             fprintf(fp, "%ccomment DEPORIGIN:%s\n", CMD_CHAR, plist->name);
383             break;
384
385         default:
386             cleanup(0);
387             errx(2, "%s: unknown command type %d (%s)", __func__,
388                 plist->type, plist->name);
389             break;
390         }
391         plist = plist->next;
392     }
393 }
394
395 /*
396  * Delete the results of a package installation.
397  *
398  * This is here rather than in the pkg_delete code because pkg_add needs to
399  * run it too in cases of failure.
400  */
401 int
402 delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
403 {
404     PackingList p;
405     const char *Where = ".", *last_file = "";
406     Boolean fail = SUCCESS;
407     Boolean preserve;
408     char tmp[FILENAME_MAX], *name = NULL;
409
410     preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
411     for (p = pkg->head; p; p = p->next) {
412         switch (p->type)  {
413         case PLIST_NAME:
414             name = p->name;
415             break;
416
417         case PLIST_IGNORE:
418             p = p->next;
419             break;
420
421         case PLIST_CWD:
422             Where = p->name;
423             if (Verbose)
424                 printf("Change working directory to %s\n", Where);
425             break;
426
427         case PLIST_UNEXEC:
428             format_cmd(tmp, p->name, Where, last_file);
429             if (Verbose)
430                 printf("Execute '%s'\n", tmp);
431             if (!Fake && system(tmp)) {
432                 warnx("unexec command for '%s' failed", tmp);
433                 fail = FAIL;
434             }
435             break;
436
437         case PLIST_FILE:
438             last_file = p->name;
439             sprintf(tmp, "%s/%s", Where, p->name);
440             if (isdir(tmp) && fexists(tmp) && !issymlink(tmp)) {
441                 warnx("cannot delete specified file '%s' - it is a directory!\n"
442            "this packing list is incorrect - ignoring delete request", tmp);
443             }
444             else {
445                 if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) {
446                     char *cp = NULL, buf[33];
447
448                     /*
449                      * For packing lists whose version is 1.1 or greater, the md5
450                      * hash for a symlink is calculated on the string returned
451                      * by readlink().
452                      */
453                     if (issymlink(tmp) && verscmp(pkg, 1, 0) > 0) {
454                         int len;
455                         char linkbuf[FILENAME_MAX];
456
457                         if ((len = readlink(tmp, linkbuf, FILENAME_MAX)) > 0)
458                              cp = MD5Data((unsigned char *)linkbuf, len, buf);
459                     } else if (isfile(tmp) || verscmp(pkg, 1, 1) < 0)
460                         cp = MD5File(tmp, buf);
461
462                     if (cp != NULL) {
463                         /* Mismatch? */
464                         if (strcmp(cp, p->next->name + 4)) {
465                             warnx("'%s' fails original MD5 checksum - %s",
466                                        tmp, Force ? "deleted anyway." : "not deleted.");
467                             if (!Force) {
468                                 fail = FAIL;
469                                 continue;
470                             }
471                         }
472                     }
473                 }
474                 if (Verbose)
475                     printf("Delete file %s\n", tmp);
476                 if (!Fake) {
477                     if (delete_hierarchy(tmp, ign_err, nukedirs))
478                         fail = FAIL;
479                     if (preserve && name) {
480                         char tmp2[FILENAME_MAX];
481                             
482                         if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) {
483                             if (fexists(tmp2)) {
484                                 if (rename(tmp2, tmp))
485                                    warn("preserve: unable to restore %s as %s",
486                                         tmp2, tmp);
487                             }
488                         }
489                     }
490                 }
491             }
492             break;
493
494         case PLIST_DIR_RM:
495             sprintf(tmp, "%s/%s", Where, p->name);
496             if (!isdir(tmp) && fexists(tmp)) {
497                 warnx("cannot delete specified directory '%s' - it is a file!\n"
498         "this packing list is incorrect - ignoring delete request", tmp);
499             }
500             else {
501                 if (Verbose)
502                     printf("Delete directory %s\n", tmp);
503                 if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) {
504                     warnx("unable to completely remove directory '%s'", tmp);
505                     fail = FAIL;
506                 }
507             }
508             last_file = p->name;
509             break;
510
511         default:
512             break;
513         }
514     }
515     return fail;
516 }
517
518 #ifdef DEBUG
519 #define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
520 #define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
521 #else
522 #define RMDIR rmdir
523 #define REMOVE(file,ie) (remove(file) && !(ie))
524 #endif
525
526 /* Selectively delete a hierarchy */
527 int
528 delete_hierarchy(const char *dir, Boolean ign_err, Boolean nukedirs)
529 {
530     char *cp1, *cp2;
531
532     cp1 = cp2 = strdup(dir);
533     if (!fexists(dir)) {
534         if (!ign_err)
535             warnx("%s '%s' doesn't really exist",
536                 isdir(dir) ? "directory" : "file", dir);
537         return !ign_err;
538     }
539     else if (nukedirs) {
540         if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
541             return 1;
542     }
543     else if (isdir(dir) && !issymlink(dir)) {
544         if (RMDIR(dir) && !ign_err)
545             return 1;
546     }
547     else {
548         if (REMOVE(dir, ign_err))
549             return 1;
550     }
551
552     if (!nukedirs)
553         return 0;
554     while (cp2) {
555         if ((cp2 = strrchr(cp1, '/')) != NULL)
556             *cp2 = '\0';
557         if (!isemptydir(dir))
558             return 0;
559         if (RMDIR(dir) && !ign_err) {
560             if (!fexists(dir))
561                 warnx("directory '%s' doesn't really exist", dir);
562             else
563                 return 1;
564         }
565         /* back up the pathname one component */
566         if (cp2) {
567             cp1 = strdup(dir);
568         }
569     }
570     return 0;
571 }