plist.c revision 27137
1#ifndef lint
2static const char *rcsid = "$Id: plist.c,v 1.21 1997/06/30 03:15:40 jkh Exp $";
3#endif
4
5/*
6 * FreeBSD install - a package for the installation and maintainance
7 * of non-core utilities.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * Jordan K. Hubbard
19 * 18 July 1993
20 *
21 * General packing list routines.
22 *
23 */
24
25#include "lib.h"
26#include <errno.h>
27#include <md5.h>
28
29/* Add an item to a packing list */
30void
31add_plist(Package *p, plist_t type, char *arg)
32{
33    PackingList tmp;
34
35    tmp = new_plist_entry();
36    tmp->name = copy_string(arg);
37    tmp->type = type;
38
39    if (!p->head)
40	p->head = p->tail = tmp;
41    else {
42	tmp->prev = p->tail;
43	p->tail->next = tmp;
44	p->tail = tmp;
45    }
46}
47
48void
49add_plist_top(Package *p, plist_t type, char *arg)
50{
51    PackingList tmp;
52
53    tmp = new_plist_entry();
54    tmp->name = copy_string(arg);
55    tmp->type = type;
56
57    if (!p->head)
58	p->head = p->tail = tmp;
59    else {
60	tmp->next = p->head;
61	p->head->prev = tmp;
62	p->head = tmp;
63    }
64}
65
66/* Return the last (most recent) entry in a packing list */
67PackingList
68last_plist(Package *p)
69{
70    return p->tail;
71}
72
73/* Mark all items in a packing list to prevent iteration over them */
74void
75mark_plist(Package *pkg)
76{
77    PackingList p = pkg->head;
78
79    while (p) {
80	p->marked = TRUE;
81	p = p->next;
82    }
83}
84
85/* Find a given item in a packing list and, if so, return it (else NULL) */
86PackingList
87find_plist(Package *pkg, plist_t type)
88{
89    PackingList p = pkg->head;
90
91    while (p) {
92	if (p->type == type)
93	    return p;
94	p = p->next;
95    }
96    return NULL;
97}
98
99/* Look for a specific boolean option argument in the list */
100char *
101find_plist_option(Package *pkg, char *name)
102{
103    PackingList p = pkg->head;
104
105    while (p) {
106	if (p->type == PLIST_OPTION && !strcmp(p->name, name))
107	    return p->name;
108	p = p->next;
109    }
110    return NULL;
111}
112
113/*
114 * Delete plist item 'type' in the list (if 'name' is non-null, match it
115 * too.)  If 'all' is set, delete all items, not just the first occurance.
116 */
117void
118delete_plist(Package *pkg, Boolean all, plist_t type, char *name)
119{
120    PackingList p = pkg->head;
121
122    while (p) {
123	PackingList pnext = p->next;
124
125	if (p->type == type && (!name || !strcmp(name, p->name))) {
126	    free(p->name);
127	    if (p->prev)
128		p->prev->next = pnext;
129	    else
130		pkg->head = pnext;
131	    if (pnext)
132		pnext->prev = p->prev;
133	    else
134		pkg->tail = p->prev;
135	    free(p);
136	    if (!all)
137		return;
138	    p = pnext;
139	}
140	else
141	    p = p->next;
142    }
143}
144
145/* Allocate a new packing list entry */
146PackingList
147new_plist_entry(void)
148{
149    PackingList ret;
150
151    ret = (PackingList)malloc(sizeof(struct _plist));
152    bzero(ret, sizeof(struct _plist));
153    return ret;
154}
155
156/* Free an entire packing list */
157void
158free_plist(Package *pkg)
159{
160    PackingList p = pkg->head;
161
162    while (p) {
163	PackingList p1 = p->next;
164
165	free(p->name);
166	free(p);
167	p = p1;
168    }
169    pkg->head = pkg->tail = NULL;
170}
171
172/*
173 * For an ascii string denoting a plist command, return its code and
174 * optionally its argument(s)
175 */
176int
177plist_cmd(char *s, char **arg)
178{
179    char cmd[FILENAME_MAX + 20];	/* 20 == fudge for max cmd len */
180    char *cp, *sp;
181
182    strcpy(cmd, s);
183    str_lowercase(cmd);
184    cp = cmd;
185    sp = s;
186    while (*cp) {
187	if (isspace(*cp)) {
188	    *cp = '\0';
189	    while (isspace(*sp)) /* Never sure if macro, increment later */
190		++sp;
191	    break;
192	}
193	++cp, ++sp;
194    }
195    if (arg)
196	*arg = sp;
197    if (!strcmp(cmd, "cwd"))
198	return PLIST_CWD;
199    else if (!strcmp(cmd, "srcdir"))
200	return PLIST_SRC;
201    else if (!strcmp(cmd, "cd"))
202	return PLIST_CWD;
203    else if (!strcmp(cmd, "exec"))
204	return PLIST_CMD;
205    else if (!strcmp(cmd, "unexec"))
206	return PLIST_UNEXEC;
207    else if (!strcmp(cmd, "mode"))
208	return PLIST_CHMOD;
209    else if (!strcmp(cmd, "owner"))
210	return PLIST_CHOWN;
211    else if (!strcmp(cmd, "group"))
212	return PLIST_CHGRP;
213    else if (!strcmp(cmd, "comment"))
214	return PLIST_COMMENT;
215    else if (!strcmp(cmd, "ignore"))
216	return PLIST_IGNORE;
217    else if (!strcmp(cmd, "ignore_inst"))
218	return PLIST_IGNORE_INST;
219    else if (!strcmp(cmd, "name"))
220	return PLIST_NAME;
221    else if (!strcmp(cmd, "display"))
222	return PLIST_DISPLAY;
223    else if (!strcmp(cmd, "pkgdep"))
224	return PLIST_PKGDEP;
225    else if (!strcmp(cmd, "mtree"))
226	return PLIST_MTREE;
227    else if (!strcmp(cmd, "dirrm"))
228	return PLIST_DIR_RM;
229    else if (!strcmp(cmd, "option"))
230	return PLIST_OPTION;
231    else
232	return FAIL;
233}
234
235/* Read a packing list from a file */
236void
237read_plist(Package *pkg, FILE *fp)
238{
239    char *cp, pline[FILENAME_MAX];
240    int cmd;
241
242    while (fgets(pline, FILENAME_MAX, fp)) {
243	int len = strlen(pline);
244
245	while (len && isspace(pline[len - 1]))
246	    pline[--len] = '\0';
247	if (!len)
248	    continue;
249	cp = pline;
250	if (pline[0] == CMD_CHAR) {
251	    cmd = plist_cmd(pline + 1, &cp);
252	    if (cmd == FAIL)
253		barf("Bad command '%s'", pline);
254	    if (*cp == '\0')
255		cp = NULL;
256	}
257	else
258	    cmd = PLIST_FILE;
259	add_plist(pkg, cmd, cp);
260    }
261}
262
263/* Write a packing list to a file, converting commands to ascii equivs */
264void
265write_plist(Package *pkg, FILE *fp)
266{
267    PackingList plist = pkg->head;
268
269    while (plist) {
270	switch(plist->type) {
271	case PLIST_FILE:
272	    fprintf(fp, "%s\n", plist->name);
273	    break;
274
275	case PLIST_CWD:
276	    fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
277	    break;
278
279	case PLIST_SRC:
280	    fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
281	    break;
282
283	case PLIST_CMD:
284	    fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
285	    break;
286
287	case PLIST_UNEXEC:
288	    fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
289	    break;
290
291	case PLIST_CHMOD:
292	    fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
293	    break;
294
295	case PLIST_CHOWN:
296	    fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
297	    break;
298
299	case PLIST_CHGRP:
300	    fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
301	    break;
302
303	case PLIST_COMMENT:
304	    fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
305	    break;
306
307	case PLIST_IGNORE:
308	case PLIST_IGNORE_INST:		/* a one-time non-ignored file */
309	    fprintf(fp, "%cignore\n", CMD_CHAR);
310	    break;
311
312	case PLIST_NAME:
313	    fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
314	    break;
315
316	case PLIST_DISPLAY:
317	    fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
318	    break;
319
320	case PLIST_PKGDEP:
321	    fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
322	    break;
323
324	case PLIST_MTREE:
325	    fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
326	    break;
327
328	case PLIST_DIR_RM:
329	    fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
330	    break;
331
332	case PLIST_OPTION:
333	    fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
334	    break;
335
336	default:
337	    barf("Unknown command type %d (%s)\n", plist->type, plist->name);
338	    break;
339	}
340	plist = plist->next;
341    }
342}
343
344/*
345 * Delete the results of a package installation.
346 *
347 * This is here rather than in the pkg_delete code because pkg_add needs to
348 * run it too in cases of failure.
349 */
350int
351delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
352{
353    PackingList p;
354    char *Where = ".", *last_file = "";
355    Boolean fail = SUCCESS;
356    Boolean preserve;
357    char tmp[FILENAME_MAX], *name = NULL;
358
359    preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
360    for (p = pkg->head; p; p = p->next) {
361	switch (p->type)  {
362	case PLIST_NAME:
363	    name = p->name;
364	    break;
365
366	case PLIST_IGNORE:
367	    p = p->next;
368	    break;
369
370	case PLIST_CWD:
371	    Where = p->name;
372	    if (Verbose)
373		printf("Change working directory to %s\n", Where);
374	    break;
375
376	case PLIST_UNEXEC:
377	    format_cmd(tmp, p->name, Where, last_file);
378	    if (Verbose)
379		printf("Execute `%s'\n", tmp);
380	    if (!Fake && system(tmp)) {
381		whinge("unexec command for `%s' failed.", tmp);
382		fail = FAIL;
383	    }
384	    break;
385
386	case PLIST_FILE:
387	    last_file = p->name;
388	    sprintf(tmp, "%s/%s", Where, p->name);
389	    if (isdir(tmp)) {
390		whinge("Attempting to delete directory `%s' as a file\n"
391		       "This packing list is incorrect - ignoring delete request.\n", tmp);
392	    }
393	    else {
394		if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) {
395		    char *cp, buf[33];
396
397		    if ((cp = MD5File(tmp, buf)) != NULL) {
398			/* Mismatch? */
399			if (strcmp(cp, p->next->name + 4)) {
400			    if (Verbose)
401				printf("%s fails original MD5 checksum - %s\n",
402				       tmp, Force ? "deleted anyway." : "not deleted.");
403			    if (!Force) {
404				fail = FAIL;
405				continue;
406			    }
407			}
408		    }
409		}
410		if (Verbose)
411		    printf("Delete file %s\n", tmp);
412		if (!Fake) {
413		    if (delete_hierarchy(tmp, ign_err, nukedirs)) {
414			whinge("Unable to completely remove file '%s'", tmp);
415			fail = FAIL;
416		    }
417		    if (preserve && name) {
418			char tmp2[FILENAME_MAX];
419
420			if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) {
421			    if (fexists(tmp2)) {
422				if (rename(tmp2, tmp))
423				    whinge("preserve:  Unable to restore %s as %s, errno = %d", tmp2, tmp, errno);
424			    }
425			}
426		    }
427		}
428	    }
429	    break;
430
431	case PLIST_DIR_RM:
432	    sprintf(tmp, "%s/%s", Where, p->name);
433	    if (!isdir(tmp)) {
434		whinge("Attempting to delete file `%s' as a directory\n"
435		       "This packing list is incorrect - ignoring delete request.\n", tmp);
436	    }
437	    else {
438		if (Verbose)
439		    printf("Delete directory %s\n", tmp);
440		if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) {
441		    whinge("Unable to completely remove directory '%s'", tmp);
442		    fail = FAIL;
443		}
444	    }
445	    last_file = p->name;
446	    break;
447	}
448    }
449    return fail;
450}
451
452#ifdef DEBUG
453#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
454#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
455#else
456#define RMDIR rmdir
457#define	REMOVE(file,ie) (remove(file) && !(ie))
458#endif
459
460/* Selectively delete a hierarchy */
461int
462delete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs)
463{
464    char *cp1, *cp2;
465
466    cp1 = cp2 = dir;
467    if (!fexists(dir)) {
468	if (!ign_err)
469	    whinge("%s `%s' doesn't really exist.", isdir(dir) ? "Directory" : "File", dir);
470	return !ign_err;
471    }
472    else if (nukedirs) {
473	if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
474	    return 1;
475    }
476    else if (isdir(dir)) {
477	if (RMDIR(dir) && !ign_err)
478	    return 1;
479    }
480    else {
481	if (REMOVE(dir, ign_err))
482	    return 1;
483    }
484
485    if (!nukedirs)
486	return 0;
487    while (cp2) {
488	if ((cp2 = rindex(cp1, '/')) != NULL)
489	    *cp2 = '\0';
490	if (!isemptydir(dir))
491	    return 0;
492	if (RMDIR(dir) && !ign_err)
493	    if (!fexists(dir))
494		whinge("Directory `%s' doesn't really exist.", dir);
495	    else
496		return 1;
497	/* back up the pathname one component */
498	if (cp2) {
499	    cp1 = dir;
500	}
501    }
502    return 0;
503}
504