No Bug, mozilla-central repo-update HSTS HPKP remote-settings tld-suffixes mobile...
[gecko.git] / config / nsinstall.c
blob675c733dba68b8ce5b196d81b84e47eb2321feb3
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 <errno.h>
14 #include <dirent.h>
15 #include <limits.h>
16 #include <grp.h>
17 #include <pwd.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <utime.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include "pathsub.h"
27 #ifdef HAVE_GETOPT_H
28 # include <getopt.h>
29 #endif
31 #ifdef SUNOS4
32 # include "sunos4.h"
33 #endif
35 #ifdef NEXTSTEP
36 # include <bsd/libc.h>
37 #endif
39 #ifdef __QNX__
40 # include <unix.h>
41 #endif
43 #ifdef NEED_S_ISLNK
44 # if !defined(S_ISLNK) && defined(S_IFLNK)
45 # define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
46 # endif
47 #endif
49 #ifndef _DIRECTORY_SEPARATOR
50 # define _DIRECTORY_SEPARATOR "/"
51 #endif /* _DIRECTORY_SEPARATOR */
53 #ifdef NEED_FCHMOD_PROTO
54 extern int fchmod(int fildes, mode_t mode);
55 #endif
57 static void usage(void) {
58 fprintf(stderr,
59 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
60 " %*s [-DdltR] file [file ...] directory\n",
61 program, (int)strlen(program), "");
62 exit(2);
65 static int mkdirs(char* path, mode_t mode) {
66 char* cp;
67 struct stat sb;
68 int res;
69 int l;
71 /* strip trailing "/." */
72 l = strlen(path);
73 if (l > 1 && path[l - 1] == '.' && path[l - 2] == '/') path[l - 2] = 0;
75 while (*path == '/' && path[1] == '/') path++;
76 for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--);
77 if (cp && cp != path) {
78 *cp = '\0';
79 if ((lstat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
80 mkdirs(path, mode) < 0) {
81 return -1;
83 *cp = '/';
86 res = mkdir(path, mode);
87 if ((res != 0) && (errno == EEXIST))
88 return 0;
89 else
90 return res;
93 static uid_t touid(char* owner) {
94 struct passwd* pw;
95 uid_t uid;
96 char* cp;
98 pw = getpwnam(owner);
99 if (pw) return pw->pw_uid;
100 uid = strtol(owner, &cp, 0);
101 if (uid == 0 && cp == owner) fail("cannot find uid for %s", owner);
102 return uid;
105 static gid_t togid(char* group) {
106 struct group* gr;
107 gid_t gid;
108 char* cp;
110 gr = getgrnam(group);
111 if (gr) return gr->gr_gid;
112 gid = strtol(group, &cp, 0);
113 if (gid == 0 && cp == group) fail("cannot find gid for %s", group);
114 return gid;
117 static void copyfile(char* name, char* toname, mode_t mode, char* group,
118 char* owner, int dotimes, uid_t uid, gid_t gid) {
119 int fromfd, tofd = -1, cc, wc, exists;
120 char buf[BUFSIZ], *bp;
121 struct stat sb, tosb;
122 struct utimbuf utb;
124 exists = (lstat(toname, &tosb) == 0);
126 fromfd = open(name, O_RDONLY);
127 if (fromfd < 0 || fstat(fromfd, &sb) < 0) fail("cannot access %s", name);
128 if (exists) {
129 if (S_ISREG(tosb.st_mode)) {
130 /* See if we can open it. This is more reliable than 'access'. */
131 tofd = open(toname, O_CREAT | O_WRONLY, 0666);
133 if (tofd < 0) {
134 (void)(S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
137 if (tofd < 0) {
138 tofd = open(toname, O_CREAT | O_WRONLY, 0666);
139 if (tofd < 0) fail("cannot create %s", toname);
142 bp = buf;
143 while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
144 while ((wc = write(tofd, bp, (unsigned int)cc)) > 0) {
145 if ((cc -= wc) == 0) break;
146 bp += wc;
148 if (wc < 0) fail("cannot write to %s", toname);
150 if (cc < 0) fail("cannot read from %s", name);
152 if (ftruncate(tofd, sb.st_size) < 0) fail("cannot truncate %s", toname);
153 #if !defined(VMS)
154 if (dotimes) {
155 utb.actime = sb.st_atime;
156 utb.modtime = sb.st_mtime;
157 if (utime(toname, &utb) < 0) fail("cannot set times of %s", toname);
159 # ifdef HAVE_FCHMOD
160 if (fchmod(tofd, mode) < 0)
161 # else
162 if (chmod(toname, mode) < 0)
163 # endif
164 fail("cannot change mode of %s", toname);
165 #endif
166 if ((owner || group) && fchown(tofd, uid, gid) < 0)
167 fail("cannot change owner of %s", toname);
169 /* Must check for delayed (NFS) write errors on close. */
170 if (close(tofd) < 0) fail("cannot write to %s", toname);
171 close(fromfd);
172 #if defined(VMS)
173 if (chmod(toname, (mode & (S_IREAD | S_IWRITE))) < 0)
174 fail("cannot change mode of %s", toname);
175 if (dotimes) {
176 utb.actime = sb.st_atime;
177 utb.modtime = sb.st_mtime;
178 if (utime(toname, &utb) < 0) fail("cannot set times of %s", toname);
180 #endif
183 static void copydir(char* from, char* to, mode_t mode, char* group, char* owner,
184 int dotimes, uid_t uid, gid_t gid) {
185 DIR* dir;
186 struct dirent* ep;
187 struct stat sb;
188 char *base, *destdir, *direntry, *destentry;
190 base = xbasename(from);
192 /* create destination directory */
193 destdir = xmalloc((unsigned int)(strlen(to) + 1 + strlen(base) + 1));
194 sprintf(destdir, "%s%s%s", to, _DIRECTORY_SEPARATOR, base);
195 if (mkdirs(destdir, mode) != 0) {
196 fail("cannot make directory %s\n", destdir);
197 free(destdir);
198 return;
201 if (!(dir = opendir(from))) {
202 fail("cannot open directory %s\n", from);
203 free(destdir);
204 return;
207 direntry = xmalloc((unsigned int)PATH_MAX);
208 destentry = xmalloc((unsigned int)PATH_MAX);
210 while ((ep = readdir(dir))) {
211 if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0) continue;
213 sprintf(direntry, "%s/%s", from, ep->d_name);
214 sprintf(destentry, "%s%s%s", destdir, _DIRECTORY_SEPARATOR, ep->d_name);
216 if (stat(direntry, &sb) == 0 && S_ISDIR(sb.st_mode))
217 copydir(direntry, destdir, mode, group, owner, dotimes, uid, gid);
218 else
219 copyfile(direntry, destentry, mode, group, owner, dotimes, uid, gid);
222 free(destdir);
223 free(direntry);
224 free(destentry);
225 closedir(dir);
228 int main(int argc, char** argv) {
229 int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen,
230 bnlen, exists;
231 mode_t mode = 0755;
232 char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base,
233 *linkname, buf[BUFSIZ];
234 uid_t uid;
235 gid_t gid;
236 struct stat sb, tosb, fromsb;
238 program = argv[0];
239 cwd = linkname = linkprefix = owner = group = 0;
240 onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
242 while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
243 switch (opt) {
244 case 'C':
245 cwd = optarg;
246 break;
247 case 'D':
248 onlydir = 1;
249 break;
250 case 'd':
251 dodir = 1;
252 break;
253 case 'L':
254 linkprefix = optarg;
255 lplen = strlen(linkprefix);
256 dolink = 1;
257 break;
258 case 'R':
259 dolink = dorelsymlink = 1;
260 break;
261 case 'm':
262 mode = strtoul(optarg, &cp, 8);
263 if (mode == 0 && cp == optarg) usage();
264 break;
265 case 'o':
266 owner = optarg;
267 break;
268 case 'g':
269 group = optarg;
270 break;
271 case 't':
272 dotimes = 1;
273 break;
274 default:
275 usage();
279 argc -= optind;
280 argv += optind;
281 if (argc < 2 - onlydir) usage();
283 todir = argv[argc - 1];
284 if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
285 mkdirs(todir, 0777) < 0) {
286 fail("cannot make directory %s", todir);
288 if (onlydir) return 0;
290 if (!cwd) {
291 #ifndef NEEDS_GETCWD
292 # ifndef GETCWD_CANT_MALLOC
293 cwd = getcwd(0, PATH_MAX);
294 # else
295 cwd = malloc(PATH_MAX + 1);
296 cwd = getcwd(cwd, PATH_MAX);
297 # endif
298 #else
299 cwd = malloc(PATH_MAX + 1);
300 cwd = getwd(cwd);
301 #endif
304 xchdir(todir);
305 #ifndef NEEDS_GETCWD
306 # ifndef GETCWD_CANT_MALLOC
307 todir = getcwd(0, PATH_MAX);
308 # else
309 todir = malloc(PATH_MAX + 1);
310 todir = getcwd(todir, PATH_MAX);
311 # endif
312 #else
313 todir = malloc(PATH_MAX + 1);
314 todir = getwd(todir);
315 #endif
316 tdlen = strlen(todir);
317 xchdir(cwd);
318 tdlen = strlen(todir);
320 uid = owner ? touid(owner) : (uid_t)(-1);
321 gid = group ? togid(group) : (gid_t)(-1);
323 while (--argc > 0) {
324 name = *argv++;
325 len = strlen(name);
326 base = xbasename(name);
327 bnlen = strlen(base);
328 toname = xmalloc((unsigned int)(tdlen + 1 + bnlen + 1));
329 sprintf(toname, "%s%s%s", todir, _DIRECTORY_SEPARATOR, base);
330 exists = (lstat(toname, &tosb) == 0);
332 if (dodir) {
333 /* -d means create a directory, always */
334 if (exists && !S_ISDIR(tosb.st_mode)) {
335 (void)unlink(toname);
336 exists = 0;
338 if (!exists && mkdir(toname, mode) < 0)
339 fail("cannot make directory %s", toname);
340 if ((owner || group) && chown(toname, uid, gid) < 0)
341 fail("cannot change owner of %s", toname);
342 } else if (dolink) {
343 if (access(name, R_OK) != 0) {
344 fail("cannot access %s", name);
346 if (*name == '/') {
347 /* source is absolute pathname, link to it directly */
348 linkname = 0;
349 } else {
350 if (linkprefix) {
351 /* -L prefixes names with a $cwd arg. */
352 len += lplen + 1;
353 linkname = xmalloc((unsigned int)(len + 1));
354 sprintf(linkname, "%s/%s", linkprefix, name);
355 } else if (dorelsymlink) {
356 /* Symlink the relative path from todir to source name. */
357 linkname = xmalloc(PATH_MAX);
359 if (*todir == '/') {
360 /* todir is absolute: skip over common prefix. */
361 lplen = relatepaths(todir, cwd, linkname);
362 strcpy(linkname + lplen, name);
363 } else {
364 /* todir is named by a relative path: reverse it. */
365 reversepath(todir, name, len, linkname);
366 xchdir(cwd);
369 len = strlen(linkname);
371 name = linkname;
374 /* Check for a pre-existing symlink with identical content. */
375 if (exists &&
376 (!S_ISLNK(tosb.st_mode) || readlink(toname, buf, sizeof buf) != len ||
377 strncmp(buf, name, (unsigned int)len) != 0 ||
378 ((stat(name, &fromsb) == 0) && (fromsb.st_mtime > tosb.st_mtime)))) {
379 (void)(S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
380 exists = 0;
382 if (!exists && symlink(name, toname) < 0)
383 fail("cannot make symbolic link %s", toname);
384 #ifdef HAVE_LCHOWN
385 if ((owner || group) && lchown(toname, uid, gid) < 0)
386 fail("cannot change owner of %s", toname);
387 #endif
389 if (linkname) {
390 free(linkname);
391 linkname = 0;
393 } else {
394 /* Copy from name to toname, which might be the same file. */
395 if (stat(name, &sb) == 0 && S_IFDIR & sb.st_mode) {
396 /* then is directory: must explicitly create destination dir */
397 /* and manually copy files over */
398 copydir(name, todir, mode, group, owner, dotimes, uid, gid);
399 } else {
400 copyfile(name, toname, mode, group, owner, dotimes, uid, gid);
404 free(toname);
407 free(cwd);
408 free(todir);
409 return 0;