plist.c revision 71373
1327Sjkh#ifndef lint
230221Scharnierstatic const char rcsid[] =
350479Speter  "$FreeBSD: head/usr.sbin/pkg_install/lib/plist.c 71373 2001-01-22 12:01:55Z sobomax $";
4327Sjkh#endif
5327Sjkh
6327Sjkh/*
7327Sjkh * FreeBSD install - a package for the installation and maintainance
8327Sjkh * of non-core utilities.
9327Sjkh *
10327Sjkh * Redistribution and use in source and binary forms, with or without
11327Sjkh * modification, are permitted provided that the following conditions
12327Sjkh * are met:
13327Sjkh * 1. Redistributions of source code must retain the above copyright
14327Sjkh *    notice, this list of conditions and the following disclaimer.
15327Sjkh * 2. Redistributions in binary form must reproduce the above copyright
16327Sjkh *    notice, this list of conditions and the following disclaimer in the
17327Sjkh *    documentation and/or other materials provided with the distribution.
18327Sjkh *
19327Sjkh * Jordan K. Hubbard
20327Sjkh * 18 July 1993
21327Sjkh *
22327Sjkh * General packing list routines.
23327Sjkh *
24327Sjkh */
25327Sjkh
26327Sjkh#include "lib.h"
2730221Scharnier#include <err.h>
2817338Sjkh#include <md5.h>
29327Sjkh
30327Sjkh/* Add an item to a packing list */
31327Sjkhvoid
32379Sjkhadd_plist(Package *p, plist_t type, char *arg)
33327Sjkh{
34327Sjkh    PackingList tmp;
35327Sjkh
36327Sjkh    tmp = new_plist_entry();
37327Sjkh    tmp->name = copy_string(arg);
38327Sjkh    tmp->type = type;
39327Sjkh
40327Sjkh    if (!p->head)
41327Sjkh	p->head = p->tail = tmp;
42327Sjkh    else {
43327Sjkh	tmp->prev = p->tail;
44327Sjkh	p->tail->next = tmp;
45327Sjkh	p->tail = tmp;
46327Sjkh    }
47327Sjkh}
48327Sjkh
49379Sjkhvoid
50379Sjkhadd_plist_top(Package *p, plist_t type, char *arg)
51379Sjkh{
52379Sjkh    PackingList tmp;
53379Sjkh
54379Sjkh    tmp = new_plist_entry();
55379Sjkh    tmp->name = copy_string(arg);
56379Sjkh    tmp->type = type;
57379Sjkh
58379Sjkh    if (!p->head)
59379Sjkh	p->head = p->tail = tmp;
60379Sjkh    else {
61379Sjkh	tmp->next = p->head;
62379Sjkh	p->head->prev = tmp;
63379Sjkh	p->head = tmp;
64379Sjkh    }
65379Sjkh}
66379Sjkh
67327Sjkh/* Return the last (most recent) entry in a packing list */
68327SjkhPackingList
69327Sjkhlast_plist(Package *p)
70327Sjkh{
71327Sjkh    return p->tail;
72327Sjkh}
73327Sjkh
74327Sjkh/* Mark all items in a packing list to prevent iteration over them */
75327Sjkhvoid
76327Sjkhmark_plist(Package *pkg)
77327Sjkh{
78327Sjkh    PackingList p = pkg->head;
79327Sjkh
80327Sjkh    while (p) {
81327Sjkh	p->marked = TRUE;
82327Sjkh	p = p->next;
83327Sjkh    }
84327Sjkh}
85327Sjkh
861547Sjkh/* Find a given item in a packing list and, if so, return it (else NULL) */
871547SjkhPackingList
881547Sjkhfind_plist(Package *pkg, plist_t type)
89379Sjkh{
90379Sjkh    PackingList p = pkg->head;
91379Sjkh
92379Sjkh    while (p) {
93379Sjkh	if (p->type == type)
941547Sjkh	    return p;
95379Sjkh	p = p->next;
96379Sjkh    }
971547Sjkh    return NULL;
98379Sjkh}
998857Srgrimes
1007996Sjkh/* Look for a specific boolean option argument in the list */
1017996Sjkhchar *
1027996Sjkhfind_plist_option(Package *pkg, char *name)
1037996Sjkh{
1047996Sjkh    PackingList p = pkg->head;
1057996Sjkh
1067996Sjkh    while (p) {
1077996Sjkh	if (p->type == PLIST_OPTION && !strcmp(p->name, name))
1087996Sjkh	    return p->name;
1097996Sjkh	p = p->next;
1107996Sjkh    }
1117996Sjkh    return NULL;
1127996Sjkh}
1137996Sjkh
114383Sjkh/*
115383Sjkh * Delete plist item 'type' in the list (if 'name' is non-null, match it
116383Sjkh * too.)  If 'all' is set, delete all items, not just the first occurance.
117383Sjkh */
118383Sjkhvoid
119383Sjkhdelete_plist(Package *pkg, Boolean all, plist_t type, char *name)
120383Sjkh{
121383Sjkh    PackingList p = pkg->head;
122383Sjkh
123383Sjkh    while (p) {
124383Sjkh	PackingList pnext = p->next;
125383Sjkh
126383Sjkh	if (p->type == type && (!name || !strcmp(name, p->name))) {
127383Sjkh	    free(p->name);
128383Sjkh	    if (p->prev)
129383Sjkh		p->prev->next = pnext;
130383Sjkh	    else
131383Sjkh		pkg->head = pnext;
132383Sjkh	    if (pnext)
133383Sjkh		pnext->prev = p->prev;
134383Sjkh	    else
135383Sjkh		pkg->tail = p->prev;
136383Sjkh	    free(p);
137383Sjkh	    if (!all)
138383Sjkh		return;
139383Sjkh	    p = pnext;
140383Sjkh	}
141383Sjkh	else
142383Sjkh	    p = p->next;
143383Sjkh    }
144383Sjkh}
1458857Srgrimes
146327Sjkh/* Allocate a new packing list entry */
147327SjkhPackingList
148327Sjkhnew_plist_entry(void)
149327Sjkh{
150327Sjkh    PackingList ret;
151327Sjkh
152327Sjkh    ret = (PackingList)malloc(sizeof(struct _plist));
153327Sjkh    bzero(ret, sizeof(struct _plist));
154327Sjkh    return ret;
155327Sjkh}
156327Sjkh
157327Sjkh/* Free an entire packing list */
158327Sjkhvoid
159327Sjkhfree_plist(Package *pkg)
160327Sjkh{
161327Sjkh    PackingList p = pkg->head;
162327Sjkh
163327Sjkh    while (p) {
164327Sjkh	PackingList p1 = p->next;
165327Sjkh
166327Sjkh	free(p->name);
167327Sjkh	free(p);
168327Sjkh	p = p1;
169327Sjkh    }
170327Sjkh    pkg->head = pkg->tail = NULL;
171327Sjkh}
172327Sjkh
173327Sjkh/*
174327Sjkh * For an ascii string denoting a plist command, return its code and
175327Sjkh * optionally its argument(s)
176327Sjkh */
177327Sjkhint
178327Sjkhplist_cmd(char *s, char **arg)
179327Sjkh{
180327Sjkh    char cmd[FILENAME_MAX + 20];	/* 20 == fudge for max cmd len */
181327Sjkh    char *cp, *sp;
182327Sjkh
183327Sjkh    strcpy(cmd, s);
184327Sjkh    str_lowercase(cmd);
185327Sjkh    cp = cmd;
186327Sjkh    sp = s;
187327Sjkh    while (*cp) {
188327Sjkh	if (isspace(*cp)) {
189327Sjkh	    *cp = '\0';
190327Sjkh	    while (isspace(*sp)) /* Never sure if macro, increment later */
191327Sjkh		++sp;
192327Sjkh	    break;
193327Sjkh	}
194327Sjkh	++cp, ++sp;
195327Sjkh    }
196327Sjkh    if (arg)
197327Sjkh	*arg = sp;
198327Sjkh    if (!strcmp(cmd, "cwd"))
199327Sjkh	return PLIST_CWD;
2002331Sjkh    else if (!strcmp(cmd, "srcdir"))
2012331Sjkh	return PLIST_SRC;
202379Sjkh    else if (!strcmp(cmd, "cd"))
203379Sjkh	return PLIST_CWD;
204327Sjkh    else if (!strcmp(cmd, "exec"))
205327Sjkh	return PLIST_CMD;
206479Sjkh    else if (!strcmp(cmd, "unexec"))
207479Sjkh	return PLIST_UNEXEC;
208327Sjkh    else if (!strcmp(cmd, "mode"))
209327Sjkh	return PLIST_CHMOD;
210327Sjkh    else if (!strcmp(cmd, "owner"))
211327Sjkh	return PLIST_CHOWN;
212327Sjkh    else if (!strcmp(cmd, "group"))
213327Sjkh	return PLIST_CHGRP;
214327Sjkh    else if (!strcmp(cmd, "comment"))
215327Sjkh	return PLIST_COMMENT;
216327Sjkh    else if (!strcmp(cmd, "ignore"))
217327Sjkh	return PLIST_IGNORE;
2184996Sjkh    else if (!strcmp(cmd, "ignore_inst"))
2194996Sjkh	return PLIST_IGNORE_INST;
220327Sjkh    else if (!strcmp(cmd, "name"))
221327Sjkh	return PLIST_NAME;
2224996Sjkh    else if (!strcmp(cmd, "display"))
2234996Sjkh	return PLIST_DISPLAY;
2244996Sjkh    else if (!strcmp(cmd, "pkgdep"))
2254996Sjkh	return PLIST_PKGDEP;
2264996Sjkh    else if (!strcmp(cmd, "mtree"))
2274996Sjkh	return PLIST_MTREE;
2284996Sjkh    else if (!strcmp(cmd, "dirrm"))
2294996Sjkh	return PLIST_DIR_RM;
2307996Sjkh    else if (!strcmp(cmd, "option"))
2317996Sjkh	return PLIST_OPTION;
232327Sjkh    else
233327Sjkh	return FAIL;
234327Sjkh}
235327Sjkh
236327Sjkh/* Read a packing list from a file */
237327Sjkhvoid
238327Sjkhread_plist(Package *pkg, FILE *fp)
239327Sjkh{
240327Sjkh    char *cp, pline[FILENAME_MAX];
241327Sjkh    int cmd;
242327Sjkh
243327Sjkh    while (fgets(pline, FILENAME_MAX, fp)) {
24417338Sjkh	int len = strlen(pline);
245327Sjkh
24617338Sjkh	while (len && isspace(pline[len - 1]))
24717338Sjkh	    pline[--len] = '\0';
24817338Sjkh	if (!len)
249327Sjkh	    continue;
250327Sjkh	cp = pline;
251327Sjkh	if (pline[0] == CMD_CHAR) {
252327Sjkh	    cmd = plist_cmd(pline + 1, &cp);
25339068Sjkh	    if (cmd == FAIL) {
25439068Sjkh		cleanup(0);
25567429Sjkh		errx(2, __FUNCTION__ ": bad command '%s'", pline);
25639068Sjkh	    }
257479Sjkh	    if (*cp == '\0')
258479Sjkh		cp = NULL;
259327Sjkh	}
260327Sjkh	else
2618857Srgrimes	    cmd = PLIST_FILE;
262327Sjkh	add_plist(pkg, cmd, cp);
263327Sjkh    }
264327Sjkh}
265327Sjkh
266327Sjkh/* Write a packing list to a file, converting commands to ascii equivs */
267327Sjkhvoid
268327Sjkhwrite_plist(Package *pkg, FILE *fp)
269327Sjkh{
270327Sjkh    PackingList plist = pkg->head;
271327Sjkh
272327Sjkh    while (plist) {
273327Sjkh	switch(plist->type) {
274327Sjkh	case PLIST_FILE:
275327Sjkh	    fprintf(fp, "%s\n", plist->name);
276327Sjkh	    break;
277327Sjkh
278327Sjkh	case PLIST_CWD:
279327Sjkh	    fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
280327Sjkh	    break;
281327Sjkh
2822331Sjkh	case PLIST_SRC:
2832331Sjkh	    fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
2842331Sjkh	    break;
2852331Sjkh
286327Sjkh	case PLIST_CMD:
287327Sjkh	    fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
288327Sjkh	    break;
289327Sjkh
290479Sjkh	case PLIST_UNEXEC:
291479Sjkh	    fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
292479Sjkh	    break;
293479Sjkh
294327Sjkh	case PLIST_CHMOD:
29512219Sjkh	    fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
296327Sjkh	    break;
297327Sjkh
298327Sjkh	case PLIST_CHOWN:
29912219Sjkh	    fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
300327Sjkh	    break;
301327Sjkh
302327Sjkh	case PLIST_CHGRP:
30312219Sjkh	    fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
304327Sjkh	    break;
305327Sjkh
306327Sjkh	case PLIST_COMMENT:
307327Sjkh	    fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
308327Sjkh	    break;
309327Sjkh
310327Sjkh	case PLIST_IGNORE:
3114996Sjkh	case PLIST_IGNORE_INST:		/* a one-time non-ignored file */
312327Sjkh	    fprintf(fp, "%cignore\n", CMD_CHAR);
313327Sjkh	    break;
314327Sjkh
315327Sjkh	case PLIST_NAME:
316327Sjkh	    fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
317327Sjkh	    break;
318327Sjkh
3194996Sjkh	case PLIST_DISPLAY:
3204996Sjkh	    fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
3214996Sjkh	    break;
3224996Sjkh
3234996Sjkh	case PLIST_PKGDEP:
3244996Sjkh	    fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
3254996Sjkh	    break;
3264996Sjkh
3274996Sjkh	case PLIST_MTREE:
3284996Sjkh	    fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
3294996Sjkh	    break;
3304996Sjkh
3314996Sjkh	case PLIST_DIR_RM:
3324996Sjkh	    fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
3334996Sjkh	    break;
3344996Sjkh
3357996Sjkh	case PLIST_OPTION:
3367996Sjkh	    fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
3377996Sjkh	    break;
3387996Sjkh
339327Sjkh	default:
34030221Scharnier	    cleanup(0);
34167429Sjkh	    errx(2, __FUNCTION__ ": unknown command type %d (%s)", plist->type, plist->name);
342327Sjkh	    break;
343327Sjkh	}
344327Sjkh	plist = plist->next;
345327Sjkh    }
346327Sjkh}
347327Sjkh
3487996Sjkh/*
3497996Sjkh * Delete the results of a package installation.
3507996Sjkh *
3517996Sjkh * This is here rather than in the pkg_delete code because pkg_add needs to
3527996Sjkh * run it too in cases of failure.
3537996Sjkh */
3543198Sjkhint
3554996Sjkhdelete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
356327Sjkh{
35717338Sjkh    PackingList p;
358479Sjkh    char *Where = ".", *last_file = "";
3593198Sjkh    Boolean fail = SUCCESS;
36027056Sjkh    Boolean preserve;
36127056Sjkh    char tmp[FILENAME_MAX], *name = NULL;
362327Sjkh
36327056Sjkh    preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
36417338Sjkh    for (p = pkg->head; p; p = p->next) {
36517338Sjkh	switch (p->type)  {
36627056Sjkh	case PLIST_NAME:
36727056Sjkh	    name = p->name;
36827056Sjkh	    break;
36927056Sjkh
37017338Sjkh	case PLIST_IGNORE:
37117338Sjkh	    p = p->next;
37217338Sjkh	    break;
37317338Sjkh
37417338Sjkh	case PLIST_CWD:
375327Sjkh	    Where = p->name;
376327Sjkh	    if (Verbose)
3779743Sache		printf("Change working directory to %s\n", Where);
37817338Sjkh	    break;
379479Sjkh
38017338Sjkh	case PLIST_UNEXEC:
38117338Sjkh	    format_cmd(tmp, p->name, Where, last_file);
382479Sjkh	    if (Verbose)
38317338Sjkh		printf("Execute `%s'\n", tmp);
38417338Sjkh	    if (!Fake && system(tmp)) {
38530221Scharnier		warnx("unexec command for `%s' failed", tmp);
3863198Sjkh		fail = FAIL;
3873198Sjkh	    }
38817338Sjkh	    break;
389327Sjkh
39017338Sjkh	case PLIST_FILE:
39127092Sjkh	    last_file = p->name;
39217338Sjkh	    sprintf(tmp, "%s/%s", Where, p->name);
39366021Ssobomax	    if (isdir(tmp) && fexists(tmp) && !issymlink(tmp)) {
39437744Shoek		warnx("cannot delete specified file `%s' - it is a directory!\n"
39530221Scharnier	   "this packing list is incorrect - ignoring delete request", tmp);
39612219Sjkh	    }
39712219Sjkh	    else {
39817338Sjkh		if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) {
39917338Sjkh		    char *cp, buf[33];
40017338Sjkh
40117338Sjkh		    if ((cp = MD5File(tmp, buf)) != NULL) {
40217338Sjkh			/* Mismatch? */
40317338Sjkh			if (strcmp(cp, p->next->name + 4)) {
40417338Sjkh			    if (Verbose)
40517338Sjkh				printf("%s fails original MD5 checksum - %s\n",
40617338Sjkh				       tmp, Force ? "deleted anyway." : "not deleted.");
40717338Sjkh			    if (!Force) {
40817338Sjkh				fail = FAIL;
40917338Sjkh				continue;
41017338Sjkh			    }
41117338Sjkh			}
41217338Sjkh		    }
41317338Sjkh		}
41412219Sjkh		if (Verbose)
41517338Sjkh		    printf("Delete file %s\n", tmp);
41627092Sjkh		if (!Fake) {
41729032Sjkh		    if (delete_hierarchy(tmp, ign_err, nukedirs))
41827092Sjkh			fail = FAIL;
41927137Sjkh		    if (preserve && name) {
42027137Sjkh			char tmp2[FILENAME_MAX];
42127092Sjkh
42227137Sjkh			if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) {
42327092Sjkh			    if (fexists(tmp2)) {
42427092Sjkh				if (rename(tmp2, tmp))
42530221Scharnier				   warn("preserve: unable to restore %s as %s",
42630221Scharnier					tmp2, tmp);
42727092Sjkh			    }
42827056Sjkh			}
42927056Sjkh		    }
43027056Sjkh		}
4313198Sjkh	    }
43217338Sjkh	    break;
43317338Sjkh
43417338Sjkh	case PLIST_DIR_RM:
43517338Sjkh	    sprintf(tmp, "%s/%s", Where, p->name);
43638723Sjkh	    if (!isdir(tmp) && fexists(tmp)) {
43737744Shoek		warnx("cannot delete specified directory `%s' - it is a file!\n"
43830221Scharnier	"this packing list is incorrect - ignoring delete request", tmp);
43917338Sjkh	    }
44017338Sjkh	    else {
44117338Sjkh		if (Verbose)
44217338Sjkh		    printf("Delete directory %s\n", tmp);
44317338Sjkh		if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) {
44430221Scharnier		    warnx("unable to completely remove directory '%s'", tmp);
44517338Sjkh		    fail = FAIL;
44617338Sjkh		}
44717338Sjkh	    }
44817338Sjkh	    last_file = p->name;
44917338Sjkh	    break;
45071373Ssobomax
45171373Ssobomax	default:
45271373Ssobomax	    break;
453327Sjkh	}
454327Sjkh    }
4553198Sjkh    return fail;
456327Sjkh}
457327Sjkh
4584996Sjkh#ifdef DEBUG
4594996Sjkh#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
4604996Sjkh#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
4614996Sjkh#else
4624996Sjkh#define RMDIR rmdir
4637989Sjkh#define	REMOVE(file,ie) (remove(file) && !(ie))
4644996Sjkh#endif
4654996Sjkh
466327Sjkh/* Selectively delete a hierarchy */
467327Sjkhint
4684996Sjkhdelete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs)
469327Sjkh{
470327Sjkh    char *cp1, *cp2;
4718857Srgrimes
472327Sjkh    cp1 = cp2 = dir;
4737989Sjkh    if (!fexists(dir)) {
4747989Sjkh	if (!ign_err)
47530221Scharnier	    warnx("%s `%s' doesn't really exist",
47630221Scharnier		isdir(dir) ? "directory" : "file", dir);
47727092Sjkh	return !ign_err;
47827092Sjkh    }
47927092Sjkh    else if (nukedirs) {
4804996Sjkh	if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
4814996Sjkh	    return 1;
48227092Sjkh    }
48366021Ssobomax    else if (isdir(dir) && !issymlink(dir)) {
4847989Sjkh	if (RMDIR(dir) && !ign_err)
4854996Sjkh	    return 1;
48627092Sjkh    }
48727092Sjkh    else {
4884996Sjkh	if (REMOVE(dir, ign_err))
4894996Sjkh	    return 1;
4904996Sjkh    }
4914996Sjkh
4924996Sjkh    if (!nukedirs)
4934996Sjkh	return 0;
494327Sjkh    while (cp2) {
49567429Sjkh	if ((cp2 = strrchr(cp1, '/')) != NULL)
496327Sjkh	    *cp2 = '\0';
4974996Sjkh	if (!isemptydir(dir))
498327Sjkh	    return 0;
49949637Sbillf	if (RMDIR(dir) && !ign_err) {
5007989Sjkh	    if (!fexists(dir))
50130221Scharnier		warnx("directory `%s' doesn't really exist", dir);
5027989Sjkh	    else
5037989Sjkh		return 1;
50449637Sbillf	}
5054996Sjkh	/* back up the pathname one component */
506327Sjkh	if (cp2) {
5074996Sjkh	    cp1 = dir;
508327Sjkh	}
509327Sjkh    }
510327Sjkh    return 0;
511327Sjkh}
512