perform.c revision 112572
1/* 2 * FreeBSD install - a package for the installation and maintainance 3 * of non-core utilities. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * Jordan K. Hubbard 15 * 23 Aug 1993 16 * 17 * This is the main body of the info module. 18 * 19 */ 20 21#include <sys/cdefs.h> 22__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/info/perform.c 112572 2003-03-25 00:51:41Z mdodd $"); 23 24#include "lib.h" 25#include "info.h" 26#include <err.h> 27#include <signal.h> 28 29static int pkg_do(char *); 30static int find_pkg(struct which_head *); 31static int cmp_path(const char *, const char *, const char *); 32static char *abspath(const char *); 33static int find_pkgs_by_origin(const char *); 34 35int 36pkg_perform(char **pkgs) 37{ 38 char **matched; 39 int err_cnt = 0; 40 int i, errcode; 41 42 signal(SIGINT, cleanup); 43 44 /* Overriding action? */ 45 if (CheckPkg) { 46 return isinstalledpkg(CheckPkg) == TRUE ? 0 : 1; 47 /* Not reached */ 48 } else if (!TAILQ_EMPTY(whead)) { 49 return find_pkg(whead); 50 } else if (LookUpOrigin != NULL) { 51 return find_pkgs_by_origin(LookUpOrigin); 52 } 53 54 if (MatchType != MATCH_EXACT) { 55 matched = matchinstalled(MatchType, pkgs, &errcode); 56 if (errcode != 0) 57 return 1; 58 /* Not reached */ 59 60 if (matched != NULL) 61 pkgs = matched; 62 else switch (MatchType) { 63 case MATCH_GLOB: 64 break; 65 case MATCH_ALL: 66 warnx("no packages installed"); 67 return 0; 68 /* Not reached */ 69 case MATCH_REGEX: 70 warnx("no packages match pattern(s)"); 71 return 1; 72 /* Not reached */ 73 default: 74 break; 75 } 76 } 77 78 for (i = 0; pkgs[i]; i++) 79 err_cnt += pkg_do(pkgs[i]); 80 81 return err_cnt; 82} 83 84static char *Home; 85 86static int 87pkg_do(char *pkg) 88{ 89 Boolean installed = FALSE, isTMP = FALSE; 90 char log_dir[FILENAME_MAX]; 91 char fname[FILENAME_MAX], extrlist[FILENAME_MAX]; 92 Package plist; 93 FILE *fp; 94 struct stat sb; 95 char *cp = NULL; 96 int code = 0; 97 98 if (isURL(pkg)) { 99 if ((cp = fileGetURL(NULL, pkg)) != NULL) { 100 strcpy(fname, cp); 101 isTMP = TRUE; 102 } 103 } 104 else if (fexists(pkg) && isfile(pkg)) { 105 int len; 106 107 if (*pkg != '/') { 108 if (!getcwd(fname, FILENAME_MAX)) 109 upchuck("getcwd"); 110 len = strlen(fname); 111 snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg); 112 } 113 else 114 strcpy(fname, pkg); 115 cp = fname; 116 } 117 else { 118 if ((cp = fileFindByPath(NULL, pkg)) != NULL) 119 strncpy(fname, cp, FILENAME_MAX); 120 } 121 if (cp) { 122 /* 123 * Apply a crude heuristic to see how much space the package will 124 * take up once it's unpacked. I've noticed that most packages 125 * compress an average of 75%, but we're only unpacking the + files so 126 * be very optimistic. 127 */ 128 if (stat(fname, &sb) == FAIL) { 129 warnx("can't stat package file '%s'", fname); 130 code = 1; 131 goto bail; 132 } 133 Home = make_playpen(PlayPen, sb.st_size / 2); 134 snprintf(extrlist, sizeof(extrlist), "--fast-read %s %s %s", 135 CONTENTS_FNAME, COMMENT_FNAME, DESC_FNAME); 136 if (Flags & SHOW_DISPLAY) 137 snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist, 138 DISPLAY_FNAME); 139 if (Flags & SHOW_INSTALL) 140 snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist, 141 INSTALL_FNAME, POST_INSTALL_FNAME); 142 if (Flags & SHOW_DEINSTALL) 143 snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist, 144 DEINSTALL_FNAME, POST_DEINSTALL_FNAME); 145 if (Flags & SHOW_MTREE) 146 snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist, 147 MTREE_FNAME); 148 if (unpack(fname, extrlist)) { 149 warnx("error during unpacking, no info for '%s' available", pkg); 150 code = 1; 151 goto bail; 152 } 153 } 154 /* It's not an ininstalled package, try and find it among the installed */ 155 else { 156 if (!isinstalledpkg(pkg)) { 157 warnx("can't find package '%s' installed or in a file!", pkg); 158 return 1; 159 } 160 sprintf(log_dir, "%s/%s", LOG_DIR, pkg); 161 if (chdir(log_dir) == FAIL) { 162 warnx("can't change directory to '%s'!", log_dir); 163 return 1; 164 } 165 installed = TRUE; 166 } 167 168 /* Suck in the contents list */ 169 plist.head = plist.tail = NULL; 170 fp = fopen(CONTENTS_FNAME, "r"); 171 if (!fp) { 172 warnx("unable to open %s file", CONTENTS_FNAME); 173 code = 1; 174 goto bail; 175 } 176 /* If we have a prefix, add it now */ 177 read_plist(&plist, fp); 178 fclose(fp); 179 180 /* 181 * Index is special info type that has to override all others to make 182 * any sense. 183 */ 184 if (Flags & SHOW_INDEX) { 185 char tmp[FILENAME_MAX]; 186 187 snprintf(tmp, FILENAME_MAX, "%-19s ", pkg); 188 show_index(tmp, COMMENT_FNAME); 189 } 190 else { 191 /* Start showing the package contents */ 192 if (!Quiet) 193 printf("%sInformation for %s:\n\n", InfoPrefix, pkg); 194 else if (QUIET) 195 printf("%s%s:", InfoPrefix, pkg); 196 if (Flags & SHOW_COMMENT) 197 show_file("Comment:\n", COMMENT_FNAME); 198 if (Flags & SHOW_REQUIRE) 199 show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE); 200 if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME)) 201 show_file("Required by:\n", REQUIRED_BY_FNAME); 202 if (Flags & SHOW_DESC) 203 show_file("Description:\n", DESC_FNAME); 204 if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME)) 205 show_file("Install notice:\n", DISPLAY_FNAME); 206 if (Flags & SHOW_PLIST) 207 show_plist("Packing list:\n", &plist, (plist_t)0, TRUE); 208 if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME)) 209 show_file("Install script:\n", INSTALL_FNAME); 210 if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME)) 211 show_file("Post-Install script:\n", POST_INSTALL_FNAME); 212 if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME)) 213 show_file("De-Install script:\n", DEINSTALL_FNAME); 214 if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME)) 215 show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME); 216 if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME)) 217 show_file("mtree file:\n", MTREE_FNAME); 218 if (Flags & SHOW_PREFIX) 219 show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE); 220 if (Flags & SHOW_FILES) 221 show_files("Files:\n", &plist); 222 if ((Flags & SHOW_SIZE) && installed) 223 show_size("Package Size:\n", &plist); 224 if ((Flags & SHOW_CKSUM) && installed) 225 show_cksum("Mismatched Checksums:\n", &plist); 226 if (Flags & SHOW_ORIGIN) 227 show_origin("Origin:\n", &plist); 228 if (Flags & SHOW_FMTREV) 229 show_fmtrev("Packing list format revision:\n", &plist); 230 if (!Quiet) 231 puts(InfoPrefix); 232 } 233 free_plist(&plist); 234 bail: 235 leave_playpen(); 236 if (isTMP) 237 unlink(fname); 238 return code; 239} 240 241void 242cleanup(int sig) 243{ 244 static int in_cleanup = 0; 245 246 if (!in_cleanup) { 247 in_cleanup = 1; 248 leave_playpen(); 249 } 250 if (sig) 251 exit(1); 252} 253 254/* 255 * Return an absolute path, additionally removing all .'s, ..'s, and extraneous 256 * /'s, as realpath() would, but without resolving symlinks, because that can 257 * potentially screw up our comparisons later. 258 */ 259static char * 260abspath(const char *pathname) 261{ 262 char *tmp, *tmp1, *resolved_path; 263 char *cwd = NULL; 264 int len; 265 266 if (pathname[0] != '/') { 267 cwd = getcwd(NULL, MAXPATHLEN); 268 asprintf(&resolved_path, "%s/%s/", cwd, pathname); 269 } else 270 asprintf(&resolved_path, "%s/", pathname); 271 272 if (resolved_path == NULL) 273 errx(2, NULL); 274 275 if (cwd != NULL) 276 free(cwd); 277 278 while ((tmp = strstr(resolved_path, "//")) != NULL) 279 strcpy(tmp, tmp + 1); 280 281 while ((tmp = strstr(resolved_path, "/./")) != NULL) 282 strcpy(tmp, tmp + 2); 283 284 while ((tmp = strstr(resolved_path, "/../")) != NULL) { 285 *tmp = '\0'; 286 if ((tmp1 = strrchr(resolved_path, '/')) == NULL) 287 tmp1 = resolved_path; 288 strcpy(tmp1, tmp + 3); 289 } 290 291 len = strlen(resolved_path); 292 if (len > 1 && resolved_path[len - 1] == '/') 293 resolved_path[len - 1] = '\0'; 294 295 return resolved_path; 296} 297 298/* 299 * Comparison to see if the path we're on matches the 300 * one we are looking for. 301 */ 302static int 303cmp_path(const char *target, const char *current, const char *cwd) 304{ 305 char *resolved, *temp; 306 int rval; 307 308 asprintf(&temp, "%s/%s", cwd, current); 309 if (temp == NULL) 310 errx(2, NULL); 311 312 /* 313 * Make sure there's no multiple /'s or other weird things in the PLIST, 314 * since some plists seem to have them and it could screw up our strncmp. 315 */ 316 resolved = abspath(temp); 317 318 if (strcmp(target, resolved) == 0) 319 rval = 1; 320 else 321 rval = 0; 322 323 free(temp); 324 free(resolved); 325 return rval; 326} 327 328/* 329 * Look through package dbs in LOG_DIR and find which 330 * packages installed the files in which_list. 331 */ 332static int 333find_pkg(struct which_head *which_list) 334{ 335 char **installed; 336 int errcode, i; 337 struct which_entry *wp; 338 339 TAILQ_FOREACH(wp, which_list, next) { 340 const char *msg = "file cannot be found"; 341 char *tmp; 342 343 wp->skip = TRUE; 344 /* If it's not a file, we'll see if it's an executable. */ 345 if (isfile(wp->file) == FALSE) { 346 if (strchr(wp->file, '/') == NULL) { 347 tmp = vpipe("/usr/bin/which %s", wp->file); 348 if (tmp != NULL) { 349 strlcpy(wp->file, tmp, PATH_MAX); 350 wp->skip = FALSE; 351 free(tmp); 352 } else 353 msg = "file is not in PATH"; 354 } 355 } else { 356 tmp = abspath(wp->file); 357 if (isfile(tmp)) { 358 strlcpy(wp->file, tmp, PATH_MAX); 359 wp->skip = FALSE; 360 } 361 free(tmp); 362 } 363 if (wp->skip == TRUE) 364 warnx("%s: %s", wp->file, msg); 365 } 366 367 installed = matchinstalled(MATCH_ALL, NULL, &errcode); 368 if (installed == NULL) 369 return errcode; 370 371 for (i = 0; installed[i] != NULL; i++) { 372 FILE *fp; 373 Package pkg; 374 PackingList itr; 375 char *cwd = NULL; 376 char tmp[PATH_MAX]; 377 378 snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i], 379 CONTENTS_FNAME); 380 fp = fopen(tmp, "r"); 381 if (fp == NULL) { 382 warn("%s", tmp); 383 return 1; 384 } 385 386 pkg.head = pkg.tail = NULL; 387 read_plist(&pkg, fp); 388 fclose(fp); 389 for (itr = pkg.head; itr != pkg.tail; itr = itr->next) { 390 if (itr->type == PLIST_CWD) { 391 cwd = itr->name; 392 } else if (itr->type == PLIST_FILE) { 393 TAILQ_FOREACH(wp, which_list, next) { 394 if (wp->skip == TRUE) 395 continue; 396 if (!cmp_path(wp->file, itr->name, cwd)) 397 continue; 398 if (wp->package[0] != '\0') { 399 warnx("both %s and %s claim to have installed %s\n", 400 wp->package, installed[i], wp->file); 401 } else { 402 strlcpy(wp->package, installed[i], PATH_MAX); 403 } 404 } 405 } 406 } 407 free_plist(&pkg); 408 } 409 410 TAILQ_FOREACH(wp, which_list, next) { 411 if (wp->package[0] != '\0') { 412 if (Quiet) 413 puts(wp->package); 414 else 415 printf("%s was installed by package %s\n", \ 416 wp->file, wp->package); 417 } 418 } 419 while (!TAILQ_EMPTY(which_list)) { 420 wp = TAILQ_FIRST(which_list); 421 TAILQ_REMOVE(which_list, wp, next); 422 free(wp); 423 } 424 425 free(which_list); 426 return 0; 427} 428 429/* 430 * Look through package dbs in LOG_DIR and find which 431 * packages have the given origin. Don't use read_plist() 432 * because this increases time necessary for lookup by 40 433 * times, as we don't really have to parse all plist to 434 * get origin. 435 */ 436static int 437find_pkgs_by_origin(const char *origin) 438{ 439 char **matched; 440 int errcode, i; 441 442 if (!Quiet) 443 printf("The following installed package(s) has %s origin:\n", origin); 444 445 matched = matchbyorigin(origin, &errcode); 446 if (matched == NULL) 447 return errcode; 448 449 for (i = 0; matched[i] != NULL; i++) 450 puts(matched[i]); 451 452 return 0; 453} 454