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 * Miscellaneous file access utilities. 23 * 24 */ 25 26#include "lib.h" 27#include <err.h> 28#include <fetch.h> 29#include <pwd.h> 30#include <time.h> 31#include <sys/wait.h> 32 33/* Quick check to see if a file exists */ 34Boolean 35fexists(char *fname) 36{ 37 struct stat dummy; 38 if (!lstat(fname, &dummy)) 39 return TRUE; 40 return FALSE; 41} 42 43/* Quick check to see if something is a directory or symlink to a directory */ 44Boolean 45isdir(char *fname) 46{ 47 struct stat sb; 48 49 if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode)) 50 return TRUE; 51 else if (lstat(strconcat(fname, "/"), &sb) != FAIL && S_ISDIR(sb.st_mode)) 52 return TRUE; 53 else 54 return FALSE; 55} 56 57/* Check to see if file is a dir or symlink to a dir, and is empty */ 58Boolean 59isemptydir(char *fname) 60{ 61 if (isdir(fname)) { 62 DIR *dirp; 63 struct dirent *dp; 64 65 dirp = opendir(fname); 66 if (!dirp) 67 return FALSE; /* no perms, leave it alone */ 68 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 69 if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) { 70 closedir(dirp); 71 return FALSE; 72 } 73 } 74 (void)closedir(dirp); 75 return TRUE; 76 } 77 return FALSE; 78} 79 80/* 81 * Returns TRUE if file is a regular file or symlink pointing to a regular 82 * file 83 */ 84Boolean 85isfile(char *fname) 86{ 87 struct stat sb; 88 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) 89 return TRUE; 90 return FALSE; 91} 92 93/* 94 * Check to see if file is a file or symlink pointing to a file and is empty. 95 * If nonexistent or not a file, say "it's empty", otherwise return TRUE if 96 * zero sized. 97 */ 98Boolean 99isemptyfile(char *fname) 100{ 101 struct stat sb; 102 if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) { 103 if (sb.st_size != 0) 104 return FALSE; 105 } 106 return TRUE; 107} 108 109/* Returns TRUE if file is a symbolic link. */ 110Boolean 111issymlink(char *fname) 112{ 113 struct stat sb; 114 if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode)) 115 return TRUE; 116 return FALSE; 117} 118 119/* Returns TRUE if file is a URL specification */ 120Boolean 121isURL(char *fname) 122{ 123 /* 124 * I'm sure there are other types of URL specifications that I could 125 * also be looking for here, but for now I'll just be happy to get ftp 126 * and http working. 127 */ 128 if (!fname) 129 return FALSE; 130 while (isspace(*fname)) 131 ++fname; 132 if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7)) 133 return TRUE; 134 return FALSE; 135} 136 137#define HOSTNAME_MAX 64 138/* 139 * Try and fetch a file by URL, returning the directory name for where 140 * it's unpacked, if successful. 141 */ 142char * 143fileGetURL(char *base, char *spec) 144{ 145 char *cp, *rp; 146 char fname[FILENAME_MAX]; 147 char pen[FILENAME_MAX]; 148 char buf[8192]; 149 FILE *ftp; 150 pid_t tpid; 151 int pfd[2], pstat; 152 size_t r, w; 153 char *hint; 154 int fd; 155 156 rp = NULL; 157 /* Special tip that sysinstall left for us */ 158 hint = getenv("PKG_ADD_BASE"); 159 if (!isURL(spec)) { 160 if (!base && !hint) 161 return NULL; 162 /* 163 * We've been given an existing URL (that's known-good) and now we need 164 * to construct a composite one out of that and the basename we were 165 * handed as a dependency. 166 */ 167 if (base) { 168 strcpy(fname, base); 169 /* 170 * Advance back two slashes to get to the root of the package 171 * hierarchy 172 */ 173 cp = strrchr(fname, '/'); 174 if (cp) { 175 *cp = '\0'; /* chop name */ 176 cp = strrchr(fname, '/'); 177 } 178 if (cp) { 179 *(cp + 1) = '\0'; 180 strcat(cp, "All/"); 181 strcat(cp, spec); 182 strcat(cp, ".tgz"); 183 } 184 else 185 return NULL; 186 } 187 else { 188 /* 189 * Otherwise, we've been given an environment variable hinting 190 * at the right location from sysinstall 191 */ 192 strcpy(fname, hint); 193 strcat(fname, spec); 194 strcat(fname, ".tgz"); 195 } 196 } 197 else 198 strcpy(fname, spec); 199 200 if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) { 201 printf("Error: FTP Unable to get %s: %s\n", 202 fname, fetchLastErrString); 203 return NULL; 204 } 205 206 if (isatty(0) || Verbose) 207 printf("Fetching %s...", fname), fflush(stdout); 208 pen[0] = '\0'; 209 if ((rp = make_playpen(pen, 0)) == NULL) { 210 printf("Error: Unable to construct a new playpen for FTP!\n"); 211 fclose(ftp); 212 return NULL; 213 } 214 if (pipe(pfd) == -1) { 215 warn("pipe()"); 216 cleanup(0); 217 exit(2); 218 } 219 if ((tpid = fork()) == -1) { 220 warn("pipe()"); 221 cleanup(0); 222 exit(2); 223 } 224 if (!tpid) { 225 dup2(pfd[0], 0); 226 for (fd = getdtablesize() - 1; fd >= 3; --fd) 227 close(fd);
|
230 _exit(2); 231 } 232 close(pfd[0]); 233 for (;;) { 234 if ((r = fread(buf, 1, sizeof buf, ftp)) < 1) 235 break; 236 if ((w = write(pfd[1], buf, r)) != r) 237 break; 238 } 239 fclose(ftp); 240 close(pfd[1]); 241 if (r == -1) 242 warn("warning: error reading from server"); 243 if (w == -1) 244 warn("warning: error writing to tar"); 245 tpid = waitpid(tpid, &pstat, 0); 246 if (Verbose) 247 printf("tar command returns %d status\n", WEXITSTATUS(pstat)); 248 if (rp && (isatty(0) || Verbose)) 249 printf(" Done.\n"); 250 return rp; 251} 252 253char * 254fileFindByPath(char *base, char *fname) 255{ 256 static char tmp[FILENAME_MAX]; 257 char *cp; 258 char *suffixes[] = {".tgz", ".tar", ".tbz2", NULL}; 259 int i; 260 261 if (fexists(fname) && isfile(fname)) { 262 strcpy(tmp, fname); 263 return tmp; 264 } 265 if (base) { 266 strcpy(tmp, base); 267 268 cp = strrchr(tmp, '/'); 269 if (cp) { 270 *cp = '\0'; /* chop name */ 271 cp = strrchr(tmp, '/'); 272 } 273 if (cp) 274 for (i = 0; suffixes[i] != NULL; i++) { 275 *(cp + 1) = '\0'; 276 strcat(cp, "All/"); 277 strcat(cp, fname); 278 strcat(cp, suffixes[i]); 279 if (fexists(tmp)) 280 return tmp; 281 } 282 } 283 284 cp = getenv("PKG_PATH"); 285 while (cp) { 286 char *cp2 = strsep(&cp, ":"); 287 288 for (i = 0; suffixes[i] != NULL; i++) { 289 snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]); 290 if (fexists(tmp) && isfile(tmp)) 291 return tmp; 292 } 293 } 294 return NULL; 295} 296 297char * 298fileGetContents(char *fname) 299{ 300 char *contents; 301 struct stat sb; 302 int fd; 303 304 if (stat(fname, &sb) == FAIL) { 305 cleanup(0); 306 errx(2, __FUNCTION__ ": can't stat '%s'", fname); 307 } 308 309 contents = (char *)malloc(sb.st_size + 1); 310 fd = open(fname, O_RDONLY, 0); 311 if (fd == FAIL) { 312 cleanup(0); 313 errx(2, __FUNCTION__ ": unable to open '%s' for reading", fname); 314 } 315 if (read(fd, contents, sb.st_size) != sb.st_size) { 316 cleanup(0); 317 errx(2, __FUNCTION__ ": short read on '%s' - did not get %qd bytes", 318 fname, sb.st_size); 319 } 320 close(fd); 321 contents[sb.st_size] = '\0'; 322 return contents; 323} 324 325/* 326 * Takes a filename and package name, returning (in "try") the 327 * canonical "preserve" name for it. 328 */ 329Boolean 330make_preserve_name(char *try, int max, char *name, char *file) 331{ 332 int len, i; 333 334 if ((len = strlen(file)) == 0) 335 return FALSE; 336 else 337 i = len - 1; 338 strncpy(try, file, max); 339 if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */ 340 --i; 341 for (; i; i--) { 342 if (try[i] == '/') { 343 try[i + 1]= '.'; 344 strncpy(&try[i + 2], &file[i + 1], max - i - 2); 345 break; 346 } 347 } 348 if (!i) { 349 try[0] = '.'; 350 strncpy(try + 1, file, max - 1); 351 } 352 /* I should probably be called rude names for these inline assignments */ 353 strncat(try, ".", max -= strlen(try)); 354 strncat(try, name, max -= strlen(name)); 355 strncat(try, ".", max--); 356 strncat(try, "backup", max -= 6); 357 return TRUE; 358} 359 360/* Write the contents of "str" to a file */ 361void 362write_file(char *name, char *str) 363{ 364 FILE *fp; 365 int len; 366 367 fp = fopen(name, "w"); 368 if (!fp) { 369 cleanup(0); 370 errx(2, __FUNCTION__ ": cannot fopen '%s' for writing", name); 371 } 372 len = strlen(str); 373 if (fwrite(str, 1, len, fp) != len) { 374 cleanup(0); 375 errx(2, __FUNCTION__ ": short fwrite on '%s', tried to write %d bytes", name, len); 376 } 377 if (fclose(fp)) { 378 cleanup(0); 379 errx(2, __FUNCTION__ ": failure to fclose '%s'", name); 380 } 381} 382 383void 384copy_file(char *dir, char *fname, char *to) 385{ 386 char cmd[FILENAME_MAX]; 387 388 if (fname[0] == '/') 389 snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to); 390 else 391 snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to); 392 if (vsystem(cmd)) { 393 cleanup(0); 394 errx(2, __FUNCTION__ ": could not perform '%s'", cmd); 395 } 396} 397 398void 399move_file(char *dir, char *fname, char *to) 400{ 401 char cmd[FILENAME_MAX]; 402 403 if (fname[0] == '/') 404 snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to); 405 else 406 snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to); 407 if (vsystem(cmd)) { 408 cleanup(0); 409 errx(2, __FUNCTION__ ": could not perform '%s'", cmd); 410 } 411} 412 413/* 414 * Copy a hierarchy (possibly from dir) to the current directory, or 415 * if "to" is TRUE, from the current directory to a location someplace 416 * else. 417 * 418 * Though slower, using tar to copy preserves symlinks and everything 419 * without me having to write some big hairy routine to do it. 420 */ 421void 422copy_hierarchy(char *dir, char *fname, Boolean to) 423{ 424 char cmd[FILENAME_MAX * 3]; 425 426 if (!to) { 427 /* If absolute path, use it */ 428 if (*fname == '/') 429 dir = "/"; 430 snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -", 431 dir, fname); 432 } 433 else 434 snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s", 435 fname, dir); 436#ifdef DEBUG 437 printf("Using '%s' to copy trees.\n", cmd); 438#endif 439 if (system(cmd)) { 440 cleanup(0); 441 errx(2, __FUNCTION__ ": could not perform '%s'", cmd); 442 } 443} 444 445/* Unpack a tar file */ 446int 447unpack(char *pkg, char *flist) 448{ 449 char args[10], suffix[80], *cp; 450 451 args[0] = '\0'; 452 /* 453 * Figure out by a crude heuristic whether this or not this is probably 454 * compressed and whichever compression utility was used (gzip or bzip2). 455 */ 456 if (strcmp(pkg, "-")) { 457 cp = strrchr(pkg, '.'); 458 if (cp) { 459 strcpy(suffix, cp + 1); 460 if (strchr(suffix, 'z') || strchr(suffix, 'Z')) { 461 if (strchr(suffix, 'b')) 462 strcpy(args, "-y"); 463 else 464 strcpy(args, "-z"); 465 } 466 } 467 } 468 else 469 strcpy(args, "-z"); 470 strcat(args, " -xpf"); 471 if (vsystem("tar %s '%s' %s", args, pkg, flist ? flist : "")) { 472 warnx("tar extract of %s failed!", pkg); 473 return 1; 474 } 475 return 0; 476} 477 478/* 479 * Using fmt, replace all instances of: 480 * 481 * %F With the parameter "name" 482 * %D With the parameter "dir" 483 * %B Return the directory part ("base") of %D/%F 484 * %f Return the filename part of %D/%F 485 * 486 * Does not check for overflow - caution! 487 * 488 */ 489void 490format_cmd(char *buf, char *fmt, char *dir, char *name) 491{ 492 char *cp, scratch[FILENAME_MAX * 2]; 493 494 while (*fmt) { 495 if (*fmt == '%') { 496 switch (*++fmt) { 497 case 'F': 498 strcpy(buf, name); 499 buf += strlen(name); 500 break; 501 502 case 'D': 503 strcpy(buf, dir); 504 buf += strlen(dir); 505 break; 506 507 case 'B': 508 sprintf(scratch, "%s/%s", dir, name); 509 cp = &scratch[strlen(scratch) - 1]; 510 while (cp != scratch && *cp != '/') 511 --cp; 512 *cp = '\0'; 513 strcpy(buf, scratch); 514 buf += strlen(scratch); 515 break; 516 517 case 'f': 518 sprintf(scratch, "%s/%s", dir, name); 519 cp = &scratch[strlen(scratch) - 1]; 520 while (cp != scratch && *(cp - 1) != '/') 521 --cp; 522 strcpy(buf, cp); 523 buf += strlen(cp); 524 break; 525 526 default: 527 *buf++ = *fmt; 528 break; 529 } 530 ++fmt; 531 } 532 else 533 *buf++ = *fmt++; 534 } 535 *buf = '\0'; 536}
|