1 /* $NetBSD: plist.c,v 1.29 2009/08/02 17:56:45 joerg Exp $ */
10 __RCSID("$NetBSD: plist.c,v 1.29 2009/08/02 17:56:45 joerg Exp $");
13 * FreeBSD install - a package for the installation and maintainance
14 * of non-core utilities.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
28 * General packing list routines.
33 * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
48 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
49 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
50 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
51 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
52 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
53 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
55 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
56 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
57 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 #include <nbcompat/md5.h>
74 static int delete_with_parents(const char *, Boolean
, Boolean
);
76 /* This struct defines a plist command type */
77 typedef struct cmd_t
{
78 const char *c_s
; /* string to recognise */
79 pl_ent_t c_type
; /* type of command */
80 int c_argc
; /* # of arguments */
81 int c_subst
; /* can substitute real prefix */
84 /* Commands to recognise */
85 static const cmd_t cmdv
[] = {
86 {"cwd", PLIST_CWD
, 1, 1},
87 {"src", PLIST_SRC
, 1, 1},
88 {"exec", PLIST_CMD
, 1, 0},
89 {"unexec", PLIST_UNEXEC
, 1, 0},
90 {"mode", PLIST_CHMOD
, 1, 0},
91 {"owner", PLIST_CHOWN
, 1, 0},
92 {"group", PLIST_CHGRP
, 1, 0},
93 {"comment", PLIST_COMMENT
, 1, 0},
94 {"ignore", PLIST_IGNORE
, 0, 0},
95 {"name", PLIST_NAME
, 1, 0},
96 {"display", PLIST_DISPLAY
, 1, 0},
97 {"pkgdep", PLIST_PKGDEP
, 1, 0},
98 {"pkgcfl", PLIST_PKGCFL
, 1, 0},
99 {"pkgdir", PLIST_PKGDIR
, 1, 0},
100 {"dirrm", PLIST_DIR_RM
, 1, 0},
101 {"option", PLIST_OPTION
, 1, 0},
102 {"blddep", PLIST_BLDDEP
, 1, 0},
107 * Add an item to the end of a packing list
110 add_plist(package_t
*p
, pl_ent_t type
, const char *arg
)
114 tmp
= new_plist_entry();
115 tmp
->name
= (arg
== NULL
) ? NULL
: xstrdup(arg
);
118 p
->head
= p
->tail
= tmp
;
127 * Add an item to the start of a packing list
130 add_plist_top(package_t
*p
, pl_ent_t type
, const char *arg
)
134 tmp
= new_plist_entry();
135 tmp
->name
= (arg
== NULL
) ? NULL
: xstrdup(arg
);
138 p
->head
= p
->tail
= tmp
;
147 * Return the last (most recent) entry in a packing list
150 last_plist(package_t
*p
)
156 * Mark all items in a packing list to prevent iteration over them
159 mark_plist(package_t
*pkg
)
163 for (pp
= pkg
->head
; pp
; pp
= pp
->next
) {
169 * Find a given item in a packing list and, if so, return it (else NULL)
172 find_plist(package_t
*pkg
, pl_ent_t type
)
176 for (pp
= pkg
->head
; pp
&& pp
->type
!= type
; pp
= pp
->next
) {
182 * Look for a specific boolean option argument in the list
185 find_plist_option(package_t
*pkg
, const char *name
)
189 for (p
= pkg
->head
; p
; p
= p
->next
) {
190 if (p
->type
== PLIST_OPTION
191 && strcmp(p
->name
, name
) == 0) {
196 return (char *) NULL
;
200 * Delete plist item 'type' in the list (if 'name' is non-null, match it
201 * too.) If 'all' is set, delete all items, not just the first occurance.
204 delete_plist(package_t
*pkg
, Boolean all
, pl_ent_t type
, char *name
)
206 plist_t
*p
= pkg
->head
;
209 plist_t
*pnext
= p
->next
;
211 if (p
->type
== type
&& (!name
|| !strcmp(name
, p
->name
))) {
214 p
->prev
->next
= pnext
;
218 pnext
->prev
= p
->prev
;
231 * Allocate a new packing list entry, and return a pointer to it.
234 new_plist_entry(void)
236 return xcalloc(1, sizeof(plist_t
));
240 * Free an entire packing list
243 free_plist(package_t
*pkg
)
245 plist_t
*p
= pkg
->head
;
248 plist_t
*p1
= p
->next
;
254 pkg
->head
= pkg
->tail
= NULL
;
258 * For an ASCII string denoting a plist command, return its code and
259 * optionally its argument(s)
262 plist_cmd(const char *s
, char **arg
)
268 sp
= NULL
; /* Older GCC can't detect that the loop is executed */
270 for (cmdp
= cmdv
; cmdp
->c_s
; ++cmdp
) {
271 for (sp
= s
, cp
= cmdp
->c_s
; *sp
&& *cp
; ++cp
, ++sp
)
278 if (cmdp
->c_s
== NULL
|| arg
== NULL
)
281 while (isspace((unsigned char)*sp
))
285 sp2
= *arg
+ strlen(*arg
) - 1;
287 * The earlier loop ensured that at least one non-whitespace
290 while (isspace((unsigned char)*sp2
))
298 * Parse a packaging list from a memory buffer.
301 parse_plist(package_t
*pkg
, const char *buf
)
305 const char *eol
, *next
;
311 for (; *buf
; buf
= next
) {
312 /* Until add_plist can deal with trailing whitespace. */
313 if ((eol
= strchr(buf
, '\n')) != NULL
) {
321 while (len
&& isspace((unsigned char)buf
[len
- 1]))
327 line
= xmalloc(len
+ 1);
328 memcpy(line
, buf
, len
);
331 if (*(cp
= line
) == CMD_CHAR
) {
332 if ((cmd
= plist_cmd(line
+ 1, &cp
)) == FAIL
) {
333 warnx("Unrecognised PLIST command `%s'", line
);
343 add_plist(pkg
, cmd
, cp
);
349 * Read a packing list from a file
352 append_plist(package_t
*pkg
, FILE * fp
)
354 char pline
[MaxPathSize
];
360 while (fgets(pline
, MaxPathSize
, fp
) != (char *) NULL
) {
361 for (len
= strlen(pline
); len
&&
362 isspace((unsigned char) pline
[len
- 1]);) {
369 if (*(cp
= pline
) == CMD_CHAR
) {
370 if ((cmd
= plist_cmd(pline
+ 1, &cp
)) == FAIL
) {
371 warnx("Unrecognised PLIST command `%s'", pline
);
382 add_plist(pkg
, cmd
, cp
);
389 read_plist(package_t
*pkg
, FILE * fp
)
394 append_plist(pkg
, fp
);
398 * Write a packing list to a file, converting commands to ASCII equivs
401 write_plist(package_t
*pkg
, FILE * fp
, char *realprefix
)
406 for (p
= pkg
->head
; p
; p
= p
->next
) {
407 if (p
->type
== PLIST_FILE
) {
408 /* Fast-track files - these are the most common */
409 (void) fprintf(fp
, "%s\n", p
->name
);
412 for (cmdp
= cmdv
; cmdp
->c_type
!= FAIL
&& cmdp
->c_type
!= p
->type
; cmdp
++) {
414 if (cmdp
->c_type
== FAIL
) {
415 warnx("Unknown PLIST command type %d (%s)", p
->type
, p
->name
);
416 } else if (cmdp
->c_argc
== 0) {
417 (void) fprintf(fp
, "%c%s\n", CMD_CHAR
, cmdp
->c_s
);
418 } else if (cmdp
->c_subst
&& realprefix
) {
419 (void) fprintf(fp
, "%c%s %s\n", CMD_CHAR
, cmdp
->c_s
, realprefix
);
421 (void) fprintf(fp
, "%c%s %s\n", CMD_CHAR
, cmdp
->c_s
,
422 (p
->name
) ? p
->name
: "");
428 * Like write_plist, but compute memory string.
431 stringify_plist(package_t
*pkg
, char **real_buf
, size_t *real_len
,
432 const char *realprefix
)
440 /* Pass One: compute output size only. */
443 for (p
= pkg
->head
; p
; p
= p
->next
) {
444 if (p
->type
== PLIST_FILE
) {
445 len
+= strlen(p
->name
) + 1;
448 for (cmdp
= cmdv
; cmdp
->c_type
!= FAIL
&& cmdp
->c_type
!= p
->type
; cmdp
++) {
450 if (cmdp
->c_type
== FAIL
)
452 if (cmdp
->c_argc
== 0)
453 len
+= 1 + strlen(cmdp
->c_s
) + 1;
454 else if (cmdp
->c_subst
&& realprefix
)
455 len
+= 1 + strlen(cmdp
->c_s
) + 1 + strlen(realprefix
) + 1;
457 len
+= 1 + strlen(cmdp
->c_s
) + 1 + strlen(p
->name
? p
->name
: "") + 1;
460 /* Pass Two: build actual string. */
461 buf
= xmalloc(len
+ 1);
468 if (item_len < 0 || (size_t)item_len > len) \
469 errx(2, "Size computation failed, aborted."); \
472 } while (/* CONSTCOND */0)
474 for (p
= pkg
->head
; p
; p
= p
->next
) {
475 if (p
->type
== PLIST_FILE
) {
476 /* Fast-track files - these are the most common */
477 item_len
= snprintf(buf
, len
, "%s\n", p
->name
);
481 for (cmdp
= cmdv
; cmdp
->c_type
!= FAIL
&& cmdp
->c_type
!= p
->type
; cmdp
++) {
483 if (cmdp
->c_type
== FAIL
) {
484 warnx("Unknown PLIST command type %d (%s)", p
->type
, p
->name
);
485 } else if (cmdp
->c_argc
== 0) {
486 item_len
= snprintf(buf
, len
, "%c%s\n", CMD_CHAR
, cmdp
->c_s
);
488 } else if (cmdp
->c_subst
&& realprefix
) {
489 item_len
= snprintf(buf
, len
, "%c%s %s\n", CMD_CHAR
, cmdp
->c_s
, realprefix
);
492 item_len
= snprintf(buf
, len
, "%c%s %s\n", CMD_CHAR
, cmdp
->c_s
,
493 (p
->name
) ? p
->name
: "");
499 errx(2, "Size computation failed, aborted.");
503 * Delete the results of a package installation.
505 * This is here rather than in the pkg_delete code because pkg_add needs to
506 * run it too in cases of failure.
509 delete_package(Boolean ign_err
, package_t
*pkg
, Boolean NoDeleteFiles
,
513 const char *last_file
= "";
516 char tmp
[MaxPathSize
];
517 const char *prefix
= NULL
, *name
= NULL
;
519 if (!pkgdb_open(ReadWrite
)) {
520 err(EXIT_FAILURE
, "cannot open pkgdb");
523 preserve
= find_plist_option(pkg
, "preserve") ? TRUE
: FALSE
;
525 for (p
= pkg
->head
; p
; p
= p
->next
) {
539 if (name
== NULL
|| prefix
== NULL
)
540 errx(EXIT_FAILURE
, "broken PLIST");
543 * Remove database entries first, directory removal is done
544 * in the main loop below.
546 for (p
= pkg
->head
; p
; p
= p
->next
) {
547 if (p
->type
== PLIST_PKGDIR
)
548 delete_pkgdir(name
, prefix
, p
->name
);
551 for (p
= pkg
->head
; p
; p
= p
->next
) {
554 /* Handled already */
559 (void) snprintf(tmp
, sizeof(tmp
), "%s/%s",
563 (void) snprintf(tmp
, sizeof(tmp
), "%s%s%s/%s",
564 destdir
? destdir
: "", destdir
? "/" : "",
567 if (p
->type
== PLIST_PKGDIR
)
568 warnx("Directory `%s' disappeared, skipping", tmp
);
569 } else if (!isdir(tmp
)) {
570 warnx("attempting to delete a file `%s' as a directory\n"
571 "this packing list is incorrect - ignoring delete request", tmp
);
572 } else if (delete_with_parents(tmp
, ign_err
, TRUE
))
583 format_cmd(tmp
, sizeof(tmp
), p
->name
, prefix
, last_file
);
584 printf("Executing `%s'\n", tmp
);
585 if (!Fake
&& system(tmp
)) {
586 warnx("unexec command for `%s' failed", tmp
);
593 (void) snprintf(tmp
, sizeof(tmp
), "%s%s%s/%s",
594 destdir
? destdir
: "", destdir
? "/" : "",
597 warnx("attempting to delete directory `%s' as a file\n"
598 "this packing list is incorrect - ignoring delete request", tmp
);
600 int restored
= 0; /* restored from preserve? */
602 if (p
->next
&& p
->next
->type
== PLIST_COMMENT
) {
603 if (strncmp(p
->next
->name
, CHECKSUM_HEADER
, ChecksumHeaderLen
) == 0) {
604 char *cp
, buf
[LegibleChecksumLen
];
606 if ((cp
= MD5File(tmp
, buf
)) != NULL
) {
608 if (strcmp(cp
, p
->next
->name
+ ChecksumHeaderLen
) != 0) {
609 printf("original MD5 checksum failed, %s: %s\n",
610 Force
? "deleting anyway" : "not deleting", tmp
);
617 } else if (strncmp(p
->next
->name
, SYMLINK_HEADER
, SymlinkHeaderLen
) == 0) {
618 char buf
[MaxPathSize
+ SymlinkHeaderLen
];
621 (void) strlcpy(buf
, SYMLINK_HEADER
,
623 if ((cc
= readlink(tmp
, &buf
[SymlinkHeaderLen
],
624 sizeof(buf
) - SymlinkHeaderLen
- 1)) < 0) {
625 warn("can't readlink `%s'", tmp
);
628 buf
[SymlinkHeaderLen
+ cc
] = 0x0;
629 if (strcmp(buf
, p
->next
->name
) != 0) {
630 if ((cc
= readlink(&buf
[SymlinkHeaderLen
], &buf
[SymlinkHeaderLen
],
631 sizeof(buf
) - SymlinkHeaderLen
)) < 0) {
632 printf("symlink %s is not same as recorded value, %s: %s\n",
633 buf
, Force
? "deleting anyway" : "not deleting", tmp
);
639 buf
[SymlinkHeaderLen
+ cc
] = 0x0;
640 if (strcmp(buf
, p
->next
->name
) != 0) {
641 printf("symlink %s is not same as recorded value, %s: %s\n",
642 buf
, Force
? "deleting anyway" : "not deleting", tmp
);
651 if (Verbose
&& !NoDeleteFiles
)
652 printf("Delete file %s\n", tmp
);
653 if (!Fake
&& !NoDeleteFiles
) {
654 if (delete_with_parents(tmp
, ign_err
, FALSE
))
656 if (preserve
&& name
) {
657 char tmp2
[MaxPathSize
];
659 if (make_preserve_name(tmp2
, MaxPathSize
, name
, tmp
)) {
661 if (rename(tmp2
, tmp
))
662 warn("preserve: unable to restore %s as %s",
675 if (pkgdb_remove(tmp
) && errno
)
676 perror("pkgdb_remove");
690 * Selectively delete a hierarchy
691 * Returns 1 on error, 0 else.
694 delete_with_parents(const char *fname
, Boolean ign_err
, Boolean ign_nonempty
)
699 if (!ign_err
&& (!ign_nonempty
|| errno
!= ENOTEMPTY
))
700 warnx("Couldn't remove %s", fname
);
705 if ((cp2
= strrchr(cp
, '/')) != NULL
)
720 add_pkgdir(const char *pkg
, const char *prefix
, const char *path
)
722 char *fullpath
, *oldvalue
, *newvalue
;
724 fullpath
= xasprintf("%s/%s", prefix
, path
);
725 oldvalue
= pkgdb_retrieve(fullpath
);
727 if (strncmp(oldvalue
, "@pkgdir ", 8) != 0)
728 errx(EXIT_FAILURE
, "Internal error while processing pkgdb, run pkg_admin rebuild");
729 newvalue
= xasprintf("%s %s", oldvalue
, pkg
);
730 pkgdb_remove(fullpath
);
732 newvalue
= xasprintf("@pkgdir %s", pkg
);
734 pkgdb_store(fullpath
, newvalue
);
741 delete_pkgdir(const char *pkg
, const char *prefix
, const char *path
)
744 char *fullpath
, *oldvalue
, *newvalue
, *iter
;
746 fullpath
= xasprintf("%s/%s", prefix
, path
);
747 oldvalue
= pkgdb_retrieve(fullpath
);
748 if (oldvalue
&& strncmp(oldvalue
, "@pkgdir ", 8) == 0) {
749 newvalue
= xstrdup(oldvalue
);
751 pkg_len
= strlen(pkg
);
753 if (strncmp(iter
, pkg
, pkg_len
) == 0 &&
754 (iter
[pkg_len
] == ' ' || iter
[pkg_len
] == '\0')) {
755 len
= strlen(iter
+ pkg_len
);
756 memmove(iter
, iter
+ pkg_len
+ 1, len
);
760 iter
+= strcspn(iter
, " ");
761 iter
+= strspn(iter
, " ");
764 pkgdb_remove(fullpath
);
765 if (iter
!= newvalue
+ 8)
766 pkgdb_store(fullpath
, newvalue
);
773 has_pkgdir(const char *path
)
777 value
= pkgdb_retrieve(path
);
779 if (value
&& strncmp(value
, "@pkgdir ", 8) == 0)