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
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.
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 */
46 typedef unsigned int mode_t
;
55 #include <sys/types.h>
61 #if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) || defined(VMS) || defined(NTO) || defined(DARWIN) || defined(BEOS)
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)
82 extern int fchmod(int fildes
, mode_t mode
);
86 #ifdef GETCWD_CANT_MALLOC
88 * this should probably go into a utility library in case other applications
92 getcwd_do_malloc(char *path
, int len
) {
95 path
= malloc(PATH_MAX
+1);
96 if (!path
) return NULL
;
98 return getcwd(path
, PATH_MAX
);
100 #define GETCWD getcwd_do_malloc
102 #define GETCWD getcwd
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
), "");
116 /* this is more-or-less equivalent to mkdir -p */
118 mkdirs(char *path
, mode_t mode
)
124 if (!path
|| !path
[0])
125 fail("Null pointer or empty string passed to mkdirs()");
126 while (*path
== '/' && path
[1] == '/')
128 while ((cp
= strrchr(path
, '/')) && cp
[1] == '\0')
130 if (cp
&& cp
!= path
) {
132 if ((stat(path
, &sb
) < 0 || !S_ISDIR(sb
.st_mode
)) &&
133 mkdirs(path
, mode
) < 0) {
138 rv
= mkdir(path
, mode
);
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
))
156 if (!owner
|| !owner
[0])
157 fail("Null pointer or empty string passed to touid()");
158 pw
= getpwnam(owner
);
161 uid
= strtol(owner
, &cp
, 0);
162 if (uid
== 0 && cp
== owner
)
163 fail("cannot find uid for %s", owner
);
174 if (!group
|| !group
[0])
175 fail("Null pointer or empty string passed to togid()");
176 gr
= getgrnam(group
);
179 gid
= strtol(group
, &cp
, 0);
180 if (gid
== 0 && cp
== group
)
181 fail("cannot find gid for %s", group
);
185 void * const uninit
= (void *)0xdeadbeef;
188 main(int argc
, char **argv
)
190 char * base
= uninit
;
196 char * linkprefix
= 0;
197 char * name
= uninit
;
199 char * todir
= uninit
;
200 char * toname
= uninit
;
206 int dorelsymlink
= 0;
228 program
= strrchr(argv
[0], '/');
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
) {
236 case 'C': cwd
= optarg
; break;
237 case 'D': onlydir
= 1; break;
238 case 'd': dodir
= 1; break;
239 case 'l': dolink
= 1; break;
242 lplen
= strlen(linkprefix
);
245 case 'R': dolink
= dorelsymlink
= 1; break;
247 mode
= strtoul(optarg
, &cp
, 8);
248 if (mode
== 0 && cp
== optarg
)
251 case 'o': owner
= optarg
; break;
252 case 'g': group
= optarg
; break;
253 case 't': dotimes
= 1; break;
260 if (argc
< 2 - onlydir
)
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
);
272 cwd
= GETCWD(0, PATH_MAX
);
274 fail("could not get CWD");
277 /* make sure we can get into todir. */
279 todir
= GETCWD(0, PATH_MAX
);
281 fail("could not get CWD in todir");
282 tdlen
= strlen(todir
);
284 /* back to original directory. */
287 uid
= owner
? touid(owner
) : -1;
288 gid
= group
? togid(group
) : -1;
293 base
= xbasename(name
);
294 bnlen
= strlen(base
);
295 toname
= (char*)xmalloc(tdlen
+ 1 + bnlen
+ 1);
296 sprintf(toname
, "%s/%s", todir
, base
);
298 exists
= (lstat(toname
, &tosb
) == 0);
301 /* -d means create a directory, always */
302 if (exists
&& !S_ISDIR(tosb
.st_mode
)) {
303 int rv
= unlink(toname
);
305 fail("cannot unlink %s", toname
);
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
);
315 fail("cannot make directory %s", toname
);
317 if ((owner
|| group
) && chown(toname
, uid
, gid
) < 0)
318 fail("cannot change owner of %s", toname
);
321 /* source is absolute pathname, link to it directly */
325 /* -L implies -l and prefixes names with a $cwd arg. */
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
);
334 /* todir is absolute: skip over common prefix. */
335 lplen
= relatepaths(todir
, cwd
, linkname
);
336 strcpy(linkname
+ lplen
, name
);
338 /* todir is named by a relative path: reverse it. */
339 reversepath(todir
, name
, len
, linkname
);
343 len
= strlen(linkname
);
348 /* Check for a pre-existing symlink with identical content. */
350 (!S_ISLNK(tosb
.st_mode
) ||
351 readlink(toname
, buf
, sizeof buf
) != len
||
352 strncmp(buf
, name
, len
) != 0)) {
354 rmrv
= (S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
356 fail("destination exists, cannot remove %s", toname
);
360 if (!exists
&& symlink(name
, toname
) < 0) {
361 if (errno
== EEXIST
) {
362 fprintf(stderr
, "symlink creation race: %s\n", toname
);
365 diagnosePath(toname
);
366 fail("cannot make symbolic link %s", toname
);
369 if ((owner
|| group
) && lchown(toname
, uid
, gid
) < 0)
370 fail("cannot change owner of %s", toname
);
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
);
383 (!S_ISREG(tosb
.st_mode
) || access(toname
, W_OK
) < 0)) {
385 rmrv
= (S_ISDIR(tosb
.st_mode
) ? rmdir
: unlink
)(toname
);
387 fail("destination exists, cannot remove %s", toname
);
390 tofd
= open(toname
, O_CREAT
| O_WRONLY
, 0666);
392 fail("cannot create %s", toname
);
395 while ((cc
= read(fromfd
, bp
, sizeof buf
)) > 0) {
396 while ((wc
= write(tofd
, bp
, cc
)) > 0) {
402 fail("cannot write to %s", toname
);
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.
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
);
421 if (fchmod(tofd
, mode
) < 0)
423 if (chmod(toname
, mode
) < 0)
425 fail("cannot change mode of %s", toname
);
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. */
432 fail("close reports write error on %s", toname
);
435 if (chmod(toname
, mode
) < 0)
436 fail("cannot change mode of %s", toname
);
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
);