1327Sjkh/* 2327Sjkh * FreeBSD install - a package for the installation and maintainance 3327Sjkh * of non-core utilities. 4327Sjkh * 5327Sjkh * Redistribution and use in source and binary forms, with or without 6327Sjkh * modification, are permitted provided that the following conditions 7327Sjkh * are met: 8327Sjkh * 1. Redistributions of source code must retain the above copyright 9327Sjkh * notice, this list of conditions and the following disclaimer. 10327Sjkh * 2. Redistributions in binary form must reproduce the above copyright 11327Sjkh * notice, this list of conditions and the following disclaimer in the 12327Sjkh * documentation and/or other materials provided with the distribution. 13327Sjkh * 14327Sjkh * Jordan K. Hubbard 15327Sjkh * 18 July 1993 16327Sjkh * 17327Sjkh * This is the main body of the delete module. 18327Sjkh * 19327Sjkh */ 20327Sjkh 2193520Sobrien#include <sys/cdefs.h> 2293520Sobrien__FBSDID("$FreeBSD$"); 2393520Sobrien 2430221Scharnier#include <err.h> 25222035Sflz#include "lib.h" 26327Sjkh#include "delete.h" 27327Sjkh 28327Sjkhstatic int pkg_do(char *); 29327Sjkhstatic void sanity_check(char *); 3096613Ssobomaxstatic void undepend(char *, char *); 31327Sjkhstatic char LogDir[FILENAME_MAX]; 32327Sjkh 33327Sjkh 34327Sjkhint 35327Sjkhpkg_perform(char **pkgs) 36327Sjkh{ 3783663Ssobomax char **matched, **rb, **rbtmp; 3883663Ssobomax int errcode, i, j; 3972694Ssobomax int err_cnt = 0; 4083663Ssobomax struct reqr_by_entry *rb_entry; 4183663Ssobomax struct reqr_by_head *rb_list; 42327Sjkh 4373134Ssobomax if (MatchType != MATCH_EXACT) { 4473134Ssobomax matched = matchinstalled(MatchType, pkgs, &errcode); 4573134Ssobomax if (errcode != 0) 4673134Ssobomax return 1; 4773134Ssobomax /* Not reached */ 4873134Ssobomax 4996613Ssobomax /* 5096613Ssobomax * Copy matched[] into pkgs[], because we'll need to use 5196613Ssobomax * matchinstalled() later on. 5296613Ssobomax */ 5396613Ssobomax if (matched != NULL) { 5496613Ssobomax pkgs = NULL; 5596613Ssobomax for (i = 0; matched[i] != NULL; i++) { 5696613Ssobomax pkgs = realloc(pkgs, sizeof(*pkgs) * (i + 2)); 5796613Ssobomax pkgs[i] = strdup(matched[i]); 5896613Ssobomax } 5996613Ssobomax pkgs[i] = NULL; 6096613Ssobomax } 6173134Ssobomax else switch (MatchType) { 6273134Ssobomax case MATCH_GLOB: 6373134Ssobomax break; 6473134Ssobomax case MATCH_ALL: 6573134Ssobomax warnx("no packages installed"); 6673134Ssobomax return 0; 67131275Seik case MATCH_EREGEX: 6873134Ssobomax case MATCH_REGEX: 6973134Ssobomax warnx("no packages match pattern(s)"); 7073134Ssobomax return 1; 7173134Ssobomax default: 7273134Ssobomax break; 7373134Ssobomax } 7473134Ssobomax } 7573134Ssobomax 7674295Ssobomax err_cnt += sortdeps(pkgs); 7772694Ssobomax for (i = 0; pkgs[i]; i++) { 7883663Ssobomax if (Recursive == TRUE) { 7983663Ssobomax errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE); 8083663Ssobomax if (errcode < 0) { 8183663Ssobomax err_cnt++; 8283663Ssobomax } else if (errcode > 0) { 8383663Ssobomax /* 8483663Ssobomax * Copy values from the rb_list queue into argv-like NULL 8583663Ssobomax * terminated list because requiredby() uses some static 8683663Ssobomax * storage, while pkg_do() below will call this function, 8783663Ssobomax * thus blowing our rb_list away. 8883663Ssobomax */ 8983663Ssobomax rbtmp = rb = alloca((errcode + 1) * sizeof(*rb)); 9083663Ssobomax if (rb == NULL) { 9196392Salfred warnx("%s(): alloca() failed", __func__); 9283663Ssobomax err_cnt++; 9383663Ssobomax continue; 9483663Ssobomax } 9583663Ssobomax STAILQ_FOREACH(rb_entry, rb_list, link) { 9683663Ssobomax *rbtmp = alloca(strlen(rb_entry->pkgname) + 1); 9783663Ssobomax if (*rbtmp == NULL) { 9896392Salfred warnx("%s(): alloca() failed", __func__); 9983663Ssobomax err_cnt++; 10083663Ssobomax continue; 10183663Ssobomax } 10283663Ssobomax strcpy(*rbtmp, rb_entry->pkgname); 10383663Ssobomax rbtmp++; 10483663Ssobomax } 10583663Ssobomax *rbtmp = NULL; 10683663Ssobomax 10783663Ssobomax err_cnt += sortdeps(rb); 10883663Ssobomax for (j = 0; rb[j]; j++) 10983663Ssobomax err_cnt += pkg_do(rb[j]); 11083663Ssobomax } 11183663Ssobomax } 112327Sjkh err_cnt += pkg_do(pkgs[i]); 11372694Ssobomax } 11472694Ssobomax 115327Sjkh return err_cnt; 116327Sjkh} 117327Sjkh 118327Sjkhstatic Package Plist; 119327Sjkh 120327Sjkh/* This is seriously ugly code following. Written very fast! */ 121327Sjkhstatic int 122327Sjkhpkg_do(char *pkg) 123327Sjkh{ 124327Sjkh FILE *cfile; 125178753Spav char *deporigin, **deporigins = NULL, **depnames = NULL, ***depmatches, home[FILENAME_MAX]; 1261550Sasami PackingList p; 12796613Ssobomax int i, len; 128131280Seik int isinstalled; 12941866Sjkh /* support for separate pre/post install scripts */ 130178103Spav int new_m = 0, dep_count = 0; 131111486Sdes const char *pre_script = DEINSTALL_FNAME; 132111486Sdes const char *post_script, *pre_arg, *post_arg; 13383663Ssobomax struct reqr_by_entry *rb_entry; 13483663Ssobomax struct reqr_by_head *rb_list; 135206043Sflz int fd; 136206043Sflz struct stat sb; 137327Sjkh 13841080Sjkh if (!pkg || !(len = strlen(pkg))) 13941080Sjkh return 1; 14041080Sjkh if (pkg[len - 1] == '/') 14141080Sjkh pkg[len - 1] = '\0'; 14241080Sjkh 143327Sjkh /* Reset some state */ 144327Sjkh if (Plist.head) 145327Sjkh free_plist(&Plist); 146327Sjkh 147131280Seik sprintf(LogDir, "%s/%s", LOG_DIR, pkg); 148131280Seik 149131280Seik isinstalled = isinstalledpkg(pkg); 150131280Seik if (isinstalled == 0) { 15130221Scharnier warnx("no such package '%s' installed", pkg); 152327Sjkh return 1; 153131280Seik } else if (isinstalled < 0) { 154131280Seik warnx("the package info for package '%s' is corrupt%s", 155131280Seik pkg, Force ? " (but I'll delete it anyway)" : " (use -f to force removal)"); 156131280Seik if (!Force) 157131280Seik return 1; 158131280Seik if (!Fake) { 159131280Seik if (vsystem("%s -rf %s", REMOVE_CMD, LogDir)) { 160131280Seik warnx("couldn't remove log entry in %s, deinstall failed", LogDir); 161131280Seik } else { 162131280Seik warnx("couldn't completely deinstall package '%s',\n" 163131280Seik "only the log entry in %s was removed", pkg, LogDir); 164131280Seik } 165131280Seik } 166131280Seik return 0; 167327Sjkh } 16841866Sjkh 16939068Sjkh if (!getcwd(home, FILENAME_MAX)) { 17039068Sjkh cleanup(0); 17196392Salfred errx(2, "%s: unable to get current working directory!", __func__); 17239068Sjkh } 17341866Sjkh 174327Sjkh if (chdir(LogDir) == FAIL) { 17530221Scharnier warnx("unable to change directory to %s! deinstall failed", LogDir); 176327Sjkh return 1; 177327Sjkh } 17841866Sjkh 17973134Ssobomax if (Interactive == TRUE) { 18073134Ssobomax int first, ch; 18173134Ssobomax 18273134Ssobomax (void)fprintf(stderr, "delete %s? ", pkg); 18373134Ssobomax (void)fflush(stderr); 18473134Ssobomax first = ch = getchar(); 18573134Ssobomax while (ch != '\n' && ch != EOF) 18673134Ssobomax ch = getchar(); 18773134Ssobomax if (first != 'y' && first != 'Y') 18873134Ssobomax return 0; 18973134Ssobomax /* Not reached */ 19073134Ssobomax } 19173134Ssobomax 19283663Ssobomax if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0) 19383663Ssobomax return 1; 19483663Ssobomax if (!STAILQ_EMPTY(rb_list)) { 19581046Ssobomax warnx("package '%s' is required by these other packages\n" 19683663Ssobomax "and may not be deinstalled%s:", 19783663Ssobomax pkg, Force ? " (but I'll delete it anyway)" : ""); 19883663Ssobomax STAILQ_FOREACH(rb_entry, rb_list, link) 19983663Ssobomax fprintf(stderr, "%s\n", rb_entry->pkgname); 2004996Sjkh if (!Force) 2014996Sjkh return 1; 2024996Sjkh } 20341866Sjkh 204327Sjkh sanity_check(LogDir); 205327Sjkh cfile = fopen(CONTENTS_FNAME, "r"); 20641866Sjkh 207327Sjkh if (!cfile) { 20830221Scharnier warnx("unable to open '%s' file", CONTENTS_FNAME); 209327Sjkh return 1; 210327Sjkh } 21141866Sjkh 212327Sjkh /* If we have a prefix, add it now */ 213327Sjkh if (Prefix) 214327Sjkh add_plist(&Plist, PLIST_CWD, Prefix); 215327Sjkh read_plist(&Plist, cfile); 216327Sjkh fclose(cfile); 21723442Sjkh p = find_plist(&Plist, PLIST_CWD); 21841866Sjkh 21923442Sjkh if (!p) { 22030221Scharnier warnx("package '%s' doesn't have a prefix", pkg); 22123442Sjkh return 1; 22223442Sjkh } 22341866Sjkh 22423442Sjkh setenv(PKG_PREFIX_VNAME, p->name, 1); 22541866Sjkh 226206043Sflz if ((fd = open(REQUIRE_FNAME, O_RDWR)) != -1) { 227206043Sflz fstat(fd, &sb); 228206043Sflz fchmod(fd, sb.st_mode | S_IXALL); /* be sure, chmod a+x */ 229206043Sflz close(fd); 2301550Sasami if (Verbose) 2311550Sasami printf("Executing 'require' script.\n"); 2321550Sasami if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) { 23330221Scharnier warnx("package %s fails requirements %s", pkg, 23430221Scharnier Force ? "" : "- not deleted"); 2354996Sjkh if (!Force) 2364996Sjkh return 1; 2371550Sasami } 2381550Sasami } 23941866Sjkh 24076739Ssobomax /* 24176739Ssobomax * Test whether to use the old method of passing tokens to deinstallation 24241866Sjkh * scripts, and set appropriate variables.. 24341866Sjkh */ 24441866Sjkh 24541866Sjkh if (fexists(POST_DEINSTALL_FNAME)) { 24641866Sjkh new_m = 1; 247111486Sdes post_script = POST_DEINSTALL_FNAME; 248111486Sdes pre_arg = post_arg = ""; 249111486Sdes } else if (fexists(DEINSTALL_FNAME)) { 250111486Sdes post_script = DEINSTALL_FNAME; 251111486Sdes pre_arg = "DEINSTALL"; 252111486Sdes post_arg = "POST-DEINSTALL"; 25341866Sjkh } else { 254111486Sdes post_script = pre_arg = post_arg = NULL; 25541866Sjkh } 25641866Sjkh 257206043Sflz if (!NoDeInstall && pre_script != NULL && (fd = open(pre_script, O_RDWR)) != -1) { 258327Sjkh if (Fake) 259327Sjkh printf("Would execute de-install script at this point.\n"); 260327Sjkh else { 261206043Sflz fstat(fd, &sb); 262206043Sflz fchmod(fd, sb.st_mode | S_IXALL); /* be sure, chmod a+x */ 263206043Sflz close(fd); 26441866Sjkh if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) { 26530221Scharnier warnx("deinstall script returned error status"); 2664996Sjkh if (!Force) 2674996Sjkh return 1; 268327Sjkh } 269327Sjkh } 270327Sjkh } 27141866Sjkh 272131280Seik for (p = Plist.head; p ; p = p->next) { 273131280Seik if (p->type != PLIST_PKGDEP) 274131280Seik continue; 275173533Skrion deporigin = (p->next != NULL && p->next->type == PLIST_DEPORIGIN) ? p->next->name : 276131280Seik NULL; 277131280Seik if (Verbose) { 278131280Seik printf("Trying to remove dependency on package '%s'", p->name); 279131280Seik if (deporigin != NULL) 280131280Seik printf(" with '%s' origin", deporigin); 281131280Seik printf(".\n"); 282131280Seik } 283131280Seik if (!Fake) { 284178103Spav if (deporigin) { 285178103Spav deporigins = realloc(deporigins, (dep_count + 2) * sizeof(*deporigins)); 286178103Spav depnames = realloc(depnames, (dep_count + 1) * sizeof(*depnames)); 287178103Spav deporigins[dep_count] = deporigin; 288178103Spav deporigins[dep_count + 1] = NULL; 289178103Spav depnames[dep_count] = p->name; 290178103Spav dep_count++; 291178103Spav } else { 292178103Spav undepend(p->name, pkg); 293131280Seik } 294131280Seik } 295131280Seik } 296131280Seik 297178103Spav if (dep_count > 0) { 298178103Spav /* Undepend all the dependencies at once */ 299178103Spav depmatches = matchallbyorigin((const char **)deporigins, NULL); 300178103Spav free(deporigins); 301178103Spav if (depmatches) { 302178103Spav for (i = 0; i < dep_count; i++) { 303178103Spav if (depmatches[i]) { 304178753Spav char **tmp = depmatches[i]; 305178753Spav int j; 306178753Spav for (j = 0; tmp[j] != NULL; j++) 307178753Spav undepend(tmp[j], pkg); 308178103Spav } else if (depnames[i]) { 309178103Spav undepend(depnames[i], pkg); 310178103Spav } 311178103Spav } 312178103Spav } 313178103Spav } 314178103Spav 31539068Sjkh if (chdir(home) == FAIL) { 31639068Sjkh cleanup(0); 31796392Salfred errx(2, "%s: unable to return to working directory %s!", __func__, 31896388Salfred home); 31939068Sjkh } 32041866Sjkh 32176739Ssobomax /* 32276739Ssobomax * Some packages aren't packed right, so we need to just ignore 32376739Ssobomax * delete_package()'s status. Ugh! :-( 32476739Ssobomax */ 32573525Sroberto if (delete_package(FALSE, CleanDirs, &Plist) == FAIL) 32673525Sroberto warnx( 327225610Spluknet "couldn't entirely delete package `%s'\n" 328225610Spluknet "(perhaps the packing list is incorrectly specified?)", pkg); 32941866Sjkh 33041866Sjkh if (chdir(LogDir) == FAIL) { 33141866Sjkh warnx("unable to change directory to %s! deinstall failed", LogDir); 33241866Sjkh return 1; 33341866Sjkh } 33441866Sjkh 335206043Sflz if (!NoDeInstall && post_script != NULL && (fd = open(post_script, O_RDWR)) != -1) { 33641866Sjkh if (Fake) 33741866Sjkh printf("Would execute post-deinstall script at this point.\n"); 33841866Sjkh else { 339206043Sflz fstat(fd, &sb); 340206043Sflz fchmod(fd, sb.st_mode | S_IXALL); /* be sure, chmod a+x */ 341206043Sflz close(fd); 34241866Sjkh if (vsystem("./%s %s %s", post_script, pkg, post_arg)) { 34341866Sjkh warnx("post-deinstall script returned error status"); 34441866Sjkh if (!Force) 34541866Sjkh return 1; 34641866Sjkh } 34741866Sjkh } 34841866Sjkh } 34941866Sjkh 35041866Sjkh if (chdir(home) == FAIL) { 35141866Sjkh cleanup(0); 35296392Salfred errx(2, "%s: unable to return to working directory %s!", __func__, 35396388Salfred home); 35441866Sjkh } 35541866Sjkh 35641866Sjkh if (!Fake) { 35761171Shoek if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) { 35830221Scharnier warnx("couldn't remove log entry in %s, deinstall failed", LogDir); 35917338Sjkh if (!Force) 36017338Sjkh return 1; 36112219Sjkh } 362327Sjkh } 363327Sjkh return 0; 364327Sjkh} 365327Sjkh 366327Sjkhstatic void 367327Sjkhsanity_check(char *pkg) 368327Sjkh{ 36939068Sjkh if (!fexists(CONTENTS_FNAME)) { 37039068Sjkh cleanup(0); 37196392Salfred errx(2, "%s: installed package %s has no %s file!", __func__, 37296388Salfred pkg, CONTENTS_FNAME); 37339068Sjkh } 374327Sjkh} 375327Sjkh 376327Sjkhvoid 377327Sjkhcleanup(int sig) 378327Sjkh{ 37939068Sjkh if (sig) 38039068Sjkh exit(1); 381327Sjkh} 3824996Sjkh 3834996Sjkhstatic void 38496613Ssobomaxundepend(char *p, char *pkgname) 3854996Sjkh{ 38683663Ssobomax char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; 38783663Ssobomax FILE *fpwr; 38883663Ssobomax int s; 38983663Ssobomax struct reqr_by_entry *rb_entry; 39083663Ssobomax struct reqr_by_head *rb_list; 3914996Sjkh 39283663Ssobomax 39396613Ssobomax if (requiredby(p, &rb_list, Verbose, FALSE) <= 0) 39483663Ssobomax return; 39596613Ssobomax snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p, REQUIRED_BY_FNAME); 39683663Ssobomax snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname); 39783663Ssobomax s = mkstemp(ftmp); 39883663Ssobomax if (s == -1) { 39983663Ssobomax warnx("couldn't open temp file '%s'", ftmp); 40083663Ssobomax return; 40183663Ssobomax } 40283663Ssobomax fpwr = fdopen(s, "w"); 40383663Ssobomax if (fpwr == NULL) { 40483663Ssobomax close(s); 40583663Ssobomax warnx("couldn't fdopen temp file '%s'", ftmp); 40683663Ssobomax goto cleanexit; 40783663Ssobomax } 40883663Ssobomax STAILQ_FOREACH(rb_entry, rb_list, link) 40983663Ssobomax if (strcmp(rb_entry->pkgname, pkgname)) /* no match */ 41083663Ssobomax fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr); 41183663Ssobomax if (fchmod(s, 0644) == FAIL) { 41283663Ssobomax warnx("error changing permission of temp file '%s'", ftmp); 41383663Ssobomax fclose(fpwr); 41483663Ssobomax goto cleanexit; 41583663Ssobomax } 41683663Ssobomax if (fclose(fpwr) == EOF) { 41783663Ssobomax warnx("error closing temp file '%s'", ftmp); 41883663Ssobomax goto cleanexit; 41983663Ssobomax } 42083663Ssobomax if (rename(ftmp, fname) == -1) 42183663Ssobomax warnx("error renaming '%s' to '%s'", ftmp, fname); 42283663Ssobomaxcleanexit: 42383663Ssobomax remove(ftmp); 42483663Ssobomax return; 4254996Sjkh} 426