nsinstall.c (9608B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 ** Netscape portable install command. 7 */ 8 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */ 9 #include <assert.h> 10 #include <fcntl.h> 11 #include <string.h> 12 #if defined(_WINDOWS) 13 #include <windows.h> 14 typedef unsigned int mode_t; 15 #else 16 #include <grp.h> 17 #include <pwd.h> 18 #include <errno.h> 19 #include <stdlib.h> 20 #include <unistd.h> 21 #include <utime.h> 22 #endif 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include "pathsub.h" 26 27 #define HAVE_LCHOWN 28 29 #if defined(AIX) || defined(HPUX) || defined(LINUX) || defined(NTO) || defined(DARWIN) || defined(__riscos__) 30 #undef HAVE_LCHOWN 31 #endif 32 33 #define HAVE_FCHMOD 34 35 #ifdef LINUX 36 #include <getopt.h> 37 #endif 38 39 #if defined(NCR) 40 #if !defined(S_ISLNK) && defined(S_IFLNK) 41 #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK) 42 #endif 43 #endif 44 45 #ifdef GETCWD_CANT_MALLOC 46 /* 47 * this should probably go into a utility library in case other applications 48 * need it. 49 */ 50 static char * 51 getcwd_do_malloc(char *path, int len) { 52 53 if (!path) { 54 path = malloc(PATH_MAX +1); 55 if (!path) return NULL; 56 } 57 return getcwd(path, PATH_MAX); 58 } 59 #define GETCWD getcwd_do_malloc 60 #else 61 #define GETCWD getcwd 62 #endif 63 64 65 static void 66 usage(void) 67 { 68 fprintf(stderr, 69 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n" 70 " %*s [-DdltR] file [file ...] directory\n", 71 program, (int)strlen(program), ""); 72 exit(2); 73 } 74 75 /* this is more-or-less equivalent to mkdir -p */ 76 static int 77 mkdirs(char *path, mode_t mode) 78 { 79 char * cp; 80 int rv; 81 struct stat sb; 82 83 if (!path || !path[0]) 84 fail("Null pointer or empty string passed to mkdirs()"); 85 while (*path == '/' && path[1] == '/') 86 path++; 87 for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--); 88 if (cp && cp != path) { 89 *cp = '\0'; 90 if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) && 91 mkdirs(path, mode) < 0) { 92 return -1; 93 } 94 *cp = '/'; 95 } 96 rv = mkdir(path, mode); 97 if (rv) { 98 if (errno != EEXIST) 99 fail("mkdirs cannot make %s", path); 100 fprintf(stderr, "directory creation race: %s\n", path); 101 if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) 102 rv = 0; 103 } 104 return rv; 105 } 106 107 static uid_t 108 touid(char *owner) 109 { 110 struct passwd *pw; 111 uid_t uid; 112 char *cp; 113 114 if (!owner || !owner[0]) 115 fail("Null pointer or empty string passed to touid()"); 116 pw = getpwnam(owner); 117 if (pw) 118 return pw->pw_uid; 119 uid = strtol(owner, &cp, 0); 120 if (uid == 0 && cp == owner) 121 fail("cannot find uid for %s", owner); 122 return uid; 123 } 124 125 static gid_t 126 togid(char *group) 127 { 128 struct group *gr; 129 gid_t gid; 130 char *cp; 131 132 if (!group || !group[0]) 133 fail("Null pointer or empty string passed to togid()"); 134 gr = getgrnam(group); 135 if (gr) 136 return gr->gr_gid; 137 gid = strtol(group, &cp, 0); 138 if (gid == 0 && cp == group) 139 fail("cannot find gid for %s", group); 140 return gid; 141 } 142 143 void * const uninit = (void *)0xdeadbeef; 144 145 int 146 main(int argc, char **argv) 147 { 148 char * base = uninit; 149 char * bp = uninit; 150 char * cp = uninit; 151 char * cwd = 0; 152 char * group = 0; 153 char * linkname = 0; 154 char * linkprefix = 0; 155 char * name = uninit; 156 char * owner = 0; 157 char * todir = uninit; 158 char * toname = uninit; 159 160 int bnlen = -1; 161 int cc = 0; 162 int dodir = 0; 163 int dolink = 0; 164 int dorelsymlink = 0; 165 int dotimes = 0; 166 int exists = 0; 167 int fromfd = -1; 168 int len = -1; 169 int lplen = 0; 170 int onlydir = 0; 171 int opt = -1; 172 int tdlen = -1; 173 int tofd = -1; 174 int wc = -1; 175 176 mode_t mode = 0755; 177 178 uid_t uid = -1; 179 gid_t gid = -1; 180 181 struct stat sb; 182 struct stat tosb; 183 struct utimbuf utb; 184 char buf[BUFSIZ]; 185 186 program = strrchr(argv[0], '/'); 187 if (!program) 188 program = strrchr(argv[0], '\\'); 189 program = program ? program+1 : argv[0]; 190 191 192 while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) { 193 switch (opt) { 194 case 'C': cwd = optarg; break; 195 case 'D': onlydir = 1; break; 196 case 'd': dodir = 1; break; 197 case 'l': dolink = 1; break; 198 case 'L': 199 linkprefix = optarg; 200 lplen = strlen(linkprefix); 201 dolink = 1; 202 break; 203 case 'R': dolink = dorelsymlink = 1; break; 204 case 'm': 205 mode = strtoul(optarg, &cp, 8); 206 if (mode == 0 && cp == optarg) 207 usage(); 208 break; 209 case 'o': owner = optarg; break; 210 case 'g': group = optarg; break; 211 case 't': dotimes = 1; break; 212 default: usage(); 213 } 214 } 215 216 argc -= optind; 217 argv += optind; 218 if (argc < 2 - onlydir) 219 usage(); 220 221 todir = argv[argc-1]; 222 if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) && 223 mkdirs(todir, 0777) < 0) { 224 fail("cannot mkdir -p %s", todir); 225 } 226 if (onlydir) 227 return 0; 228 229 if (!cwd) { 230 cwd = GETCWD(0, PATH_MAX); 231 if (!cwd) 232 fail("could not get CWD"); 233 } 234 235 /* make sure we can get into todir. */ 236 xchdir(todir); 237 todir = GETCWD(0, PATH_MAX); 238 if (!todir) 239 fail("could not get CWD in todir"); 240 tdlen = strlen(todir); 241 242 /* back to original directory. */ 243 xchdir(cwd); 244 245 uid = owner ? touid(owner) : -1; 246 gid = group ? togid(group) : -1; 247 248 while (--argc > 0) { 249 name = *argv++; 250 len = strlen(name); 251 base = xbasename(name); 252 bnlen = strlen(base); 253 size_t toname_len = tdlen + 1 + bnlen + 1; 254 toname = (char*)xmalloc(toname_len); 255 snprintf(toname, toname_len, "%s/%s", todir, base); 256 retry: 257 exists = (lstat(toname, &tosb) == 0); 258 259 if (dodir) { 260 /* -d means create a directory, always */ 261 if (exists && !S_ISDIR(tosb.st_mode)) { 262 int rv = unlink(toname); 263 if (rv) 264 fail("cannot unlink %s", toname); 265 exists = 0; 266 } 267 if (!exists && mkdir(toname, mode) < 0) { 268 /* we probably have two nsinstall programs in a race here. */ 269 if (errno == EEXIST && !stat(toname, &sb) && 270 S_ISDIR(sb.st_mode)) { 271 fprintf(stderr, "directory creation race: %s\n", toname); 272 goto retry; 273 } 274 fail("cannot make directory %s", toname); 275 } 276 if ((owner || group) && chown(toname, uid, gid) < 0) 277 fail("cannot change owner of %s", toname); 278 } else if (dolink) { 279 if (*name == '/') { 280 /* source is absolute pathname, link to it directly */ 281 linkname = 0; 282 } else { 283 if (linkprefix) { 284 /* -L implies -l and prefixes names with a $cwd arg. */ 285 len += lplen + 1; 286 linkname = (char*)xmalloc(len + 1); 287 snprintf(linkname, len+1, "%s/%s", linkprefix, name); 288 } else if (dorelsymlink) { 289 /* Symlink the relative path from todir to source name. */ 290 linkname = (char*)xmalloc(PATH_MAX); 291 292 if (*todir == '/') { 293 /* todir is absolute: skip over common prefix. */ 294 lplen = relatepaths(todir, cwd, linkname); 295 strcpy(linkname + lplen, name); 296 } else { 297 /* todir is named by a relative path: reverse it. */ 298 reversepath(todir, name, len, linkname); 299 xchdir(cwd); 300 } 301 302 len = strlen(linkname); 303 } 304 name = linkname; 305 } 306 307 /* Check for a pre-existing symlink with identical content. */ 308 if (exists && 309 (!S_ISLNK(tosb.st_mode) || 310 readlink(toname, buf, sizeof buf) != len || 311 strncmp(buf, name, len) != 0)) { 312 int rmrv; 313 rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); 314 if (rmrv < 0) { 315 fail("destination exists, cannot remove %s", toname); 316 } 317 exists = 0; 318 } 319 if (!exists && symlink(name, toname) < 0) { 320 if (errno == EEXIST) { 321 fprintf(stderr, "symlink creation race: %s\n", toname); 322 fail("symlink was attempted in working directory %s " 323 "from %s to %s.\n", cwd, name, toname); 324 goto retry; 325 } 326 diagnosePath(toname); 327 fail("cannot make symbolic link %s", toname); 328 } 329 #ifdef HAVE_LCHOWN 330 if ((owner || group) && lchown(toname, uid, gid) < 0) 331 fail("cannot change owner of %s", toname); 332 #endif 333 334 if (linkname) { 335 free(linkname); 336 linkname = 0; 337 } 338 } else { 339 /* Copy from name to toname, which might be the same file. */ 340 fromfd = open(name, O_RDONLY); 341 if (fromfd < 0 || fstat(fromfd, &sb) < 0) 342 fail("cannot access %s", name); 343 if (exists && 344 (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) { 345 int rmrv; 346 rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); 347 if (rmrv < 0) { 348 fail("destination exists, cannot remove %s", toname); 349 } 350 } 351 tofd = open(toname, O_CREAT | O_WRONLY, 0666); 352 if (tofd < 0) 353 fail("cannot create %s", toname); 354 355 bp = buf; 356 while ((cc = read(fromfd, bp, sizeof buf)) > 0) { 357 while ((wc = write(tofd, bp, cc)) > 0) { 358 if ((cc -= wc) == 0) 359 break; 360 bp += wc; 361 } 362 if (wc < 0) 363 fail("cannot write to %s", toname); 364 } 365 if (cc < 0) 366 fail("cannot read from %s", name); 367 368 if (ftruncate(tofd, sb.st_size) < 0) 369 fail("cannot truncate %s", toname); 370 if (dotimes) { 371 utb.actime = sb.st_atime; 372 utb.modtime = sb.st_mtime; 373 if (utime(toname, &utb) < 0) 374 fail("cannot set times of %s", toname); 375 } 376 #ifdef HAVE_FCHMOD 377 if (fchmod(tofd, mode) < 0) 378 #else 379 if (chmod(toname, mode) < 0) 380 #endif 381 fail("cannot change mode of %s", toname); 382 383 if ((owner || group) && fchown(tofd, uid, gid) < 0) 384 fail("cannot change owner of %s", toname); 385 386 /* Must check for delayed (NFS) write errors on close. */ 387 if (close(tofd) < 0) 388 fail("close reports write error on %s", toname); 389 close(fromfd); 390 } 391 392 free(toname); 393 } 394 395 free(cwd); 396 free(todir); 397 return 0; 398 }