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