Back to index

lightning-sunbird  0.9+nobinonly
nsinstall.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039 ** Netscape portable install command.
00040 **
00041 ** Brendan Eich, 7/20/95
00042 */
00043 #include <stdio.h>  /* OSF/1 requires this before grp.h, so put it first */
00044 #include <assert.h>
00045 #include <fcntl.h>
00046 #include <grp.h>
00047 #include <pwd.h>
00048 #include <stdlib.h>
00049 #include <string.h>
00050 #include <unistd.h>
00051 #include <utime.h>
00052 #include <sys/types.h>
00053 #include <sys/stat.h>
00054 #include <dirent.h>
00055 #include <errno.h>
00056 #include <stdarg.h>
00057 #ifdef USE_REENTRANT_LIBC
00058 #include "libc_r.h"
00059 #endif /* USE_REENTRANT_LIBC */
00060 
00061 #include "pathsub.h"
00062 
00063 #ifndef HAVE_LCHOWN
00064 #define HAVE_LCHOWN
00065 #endif
00066 
00067 #if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) \
00068     || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) \
00069     || defined(RHAPSODY) || defined(NEXTSTEP) || defined(QNX) \
00070     || defined(BEOS) || defined(VMS) || defined(DARWIN)
00071 #undef HAVE_LCHOWN
00072 #endif
00073 
00074 #define HAVE_FCHMOD
00075 
00076 #if defined(BEOS)
00077 #undef HAVE_FCHMOD
00078 #endif
00079 
00080 /*
00081  * Does getcwd() take NULL as the first argument and malloc
00082  * the result buffer?
00083  */
00084 #if !defined(RHAPSODY) && !defined(NEXTSTEP) && !defined(VMS)
00085 #define GETCWD_CAN_MALLOC
00086 #endif
00087 
00088 #ifdef NEXTSTEP
00089 #include <bsd/libc.h>
00090 
00091 /*
00092 ** balazs.pataki@sztaki.hu: The getcwd is broken in NEXTSTEP (returns 0),
00093 ** when called on a mounted fs. Did anyone notice this? Here's an ugly
00094 ** workaround ...
00095 */
00096 #define getcwd(b,s)   my_getcwd(b,s)
00097 
00098 static char *
00099 my_getcwd (char *buf, size_t size)
00100 {
00101     FILE *pwd = popen("pwd", "r");
00102     char *result = fgets(buf, size, pwd);
00103 
00104     if (result) {
00105         buf[strlen(buf)-1] = '\0';
00106     }
00107     pclose (pwd);
00108     return buf;
00109 }
00110 #endif /* NEXTSTEP */
00111 
00112 #ifdef LINUX
00113 #include <getopt.h>
00114 #endif
00115 
00116 #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC) || defined(NEXTSTEP)
00117 #if !defined(S_ISLNK) && defined(S_IFLNK)
00118 #define S_ISLNK(a)   (((a) & S_IFMT) == S_IFLNK)
00119 #endif
00120 #endif
00121 
00122 #if defined(SNI)
00123 extern int fchmod(int fildes, mode_t mode);
00124 #endif
00125 
00126 #ifdef QNX
00127 #define d_ino d_stat.st_ino
00128 #endif
00129 
00130 static void
00131 usage(void)
00132 {
00133     fprintf(stderr,
00134        "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
00135        "       %*s [-DdltR] file [file ...] directory\n",
00136        program, (int)strlen(program), "");
00137     exit(2);
00138 }
00139 
00140 static int
00141 mkdirs(char *path, mode_t mode)
00142 {
00143     char *cp;
00144     struct stat sb;
00145     int res;
00146     
00147     while (*path == '/' && path[1] == '/')
00148        path++;
00149     while ((cp = strrchr(path, '/')) && cp[1] == '\0')
00150        *cp = '\0';
00151     if (cp && cp != path) {
00152        *cp = '\0';
00153        if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00154            mkdirs(path, mode) < 0) {
00155            return -1;
00156        }
00157        *cp = '/';
00158     }
00159     res = mkdir(path, mode);
00160     if ((res != 0) && (errno == EEXIST))
00161        return 0;
00162      else
00163        return res;
00164 }
00165 
00166 static uid_t
00167 touid(char *owner)
00168 {
00169     struct passwd *pw;
00170     uid_t uid;
00171     char *cp;
00172 
00173     pw = getpwnam(owner);
00174     if (pw)
00175        return pw->pw_uid;
00176     uid = strtol(owner, &cp, 0);
00177     if (uid == 0 && cp == owner)
00178        fail("cannot find uid for %s", owner);
00179     return uid;
00180 }
00181 
00182 static gid_t
00183 togid(char *group)
00184 {
00185     struct group *gr;
00186     gid_t gid;
00187     char *cp;
00188 
00189     gr = getgrnam(group);
00190     if (gr)
00191        return gr->gr_gid;
00192     gid = strtol(group, &cp, 0);
00193     if (gid == 0 && cp == group)
00194        fail("cannot find gid for %s", group);
00195     return gid;
00196 }
00197 
00198 int
00199 main(int argc, char **argv)
00200 {
00201     int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
00202     mode_t mode = 0755;
00203     char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
00204     uid_t uid;
00205     gid_t gid;
00206     struct stat sb, tosb;
00207     struct utimbuf utb;
00208 
00209     program = argv[0];
00210     cwd = linkname = linkprefix = owner = group = 0;
00211     onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
00212 
00213     while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
00214        switch (opt) {
00215          case 'C':
00216            cwd = optarg;
00217            break;
00218          case 'D':
00219            onlydir = 1;
00220            break;
00221          case 'd':
00222            dodir = 1;
00223            break;
00224          case 'l':
00225            dolink = 1;
00226            break;
00227          case 'L':
00228            linkprefix = optarg;
00229            lplen = strlen(linkprefix);
00230            dolink = 1;
00231            break;
00232          case 'R':
00233            dolink = dorelsymlink = 1;
00234            break;
00235          case 'm':
00236            mode = strtoul(optarg, &cp, 8);
00237            if (mode == 0 && cp == optarg)
00238               usage();
00239            break;
00240          case 'o':
00241            owner = optarg;
00242            break;
00243          case 'g':
00244            group = optarg;
00245            break;
00246          case 't':
00247            dotimes = 1;
00248            break;
00249          default:
00250            usage();
00251        }
00252     }
00253 
00254     argc -= optind;
00255     argv += optind;
00256     if (argc < 2 - onlydir)
00257        usage();
00258 
00259     todir = argv[argc-1];
00260     if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00261        mkdirs(todir, 0777) < 0) {
00262        fail("cannot make directory %s", todir);
00263     }
00264     if (onlydir)
00265        return 0;
00266 
00267     if (!cwd) {
00268 #ifdef GETCWD_CAN_MALLOC
00269        cwd = getcwd(0, PATH_MAX);
00270 #else
00271        cwd = malloc(PATH_MAX + 1);
00272        cwd = getcwd(cwd, PATH_MAX);
00273 #endif
00274     }
00275     xchdir(todir);
00276 #ifdef GETCWD_CAN_MALLOC
00277     todir = getcwd(0, PATH_MAX);
00278 #else
00279     todir = malloc(PATH_MAX + 1);
00280     todir = getcwd(todir, PATH_MAX);
00281 #endif
00282     tdlen = strlen(todir);
00283     xchdir(cwd);
00284     tdlen = strlen(todir);
00285 
00286     uid = owner ? touid(owner) : -1;
00287     gid = group ? togid(group) : -1;
00288 
00289     while (--argc > 0) {
00290        name = *argv++;
00291        len = strlen(name);
00292        base = xbasename(name);
00293        bnlen = strlen(base);
00294        toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
00295        sprintf(toname, "%s/%s", todir, base);
00296        exists = (lstat(toname, &tosb) == 0);
00297 
00298        if (dodir) {
00299            /* -d means create a directory, always */
00300            if (exists && !S_ISDIR(tosb.st_mode)) {
00301               (void) unlink(toname);
00302               exists = 0;
00303            }
00304            if (!exists && mkdir(toname, mode) < 0)
00305               fail("cannot make directory %s", toname);
00306            if ((owner || group) && chown(toname, uid, gid) < 0)
00307               fail("cannot change owner of %s", toname);
00308        } else if (dolink) {
00309            if (*name == '/') {
00310               /* source is absolute pathname, link to it directly */
00311               linkname = 0;
00312            } else {
00313               if (linkprefix) {
00314                   /* -L implies -l and prefixes names with a $cwd arg. */
00315                   len += lplen + 1;
00316                   linkname = (char*)xmalloc(len + 1);
00317                   sprintf(linkname, "%s/%s", linkprefix, name);
00318               } else if (dorelsymlink) {
00319                   /* Symlink the relative path from todir to source name. */
00320                   linkname = (char*)xmalloc(PATH_MAX);
00321 
00322                   if (*todir == '/') {
00323                      /* todir is absolute: skip over common prefix. */
00324                      lplen = relatepaths(todir, cwd, linkname);
00325                      strcpy(linkname + lplen, name);
00326                   } else {
00327                      /* todir is named by a relative path: reverse it. */
00328                      reversepath(todir, name, len, linkname);
00329                      xchdir(cwd);
00330                   }
00331 
00332                   len = strlen(linkname);
00333               }
00334               name = linkname;
00335            }
00336 
00337            /* Check for a pre-existing symlink with identical content. */
00338            if (exists &&
00339               (!S_ISLNK(tosb.st_mode) ||
00340                readlink(toname, buf, sizeof buf) != len ||
00341                strncmp(buf, name, len) != 0)) {
00342               (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00343               exists = 0;
00344            }
00345            if (!exists && symlink(name, toname) < 0)
00346               fail("cannot make symbolic link %s", toname);
00347 #ifdef HAVE_LCHOWN
00348            if ((owner || group) && lchown(toname, uid, gid) < 0)
00349               fail("cannot change owner of %s", toname);
00350 #endif
00351 
00352            if (linkname) {
00353               free(linkname);
00354               linkname = 0;
00355            }
00356        } else {
00357            /* Copy from name to toname, which might be the same file. */
00358            fromfd = open(name, O_RDONLY);
00359            if (fromfd < 0 || fstat(fromfd, &sb) < 0)
00360               fail("cannot access %s", name);
00361            if (exists && (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0))
00362               (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00363            tofd = open(toname, O_CREAT | O_WRONLY, 0666);
00364            if (tofd < 0)
00365               fail("cannot create %s", toname);
00366 
00367            bp = buf;
00368            while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
00369               while ((wc = write(tofd, bp, cc)) > 0) {
00370                   if ((cc -= wc) == 0)
00371                      break;
00372                   bp += wc;
00373               }
00374               if (wc < 0)
00375                   fail("cannot write to %s", toname);
00376            }
00377            if (cc < 0)
00378               fail("cannot read from %s", name);
00379 
00380            if (ftruncate(tofd, sb.st_size) < 0)
00381               fail("cannot truncate %s", toname);
00382            if (dotimes) {
00383               utb.actime = sb.st_atime;
00384               utb.modtime = sb.st_mtime;
00385               if (utime(toname, &utb) < 0)
00386                   fail("cannot set times of %s", toname);
00387            }
00388 #ifdef HAVE_FCHMOD
00389            if (fchmod(tofd, mode) < 0)
00390 #else
00391            if (chmod(toname, mode) < 0)
00392 #endif
00393               fail("cannot change mode of %s", toname);
00394            if ((owner || group) && fchown(tofd, uid, gid) < 0)
00395               fail("cannot change owner of %s", toname);
00396 
00397            /* Must check for delayed (NFS) write errors on close. */
00398            if (close(tofd) < 0)
00399               fail("cannot write to %s", toname);
00400            close(fromfd);
00401        }
00402 
00403        free(toname);
00404     }
00405 
00406     free(cwd);
00407     free(todir);
00408     return 0;
00409 }
00410 
00411 /*
00412 ** Pathname subroutines.
00413 **
00414 ** Brendan Eich, 8/29/95
00415 */
00416 
00417 char *program;
00418 
00419 void
00420 fail(char *format, ...)
00421 {
00422     int error;
00423     va_list ap;
00424 
00425 #ifdef USE_REENTRANT_LIBC
00426     R_STRERROR_INIT_R();
00427 #endif
00428 
00429     error = errno;
00430     fprintf(stderr, "%s: ", program);
00431     va_start(ap, format);
00432     vfprintf(stderr, format, ap);
00433     va_end(ap);
00434     if (error)
00435 
00436 #ifdef USE_REENTRANT_LIBC
00437     R_STRERROR_R(errno);
00438        fprintf(stderr, ": %s", r_strerror_r);
00439 #else
00440        fprintf(stderr, ": %s", strerror(errno));
00441 #endif
00442 
00443     putc('\n', stderr);
00444     exit(1);
00445 }
00446 
00447 char *
00448 getcomponent(char *path, char *name)
00449 {
00450     if (*path == '\0')
00451        return 0;
00452     if (*path == '/') {
00453        *name++ = '/';
00454     } else {
00455        do {
00456            *name++ = *path++;
00457        } while (*path != '/' && *path != '\0');
00458     }
00459     *name = '\0';
00460     while (*path == '/')
00461        path++;
00462     return path;
00463 }
00464 
00465 #ifdef UNIXWARE_READDIR_BUFFER_TOO_SMALL
00466 /* Sigh.  The static buffer in Unixware's readdir is too small. */
00467 struct dirent * readdir(DIR *d)
00468 {
00469         static struct dirent *buf = NULL;
00470 #define MAX_PATH_LEN 1024
00471 
00472 
00473         if(buf == NULL)
00474                 buf = (struct dirent *) malloc(sizeof(struct dirent) + MAX_PATH_LEN)
00475 ;
00476         return(readdir_r(d, buf));
00477 }
00478 #endif
00479 
00480 char *
00481 ino2name(ino_t ino, char *dir)
00482 {
00483     DIR *dp;
00484     struct dirent *ep;
00485     char *name;
00486 
00487     dp = opendir("..");
00488     if (!dp)
00489        fail("cannot read parent directory");
00490     for (;;) {
00491        if (!(ep = readdir(dp)))
00492            fail("cannot find current directory");
00493        if (ep->d_ino == ino)
00494            break;
00495     }
00496     name = xstrdup(ep->d_name);
00497     closedir(dp);
00498     return name;
00499 }
00500 
00501 void *
00502 xmalloc(size_t size)
00503 {
00504     void *p = malloc(size);
00505     if (!p)
00506        fail("cannot allocate %u bytes", size);
00507     return p;
00508 }
00509 
00510 char *
00511 xstrdup(char *s)
00512 {
00513     return strcpy((char*)xmalloc(strlen(s) + 1), s);
00514 }
00515 
00516 char *
00517 xbasename(char *path)
00518 {
00519     char *cp;
00520 
00521     while ((cp = strrchr(path, '/')) && cp[1] == '\0')
00522        *cp = '\0';
00523     if (!cp) return path;
00524     return cp + 1;
00525 }
00526 
00527 void
00528 xchdir(char *dir)
00529 {
00530     if (chdir(dir) < 0)
00531        fail("cannot change directory to %s", dir);
00532 }
00533 
00534 int
00535 relatepaths(char *from, char *to, char *outpath)
00536 {
00537     char *cp, *cp2;
00538     int len;
00539     char buf[NAME_MAX];
00540 
00541     assert(*from == '/' && *to == '/');
00542     for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++)
00543        if (*cp == '\0')
00544            break;
00545     while (cp[-1] != '/')
00546        cp--, cp2--;
00547     if (cp - 1 == to) {
00548        /* closest common ancestor is /, so use full pathname */
00549        len = strlen(strcpy(outpath, to));
00550        if (outpath[len] != '/') {
00551            outpath[len++] = '/';
00552            outpath[len] = '\0';
00553        }
00554     } else {
00555        len = 0;
00556        while ((cp2 = getcomponent(cp2, buf)) != 0) {
00557            strcpy(outpath + len, "../");
00558            len += 3;
00559        }
00560        while ((cp = getcomponent(cp, buf)) != 0) {
00561            sprintf(outpath + len, "%s/", buf);
00562            len += strlen(outpath + len);
00563        }
00564     }
00565     return len;
00566 }
00567 
00568 void
00569 reversepath(char *inpath, char *name, int len, char *outpath)
00570 {
00571     char *cp, *cp2;
00572     char buf[NAME_MAX];
00573     struct stat sb;
00574 
00575     cp = strcpy(outpath + PATH_MAX - (len + 1), name);
00576     cp2 = inpath;
00577     while ((cp2 = getcomponent(cp2, buf)) != 0) {
00578        if (strcmp(buf, ".") == 0)
00579            continue;
00580        if (strcmp(buf, "..") == 0) {
00581            if (stat(".", &sb) < 0)
00582               fail("cannot stat current directory");
00583            name = ino2name(sb.st_ino, "..");
00584            len = strlen(name);
00585            cp -= len + 1;
00586            strcpy(cp, name);
00587            cp[len] = '/';
00588            free(name);
00589            xchdir("..");
00590        } else {
00591            cp -= 3;
00592            strncpy(cp, "../", 3);
00593            xchdir(buf);
00594        }
00595     }
00596     strcpy(outpath, cp);
00597 }