nsinstall.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /*
  6. ** Netscape portable install command.
  7. **
  8. ** Brendan Eich, 7/20/95
  9. */
  10. #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
  11. #include <assert.h>
  12. #include <fcntl.h>
  13. #include <grp.h>
  14. #include <pwd.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <unistd.h>
  18. #include <utime.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <dirent.h>
  22. #include <errno.h>
  23. #include <stdarg.h>
  24. #ifdef USE_REENTRANT_LIBC
  25. #include "libc_r.h"
  26. #endif /* USE_REENTRANT_LIBC */
  27. #include "pathsub.h"
  28. #define HAVE_FCHMOD
  29. /*
  30. * Does getcwd() take NULL as the first argument and malloc
  31. * the result buffer?
  32. */
  33. #if !defined(DARWIN)
  34. #define GETCWD_CAN_MALLOC
  35. #endif
  36. #if defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)
  37. #include <getopt.h>
  38. #endif
  39. #if defined(SCO) || defined(UNIXWARE)
  40. #if !defined(S_ISLNK) && defined(S_IFLNK)
  41. #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
  42. #endif
  43. #endif
  44. #ifdef QNX
  45. #define d_ino d_stat.st_ino
  46. #endif
  47. static void
  48. usage(void)
  49. {
  50. fprintf(stderr,
  51. "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
  52. " %*s [-DdltR] file [file ...] directory\n",
  53. program, (int)strlen(program), "");
  54. exit(2);
  55. }
  56. static int
  57. mkdirs(char *path, mode_t mode)
  58. {
  59. char *cp;
  60. struct stat sb;
  61. int res;
  62. while (*path == '/' && path[1] == '/') {
  63. path++;
  64. }
  65. for (cp = strrchr(path, '/'); cp && cp != path && cp[-1] == '/'; cp--)
  66. ;
  67. if (cp && cp != path) {
  68. *cp = '\0';
  69. if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
  70. mkdirs(path, mode) < 0) {
  71. return -1;
  72. }
  73. *cp = '/';
  74. }
  75. res = mkdir(path, mode);
  76. if ((res != 0) && (errno == EEXIST)) {
  77. return 0;
  78. }
  79. else {
  80. return res;
  81. }
  82. }
  83. static uid_t
  84. touid(char *owner)
  85. {
  86. struct passwd *pw;
  87. uid_t uid;
  88. char *cp;
  89. pw = getpwnam(owner);
  90. if (pw) {
  91. return pw->pw_uid;
  92. }
  93. uid = strtol(owner, &cp, 0);
  94. if (uid == 0 && cp == owner) {
  95. fail("cannot find uid for %s", owner);
  96. }
  97. return uid;
  98. }
  99. static gid_t
  100. togid(char *group)
  101. {
  102. struct group *gr;
  103. gid_t gid;
  104. char *cp;
  105. gr = getgrnam(group);
  106. if (gr) {
  107. return gr->gr_gid;
  108. }
  109. gid = strtol(group, &cp, 0);
  110. if (gid == 0 && cp == group) {
  111. fail("cannot find gid for %s", group);
  112. }
  113. return gid;
  114. }
  115. int
  116. main(int argc, char **argv)
  117. {
  118. int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
  119. mode_t mode = 0755;
  120. char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
  121. uid_t uid;
  122. gid_t gid;
  123. struct stat sb, tosb;
  124. struct utimbuf utb;
  125. program = argv[0];
  126. cwd = linkname = linkprefix = owner = group = 0;
  127. onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
  128. while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
  129. switch (opt) {
  130. case 'C':
  131. cwd = optarg;
  132. break;
  133. case 'D':
  134. onlydir = 1;
  135. break;
  136. case 'd':
  137. dodir = 1;
  138. break;
  139. case 'l':
  140. dolink = 1;
  141. break;
  142. case 'L':
  143. linkprefix = optarg;
  144. lplen = strlen(linkprefix);
  145. dolink = 1;
  146. break;
  147. case 'R':
  148. dolink = dorelsymlink = 1;
  149. break;
  150. case 'm':
  151. mode = strtoul(optarg, &cp, 8);
  152. if (mode == 0 && cp == optarg) {
  153. usage();
  154. }
  155. break;
  156. case 'o':
  157. owner = optarg;
  158. break;
  159. case 'g':
  160. group = optarg;
  161. break;
  162. case 't':
  163. dotimes = 1;
  164. break;
  165. default:
  166. usage();
  167. }
  168. }
  169. argc -= optind;
  170. argv += optind;
  171. if (argc < 2 - onlydir) {
  172. usage();
  173. }
  174. todir = argv[argc-1];
  175. if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
  176. mkdirs(todir, 0777) < 0) {
  177. fail("cannot make directory %s", todir);
  178. }
  179. if (onlydir) {
  180. return 0;
  181. }
  182. if (!cwd) {
  183. #ifdef GETCWD_CAN_MALLOC
  184. cwd = getcwd(0, PATH_MAX);
  185. #else
  186. cwd = malloc(PATH_MAX + 1);
  187. cwd = getcwd(cwd, PATH_MAX);
  188. #endif
  189. }
  190. xchdir(todir);
  191. #ifdef GETCWD_CAN_MALLOC
  192. todir = getcwd(0, PATH_MAX);
  193. #else
  194. todir = malloc(PATH_MAX + 1);
  195. todir = getcwd(todir, PATH_MAX);
  196. #endif
  197. xchdir(cwd);
  198. tdlen = strlen(todir);
  199. uid = owner ? touid(owner) : -1;
  200. gid = group ? togid(group) : -1;
  201. while (--argc > 0) {
  202. name = *argv++;
  203. len = strlen(name);
  204. base = xbasename(name);
  205. bnlen = strlen(base);
  206. toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
  207. sprintf(toname, "%s/%s", todir, base);
  208. exists = (lstat(toname, &tosb) == 0);
  209. if (dodir) {
  210. /* -d means create a directory, always */
  211. if (exists && !S_ISDIR(tosb.st_mode)) {
  212. (void) unlink(toname);
  213. exists = 0;
  214. }
  215. if (!exists && mkdir(toname, mode) < 0) {
  216. fail("cannot make directory %s", toname);
  217. }
  218. if ((owner || group) && chown(toname, uid, gid) < 0) {
  219. fail("cannot change owner of %s", toname);
  220. }
  221. } else if (dolink) {
  222. if (*name == '/') {
  223. /* source is absolute pathname, link to it directly */
  224. linkname = 0;
  225. } else {
  226. if (linkprefix) {
  227. /* -L implies -l and prefixes names with a $cwd arg. */
  228. len += lplen + 1;
  229. linkname = (char*)xmalloc(len + 1);
  230. sprintf(linkname, "%s/%s", linkprefix, name);
  231. } else if (dorelsymlink) {
  232. /* Symlink the relative path from todir to source name. */
  233. linkname = (char*)xmalloc(PATH_MAX);
  234. if (*todir == '/') {
  235. /* todir is absolute: skip over common prefix. */
  236. lplen = relatepaths(todir, cwd, linkname);
  237. strcpy(linkname + lplen, name);
  238. } else {
  239. /* todir is named by a relative path: reverse it. */
  240. reversepath(todir, name, len, linkname);
  241. xchdir(cwd);
  242. }
  243. len = strlen(linkname);
  244. }
  245. name = linkname;
  246. }
  247. /* Check for a pre-existing symlink with identical content. */
  248. if (exists &&
  249. (!S_ISLNK(tosb.st_mode) ||
  250. readlink(toname, buf, sizeof buf) != len ||
  251. strncmp(buf, name, len) != 0)) {
  252. (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
  253. exists = 0;
  254. }
  255. if (!exists && symlink(name, toname) < 0) {
  256. fail("cannot make symbolic link %s", toname);
  257. }
  258. #ifdef HAVE_LCHOWN
  259. if ((owner || group) && lchown(toname, uid, gid) < 0) {
  260. fail("cannot change owner of %s", toname);
  261. }
  262. #endif
  263. if (linkname) {
  264. free(linkname);
  265. linkname = 0;
  266. }
  267. } else {
  268. /* Copy from name to toname, which might be the same file. */
  269. fromfd = open(name, O_RDONLY);
  270. if (fromfd < 0 || fstat(fromfd, &sb) < 0) {
  271. fail("cannot access %s", name);
  272. }
  273. if (exists && (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) {
  274. (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
  275. }
  276. tofd = open(toname, O_CREAT | O_WRONLY, 0666);
  277. if (tofd < 0) {
  278. fail("cannot create %s", toname);
  279. }
  280. bp = buf;
  281. while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
  282. while ((wc = write(tofd, bp, cc)) > 0) {
  283. if ((cc -= wc) == 0) {
  284. break;
  285. }
  286. bp += wc;
  287. }
  288. if (wc < 0) {
  289. fail("cannot write to %s", toname);
  290. }
  291. }
  292. if (cc < 0) {
  293. fail("cannot read from %s", name);
  294. }
  295. if (ftruncate(tofd, sb.st_size) < 0) {
  296. fail("cannot truncate %s", toname);
  297. }
  298. if (dotimes) {
  299. utb.actime = sb.st_atime;
  300. utb.modtime = sb.st_mtime;
  301. if (utime(toname, &utb) < 0) {
  302. fail("cannot set times of %s", toname);
  303. }
  304. }
  305. #ifdef HAVE_FCHMOD
  306. if (fchmod(tofd, mode) < 0)
  307. #else
  308. if (chmod(toname, mode) < 0)
  309. #endif
  310. fail("cannot change mode of %s", toname);
  311. if ((owner || group) && fchown(tofd, uid, gid) < 0) {
  312. fail("cannot change owner of %s", toname);
  313. }
  314. /* Must check for delayed (NFS) write errors on close. */
  315. if (close(tofd) < 0) {
  316. fail("cannot write to %s", toname);
  317. }
  318. close(fromfd);
  319. }
  320. free(toname);
  321. }
  322. free(cwd);
  323. free(todir);
  324. return 0;
  325. }
  326. /*
  327. ** Pathname subroutines.
  328. **
  329. ** Brendan Eich, 8/29/95
  330. */
  331. char *program;
  332. void
  333. fail(char *format, ...)
  334. {
  335. int error;
  336. va_list ap;
  337. #ifdef USE_REENTRANT_LIBC
  338. R_STRERROR_INIT_R();
  339. #endif
  340. error = errno;
  341. fprintf(stderr, "%s: ", program);
  342. va_start(ap, format);
  343. vfprintf(stderr, format, ap);
  344. va_end(ap);
  345. if (error)
  346. #ifdef USE_REENTRANT_LIBC
  347. R_STRERROR_R(errno);
  348. fprintf(stderr, ": %s", r_strerror_r);
  349. #else
  350. fprintf(stderr, ": %s", strerror(errno));
  351. #endif
  352. putc('\n', stderr);
  353. exit(1);
  354. }
  355. char *
  356. getcomponent(char *path, char *name)
  357. {
  358. if (*path == '\0') {
  359. return 0;
  360. }
  361. if (*path == '/') {
  362. *name++ = '/';
  363. } else {
  364. do {
  365. *name++ = *path++;
  366. } while (*path != '/' && *path != '\0');
  367. }
  368. *name = '\0';
  369. while (*path == '/') {
  370. path++;
  371. }
  372. return path;
  373. }
  374. #ifdef UNIXWARE_READDIR_BUFFER_TOO_SMALL
  375. /* Sigh. The static buffer in Unixware's readdir is too small. */
  376. struct dirent * readdir(DIR *d)
  377. {
  378. static struct dirent *buf = NULL;
  379. #define MAX_PATH_LEN 1024
  380. if(buf == NULL)
  381. buf = (struct dirent *) malloc(sizeof(struct dirent) + MAX_PATH_LEN)
  382. ;
  383. return(readdir_r(d, buf));
  384. }
  385. #endif
  386. char *
  387. ino2name(ino_t ino, char *dir)
  388. {
  389. DIR *dp;
  390. struct dirent *ep;
  391. char *name;
  392. dp = opendir("..");
  393. if (!dp) {
  394. fail("cannot read parent directory");
  395. }
  396. for (;;) {
  397. if (!(ep = readdir(dp))) {
  398. fail("cannot find current directory");
  399. }
  400. if (ep->d_ino == ino) {
  401. break;
  402. }
  403. }
  404. name = xstrdup(ep->d_name);
  405. closedir(dp);
  406. return name;
  407. }
  408. void *
  409. xmalloc(size_t size)
  410. {
  411. void *p = malloc(size);
  412. if (!p) {
  413. fail("cannot allocate %u bytes", size);
  414. }
  415. return p;
  416. }
  417. char *
  418. xstrdup(char *s)
  419. {
  420. return strcpy((char*)xmalloc(strlen(s) + 1), s);
  421. }
  422. char *
  423. xbasename(char *path)
  424. {
  425. char *cp;
  426. while ((cp = strrchr(path, '/')) && cp[1] == '\0') {
  427. *cp = '\0';
  428. }
  429. if (!cp) {
  430. return path;
  431. }
  432. return cp + 1;
  433. }
  434. void
  435. xchdir(char *dir)
  436. {
  437. if (chdir(dir) < 0) {
  438. fail("cannot change directory to %s", dir);
  439. }
  440. }
  441. int
  442. relatepaths(char *from, char *to, char *outpath)
  443. {
  444. char *cp, *cp2;
  445. int len;
  446. char buf[NAME_MAX];
  447. assert(*from == '/' && *to == '/');
  448. for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++)
  449. if (*cp == '\0') {
  450. break;
  451. }
  452. while (cp[-1] != '/') {
  453. cp--, cp2--;
  454. }
  455. if (cp - 1 == to) {
  456. /* closest common ancestor is /, so use full pathname */
  457. len = strlen(strcpy(outpath, to));
  458. if (outpath[len] != '/') {
  459. outpath[len++] = '/';
  460. outpath[len] = '\0';
  461. }
  462. } else {
  463. len = 0;
  464. while ((cp2 = getcomponent(cp2, buf)) != 0) {
  465. strcpy(outpath + len, "../");
  466. len += 3;
  467. }
  468. while ((cp = getcomponent(cp, buf)) != 0) {
  469. sprintf(outpath + len, "%s/", buf);
  470. len += strlen(outpath + len);
  471. }
  472. }
  473. return len;
  474. }
  475. void
  476. reversepath(char *inpath, char *name, int len, char *outpath)
  477. {
  478. char *cp, *cp2;
  479. char buf[NAME_MAX];
  480. struct stat sb;
  481. cp = strcpy(outpath + PATH_MAX - (len + 1), name);
  482. cp2 = inpath;
  483. while ((cp2 = getcomponent(cp2, buf)) != 0) {
  484. if (strcmp(buf, ".") == 0) {
  485. continue;
  486. }
  487. if (strcmp(buf, "..") == 0) {
  488. if (stat(".", &sb) < 0) {
  489. fail("cannot stat current directory");
  490. }
  491. name = ino2name(sb.st_ino, "..");
  492. len = strlen(name);
  493. cp -= len + 1;
  494. strcpy(cp, name);
  495. cp[len] = '/';
  496. free(name);
  497. xchdir("..");
  498. } else {
  499. cp -= 3;
  500. memcpy(cp, "../", 3);
  501. xchdir(buf);
  502. }
  503. }
  504. strcpy(outpath, cp);
  505. }