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