Import from 1.9a8 tarball
[mozilla-nss.git] / security / coreconf / nsinstall / nsinstall.c
blob0b7feff393f688d788ec230dfa65d7b54665ffbe
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
38 ** Netscape portable install command.
40 #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
41 #include <assert.h>
42 #include <fcntl.h>
43 #include <string.h>
44 #if defined(_WINDOWS)
45 #include <windows.h>
46 typedef unsigned int mode_t;
47 #else
48 #include <grp.h>
49 #include <pwd.h>
50 #include <errno.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <utime.h>
54 #endif
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include "pathsub.h"
59 #define HAVE_LCHOWN
61 #if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) || defined(VMS) || defined(NTO) || defined(DARWIN) || defined(BEOS)
62 #undef HAVE_LCHOWN
63 #endif
65 #define HAVE_FCHMOD
67 #if defined(BEOS)
68 #undef HAVE_FCHMOD
69 #endif
71 #ifdef LINUX
72 #include <getopt.h>
73 #endif
75 #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC)
76 #if !defined(S_ISLNK) && defined(S_IFLNK)
77 #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
78 #endif
79 #endif
81 #if defined(SNI)
82 extern int fchmod(int fildes, mode_t mode);
83 #endif
86 #ifdef GETCWD_CANT_MALLOC
88 * this should probably go into a utility library in case other applications
89 * need it.
91 static char *
92 getcwd_do_malloc(char *path, int len) {
94 if (!path) {
95 path = malloc(PATH_MAX +1);
96 if (!path) return NULL;
98 return getcwd(path, PATH_MAX);
100 #define GETCWD getcwd_do_malloc
101 #else
102 #define GETCWD getcwd
103 #endif
106 static void
107 usage(void)
109 fprintf(stderr,
110 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
111 " %*s [-DdltR] file [file ...] directory\n",
112 program, (int)strlen(program), "");
113 exit(2);
116 /* this is more-or-less equivalent to mkdir -p */
117 static int
118 mkdirs(char *path, mode_t mode)
120 char * cp;
121 int rv;
122 struct stat sb;
124 if (!path || !path[0])
125 fail("Null pointer or empty string passed to mkdirs()");
126 while (*path == '/' && path[1] == '/')
127 path++;
128 while ((cp = strrchr(path, '/')) && cp[1] == '\0')
129 *cp = '\0';
130 if (cp && cp != path) {
131 *cp = '\0';
132 if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
133 mkdirs(path, mode) < 0) {
134 return -1;
136 *cp = '/';
138 rv = mkdir(path, mode);
139 if (rv) {
140 if (errno != EEXIST)
141 fail("mkdirs cannot make %s", path);
142 fprintf(stderr, "directory creation race: %s\n", path);
143 if (!stat(path, &sb) && S_ISDIR(sb.st_mode))
144 rv = 0;
146 return rv;
149 static uid_t
150 touid(char *owner)
152 struct passwd *pw;
153 uid_t uid;
154 char *cp;
156 if (!owner || !owner[0])
157 fail("Null pointer or empty string passed to touid()");
158 pw = getpwnam(owner);
159 if (pw)
160 return pw->pw_uid;
161 uid = strtol(owner, &cp, 0);
162 if (uid == 0 && cp == owner)
163 fail("cannot find uid for %s", owner);
164 return uid;
167 static gid_t
168 togid(char *group)
170 struct group *gr;
171 gid_t gid;
172 char *cp;
174 if (!group || !group[0])
175 fail("Null pointer or empty string passed to togid()");
176 gr = getgrnam(group);
177 if (gr)
178 return gr->gr_gid;
179 gid = strtol(group, &cp, 0);
180 if (gid == 0 && cp == group)
181 fail("cannot find gid for %s", group);
182 return gid;
185 void * const uninit = (void *)0xdeadbeef;
188 main(int argc, char **argv)
190 char * base = uninit;
191 char * bp = uninit;
192 char * cp = uninit;
193 char * cwd = 0;
194 char * group = 0;
195 char * linkname = 0;
196 char * linkprefix = 0;
197 char * name = uninit;
198 char * owner = 0;
199 char * todir = uninit;
200 char * toname = uninit;
202 int bnlen = -1;
203 int cc = 0;
204 int dodir = 0;
205 int dolink = 0;
206 int dorelsymlink = 0;
207 int dotimes = 0;
208 int exists = 0;
209 int fromfd = -1;
210 int len = -1;
211 int lplen = 0;
212 int onlydir = 0;
213 int opt = -1;
214 int tdlen = -1;
215 int tofd = -1;
216 int wc = -1;
218 mode_t mode = 0755;
220 uid_t uid = -1;
221 gid_t gid = -1;
223 struct stat sb;
224 struct stat tosb;
225 struct utimbuf utb;
226 char buf[BUFSIZ];
228 program = strrchr(argv[0], '/');
229 if (!program)
230 program = strrchr(argv[0], '\\');
231 program = program ? program+1 : argv[0];
234 while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
235 switch (opt) {
236 case 'C': cwd = optarg; break;
237 case 'D': onlydir = 1; break;
238 case 'd': dodir = 1; break;
239 case 'l': dolink = 1; break;
240 case 'L':
241 linkprefix = optarg;
242 lplen = strlen(linkprefix);
243 dolink = 1;
244 break;
245 case 'R': dolink = dorelsymlink = 1; break;
246 case 'm':
247 mode = strtoul(optarg, &cp, 8);
248 if (mode == 0 && cp == optarg)
249 usage();
250 break;
251 case 'o': owner = optarg; break;
252 case 'g': group = optarg; break;
253 case 't': dotimes = 1; break;
254 default: usage();
258 argc -= optind;
259 argv += optind;
260 if (argc < 2 - onlydir)
261 usage();
263 todir = argv[argc-1];
264 if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
265 mkdirs(todir, 0777) < 0) {
266 fail("cannot mkdir -p %s", todir);
268 if (onlydir)
269 return 0;
271 if (!cwd) {
272 cwd = GETCWD(0, PATH_MAX);
273 if (!cwd)
274 fail("could not get CWD");
277 /* make sure we can get into todir. */
278 xchdir(todir);
279 todir = GETCWD(0, PATH_MAX);
280 if (!todir)
281 fail("could not get CWD in todir");
282 tdlen = strlen(todir);
284 /* back to original directory. */
285 xchdir(cwd);
287 uid = owner ? touid(owner) : -1;
288 gid = group ? togid(group) : -1;
290 while (--argc > 0) {
291 name = *argv++;
292 len = strlen(name);
293 base = xbasename(name);
294 bnlen = strlen(base);
295 toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
296 sprintf(toname, "%s/%s", todir, base);
297 retry:
298 exists = (lstat(toname, &tosb) == 0);
300 if (dodir) {
301 /* -d means create a directory, always */
302 if (exists && !S_ISDIR(tosb.st_mode)) {
303 int rv = unlink(toname);
304 if (rv)
305 fail("cannot unlink %s", toname);
306 exists = 0;
308 if (!exists && mkdir(toname, mode) < 0) {
309 /* we probably have two nsinstall programs in a race here. */
310 if (errno == EEXIST && !stat(toname, &sb) &&
311 S_ISDIR(sb.st_mode)) {
312 fprintf(stderr, "directory creation race: %s\n", toname);
313 goto retry;
315 fail("cannot make directory %s", toname);
317 if ((owner || group) && chown(toname, uid, gid) < 0)
318 fail("cannot change owner of %s", toname);
319 } else if (dolink) {
320 if (*name == '/') {
321 /* source is absolute pathname, link to it directly */
322 linkname = 0;
323 } else {
324 if (linkprefix) {
325 /* -L implies -l and prefixes names with a $cwd arg. */
326 len += lplen + 1;
327 linkname = (char*)xmalloc(len + 1);
328 sprintf(linkname, "%s/%s", linkprefix, name);
329 } else if (dorelsymlink) {
330 /* Symlink the relative path from todir to source name. */
331 linkname = (char*)xmalloc(PATH_MAX);
333 if (*todir == '/') {
334 /* todir is absolute: skip over common prefix. */
335 lplen = relatepaths(todir, cwd, linkname);
336 strcpy(linkname + lplen, name);
337 } else {
338 /* todir is named by a relative path: reverse it. */
339 reversepath(todir, name, len, linkname);
340 xchdir(cwd);
343 len = strlen(linkname);
345 name = linkname;
348 /* Check for a pre-existing symlink with identical content. */
349 if (exists &&
350 (!S_ISLNK(tosb.st_mode) ||
351 readlink(toname, buf, sizeof buf) != len ||
352 strncmp(buf, name, len) != 0)) {
353 int rmrv;
354 rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
355 if (rmrv < 0) {
356 fail("destination exists, cannot remove %s", toname);
358 exists = 0;
360 if (!exists && symlink(name, toname) < 0) {
361 if (errno == EEXIST) {
362 fprintf(stderr, "symlink creation race: %s\n", toname);
363 goto retry;
365 diagnosePath(toname);
366 fail("cannot make symbolic link %s", toname);
368 #ifdef HAVE_LCHOWN
369 if ((owner || group) && lchown(toname, uid, gid) < 0)
370 fail("cannot change owner of %s", toname);
371 #endif
373 if (linkname) {
374 free(linkname);
375 linkname = 0;
377 } else {
378 /* Copy from name to toname, which might be the same file. */
379 fromfd = open(name, O_RDONLY);
380 if (fromfd < 0 || fstat(fromfd, &sb) < 0)
381 fail("cannot access %s", name);
382 if (exists &&
383 (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) {
384 int rmrv;
385 rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
386 if (rmrv < 0) {
387 fail("destination exists, cannot remove %s", toname);
390 tofd = open(toname, O_CREAT | O_WRONLY, 0666);
391 if (tofd < 0)
392 fail("cannot create %s", toname);
394 bp = buf;
395 while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
396 while ((wc = write(tofd, bp, cc)) > 0) {
397 if ((cc -= wc) == 0)
398 break;
399 bp += wc;
401 if (wc < 0)
402 fail("cannot write to %s", toname);
404 if (cc < 0)
405 fail("cannot read from %s", name);
407 if (ftruncate(tofd, sb.st_size) < 0)
408 fail("cannot truncate %s", toname);
410 ** On OpenVMS we can't chmod() until the file is closed, and we
411 ** have to utime() last since fchown/chmod alter the timestamps.
413 #ifndef VMS
414 if (dotimes) {
415 utb.actime = sb.st_atime;
416 utb.modtime = sb.st_mtime;
417 if (utime(toname, &utb) < 0)
418 fail("cannot set times of %s", toname);
420 #ifdef HAVE_FCHMOD
421 if (fchmod(tofd, mode) < 0)
422 #else
423 if (chmod(toname, mode) < 0)
424 #endif
425 fail("cannot change mode of %s", toname);
426 #endif
427 if ((owner || group) && fchown(tofd, uid, gid) < 0)
428 fail("cannot change owner of %s", toname);
430 /* Must check for delayed (NFS) write errors on close. */
431 if (close(tofd) < 0)
432 fail("close reports write error on %s", toname);
433 close(fromfd);
434 #ifdef VMS
435 if (chmod(toname, mode) < 0)
436 fail("cannot change mode of %s", toname);
437 if (dotimes) {
438 utb.actime = sb.st_atime;
439 utb.modtime = sb.st_mtime;
440 if (utime(toname, &utb) < 0)
441 fail("cannot set times of %s", toname);
443 #endif
446 free(toname);
449 free(cwd);
450 free(todir);
451 return 0;