perform.c revision 72694
1#ifndef lint 2static const char rcsid[] = 3 "$FreeBSD: head/usr.sbin/pkg_install/delete/perform.c 72694 2001-02-19 13:26:13Z sobomax $"; 4#endif 5 6/* 7 * FreeBSD install - a package for the installation and maintainance 8 * of non-core utilities. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * Jordan K. Hubbard 20 * 18 July 1993 21 * 22 * This is the main body of the delete module. 23 * 24 */ 25 26#include <err.h> 27#include "lib.h" 28#include "delete.h" 29 30static int pkg_do(char *); 31static void sanity_check(char *); 32static void undepend(PackingList, char *); 33static int chkifdepends(char *pkgname1, char *pkgname2); 34static char LogDir[FILENAME_MAX]; 35 36 37int 38pkg_perform(char **pkgs) 39{ 40 char *tmp; 41 int i, j; 42 int err_cnt = 0; 43 int loop_cnt; 44 45 for (i = 0; pkgs[i]; i++) { 46 /* 47 * Check to see if any other package in pkgs[i+1:] depends 48 * on pkgs[i] and deffer removal of pkgs[i] if so. 49 */ 50 loop_cnt = 0; 51 for (j = i + 1; pkgs[j]; j++) { 52 if (chkifdepends(pkgs[j], pkgs[i]) == 1) { 53 /* 54 * Try to avoid deadlock if package A depends on B which in 55 * turn depends on C and C due to an error depends on A. 56 * Use ugly but simple method, becase it Should Never 57 * Happen[tm] in the real life anyway. 58 */ 59 if (loop_cnt > 4096) { 60 warnx("dependency loop detected for package %s", pkgs[j]); 61 err_cnt++; 62 break; 63 } 64 loop_cnt++; 65 tmp = pkgs[i]; 66 pkgs[i] = pkgs[j]; 67 pkgs[j] = tmp; 68 /* 69 * Another iteration requred to check if new pkgs[i] 70 * itself has any packages that depend on it 71 */ 72 j--; 73 } 74 } 75 err_cnt += pkg_do(pkgs[i]); 76 } 77 78 return err_cnt; 79} 80 81static Package Plist; 82 83/* This is seriously ugly code following. Written very fast! */ 84static int 85pkg_do(char *pkg) 86{ 87 FILE *cfile; 88 char home[FILENAME_MAX]; 89 PackingList p; 90 char *tmp; 91 int len; 92 /* support for separate pre/post install scripts */ 93 int new_m = 0; 94 char pre_script[FILENAME_MAX] = DEINSTALL_FNAME; 95 char post_script[FILENAME_MAX]; 96 char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX]; 97 98 if (!pkg || !(len = strlen(pkg))) 99 return 1; 100 if (pkg[len - 1] == '/') 101 pkg[len - 1] = '\0'; 102 103 /* Reset some state */ 104 if (Plist.head) 105 free_plist(&Plist); 106 107 sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, 108 pkg); 109 110 if (!fexists(LogDir)) { 111 warnx("no such package '%s' installed", pkg); 112 return 1; 113 } 114 115 if (!getcwd(home, FILENAME_MAX)) { 116 cleanup(0); 117 errx(2, __FUNCTION__ ": unable to get current working directory!"); 118 } 119 120 if (chdir(LogDir) == FAIL) { 121 warnx("unable to change directory to %s! deinstall failed", LogDir); 122 return 1; 123 } 124 125 if (!isemptyfile(REQUIRED_BY_FNAME)) { 126 char buf[512]; 127 warnx("package `%s' is required by these other packages\n" 128 "and may not be deinstalled%s:", 129 pkg, Force ? " (but I'll delete it anyway)" : "" ); 130 cfile = fopen(REQUIRED_BY_FNAME, "r"); 131 if (cfile) { 132 while (fgets(buf, sizeof(buf), cfile)) 133 fprintf(stderr, "%s", buf); 134 fclose(cfile); 135 } else 136 warnx("cannot open requirements file `%s'", REQUIRED_BY_FNAME); 137 if (!Force) 138 return 1; 139 } 140 141 sanity_check(LogDir); 142 cfile = fopen(CONTENTS_FNAME, "r"); 143 144 if (!cfile) { 145 warnx("unable to open '%s' file", CONTENTS_FNAME); 146 return 1; 147 } 148 149 /* If we have a prefix, add it now */ 150 if (Prefix) 151 add_plist(&Plist, PLIST_CWD, Prefix); 152 read_plist(&Plist, cfile); 153 fclose(cfile); 154 p = find_plist(&Plist, PLIST_CWD); 155 156 if (!p) { 157 warnx("package '%s' doesn't have a prefix", pkg); 158 return 1; 159 } 160 161 setenv(PKG_PREFIX_VNAME, p->name, 1); 162 163 if (fexists(REQUIRE_FNAME)) { 164 if (Verbose) 165 printf("Executing 'require' script.\n"); 166 vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ 167 if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) { 168 warnx("package %s fails requirements %s", pkg, 169 Force ? "" : "- not deleted"); 170 if (!Force) 171 return 1; 172 } 173 } 174 175 /* Test whether to use the old method of passing tokens to deinstallation 176 * scripts, and set appropriate variables.. 177 */ 178 179 if (fexists(POST_DEINSTALL_FNAME)) { 180 new_m = 1; 181 sprintf(post_script, "%s", POST_DEINSTALL_FNAME); 182 pre_arg[0] = '\0'; 183 post_arg[0] = '\0'; 184 } else { 185 if (fexists(DEINSTALL_FNAME)) { 186 sprintf(post_script, "%s", DEINSTALL_FNAME); 187 sprintf(pre_arg, "DEINSTALL"); 188 sprintf(post_arg, "POST-DEINSTALL"); 189 } 190 } 191 192 if (!NoDeInstall && fexists(pre_script)) { 193 if (Fake) 194 printf("Would execute de-install script at this point.\n"); 195 else { 196 vsystem("chmod +x %s", pre_script); /* make sure */ 197 if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) { 198 warnx("deinstall script returned error status"); 199 if (!Force) 200 return 1; 201 } 202 } 203 } 204 205 if (chdir(home) == FAIL) { 206 cleanup(0); 207 errx(2, __FUNCTION__ ": unable to return to working directory %s!", home); 208 } 209 210 if (!Fake) { 211 /* Some packages aren't packed right, so we need to just ignore delete_package()'s status. Ugh! :-( */ 212 if (delete_package(FALSE, CleanDirs, &Plist) == FAIL) 213 warnx( 214 "couldn't entirely delete package (perhaps the packing list is\n" 215 "incorrectly specified?)"); 216 } 217 218 if (chdir(LogDir) == FAIL) { 219 warnx("unable to change directory to %s! deinstall failed", LogDir); 220 return 1; 221 } 222 223 if (!NoDeInstall && fexists(post_script)) { 224 if (Fake) 225 printf("Would execute post-deinstall script at this point.\n"); 226 else { 227 vsystem("chmod +x %s", post_script); /* make sure */ 228 if (vsystem("./%s %s %s", post_script, pkg, post_arg)) { 229 warnx("post-deinstall script returned error status"); 230 if (!Force) 231 return 1; 232 } 233 } 234 } 235 236 if (chdir(home) == FAIL) { 237 cleanup(0); 238 errx(2, __FUNCTION__ ": unable to return to working directory %s!", home); 239 } 240 241 if (!Fake) { 242 if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) { 243 warnx("couldn't remove log entry in %s, deinstall failed", LogDir); 244 if (!Force) 245 return 1; 246 } 247 } 248 249 for (p = Plist.head; p ; p = p->next) { 250 if (p->type != PLIST_PKGDEP) 251 continue; 252 if (Verbose) 253 printf("Attempting to remove dependency on package `%s'\n", p->name); 254 if (!Fake) 255 undepend(p, pkg); 256 } 257 return 0; 258} 259 260static void 261sanity_check(char *pkg) 262{ 263 if (!fexists(CONTENTS_FNAME)) { 264 cleanup(0); 265 errx(2, __FUNCTION__ ": installed package %s has no %s file!", pkg, CONTENTS_FNAME); 266 } 267} 268 269void 270cleanup(int sig) 271{ 272 if (sig) 273 exit(1); 274} 275 276static void 277undepend(PackingList p, char *pkgname) 278{ 279 char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; 280 char fbuf[FILENAME_MAX]; 281 FILE *fp, *fpwr; 282 char *tmp; 283 int s; 284 285 sprintf(fname, "%s/%s/%s", 286 (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, 287 p->name, REQUIRED_BY_FNAME); 288 fp = fopen(fname, "r"); 289 if (fp == NULL) { 290 warnx("couldn't open dependency file `%s'", fname); 291 return; 292 } 293 sprintf(ftmp, "%s.XXXXXX", fname); 294 s = mkstemp(ftmp); 295 if (s == -1) { 296 fclose(fp); 297 warnx("couldn't open temp file `%s'", ftmp); 298 return; 299 } 300 fpwr = fdopen(s, "w"); 301 if (fpwr == NULL) { 302 close(s); 303 fclose(fp); 304 warnx("couldn't fdopen temp file `%s'", ftmp); 305 remove(ftmp); 306 return; 307 } 308 while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { 309 if (fbuf[strlen(fbuf)-1] == '\n') 310 fbuf[strlen(fbuf)-1] = '\0'; 311 if (strcmp(fbuf, pkgname)) /* no match */ 312 fputs(fbuf, fpwr), putc('\n', fpwr); 313 } 314 (void) fclose(fp); 315 if (fchmod(s, 0644) == FAIL) { 316 warnx("error changing permission of temp file `%s'", ftmp); 317 fclose(fpwr); 318 remove(ftmp); 319 return; 320 } 321 if (fclose(fpwr) == EOF) { 322 warnx("error closing temp file `%s'", ftmp); 323 remove(ftmp); 324 return; 325 } 326 if (rename(ftmp, fname) == -1) 327 warnx("error renaming `%s' to `%s'", ftmp, fname); 328 remove(ftmp); /* just in case */ 329 return; 330} 331 332/* 333 * Check to see if pkgname1 depends on pkgname2. 334 * Returns 1 if depends, 0 if not, and -1 if error occured. 335 */ 336static int 337chkifdepends(char *pkgname1, char *pkgname2) 338{ 339 FILE *fp; 340 char fname[FILENAME_MAX]; 341 char fbuf[FILENAME_MAX]; 342 char *tmp; 343 int retval; 344 345 sprintf(fname, "%s/%s/%s", 346 (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, 347 pkgname2, REQUIRED_BY_FNAME); 348 fp = fopen(fname, "r"); 349 if (fp == NULL) { 350 /* Probably pkgname2 doesn't have any packages that depend on it */ 351 return 0; 352 } 353 354 retval = 0; 355 while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { 356 if (fbuf[strlen(fbuf)-1] == '\n') 357 fbuf[strlen(fbuf)-1] = '\0'; 358 if (strcmp(fbuf, pkgname1) == 0) { /* match */ 359 retval = 1; 360 break; 361 } 362 } 363 364 fclose(fp); 365 return retval; 366} 367