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 mozilla.org code.
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
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 of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or 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 ** Netscape portable install command.
00039 **
00040 ** Brendan Eich, 7/20/95
00041 */
00042 #include <stdio.h>  /* OSF/1 requires this before grp.h, so put it first */
00043 #include <assert.h>
00044 #include <fcntl.h>
00045 #include <errno.h>
00046 #include <dirent.h>
00047 #include <limits.h>
00048 #include <grp.h>
00049 #include <pwd.h>
00050 #include <stdio.h>
00051 #include <stdlib.h>
00052 #include <string.h>
00053 #include <unistd.h>
00054 #include <utime.h>
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 #include "pathsub.h"
00058 
00059 #ifdef HAVE_GETOPT_H
00060 #include <getopt.h>
00061 #endif
00062 
00063 #ifdef SUNOS4
00064 #include "sunos4.h"
00065 #endif
00066 
00067 #ifdef NEXTSTEP
00068 #include <bsd/libc.h>
00069 #endif
00070 
00071 #ifdef __QNX__
00072 #include <unix.h>
00073 #endif
00074 
00075 #ifdef NEED_S_ISLNK
00076 #if !defined(S_ISLNK) && defined(S_IFLNK)
00077 #define S_ISLNK(a)   (((a) & S_IFMT) == S_IFLNK)
00078 #endif
00079 #endif
00080 
00081 #ifndef _DIRECTORY_SEPARATOR
00082 #define _DIRECTORY_SEPARATOR "/"
00083 #endif /* _DIRECTORY_SEPARATOR */
00084 
00085 #ifdef NEED_FCHMOD_PROTO
00086 extern int fchmod(int fildes, mode_t mode);
00087 #endif
00088 
00089 static void
00090 usage(void)
00091 {
00092     fprintf(stderr,
00093        "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
00094        "       %*s [-DdltR] file [file ...] directory\n",
00095        program, (int) strlen(program), "");
00096     exit(2);
00097 }
00098 
00099 static int
00100 mkdirs(char *path, mode_t mode)
00101 {
00102     char *cp;
00103     struct stat sb;
00104     int res;
00105     int l;
00106 
00107     /* strip trailing "/." */
00108     l = strlen(path);
00109     if(l > 1 && path[l - 1] == '.' && path[l - 2] == '/')
00110         path[l - 2] = 0;
00111 
00112     while (*path == '/' && path[1] == '/')
00113        path++;
00114     while ((cp = strrchr(path, '/')) && cp[1] == '\0')
00115        *cp = '\0';
00116     if (cp && cp != path) {
00117        *cp = '\0';
00118        if ((lstat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00119            mkdirs(path, mode) < 0) {
00120            return -1;
00121        }
00122        *cp = '/';
00123     }
00124     
00125     res = mkdir(path, mode);
00126     if ((res != 0) && (errno == EEXIST))
00127       return 0;
00128     else
00129       return res;
00130 }
00131 
00132 static uid_t
00133 touid(char *owner)
00134 {
00135     struct passwd *pw;
00136     uid_t uid;
00137     char *cp;
00138 
00139     pw = getpwnam(owner);
00140     if (pw)
00141        return pw->pw_uid;
00142     uid = strtol(owner, &cp, 0);
00143     if (uid == 0 && cp == owner)
00144        fail("cannot find uid for %s", owner);
00145     return uid;
00146 }
00147 
00148 static gid_t
00149 togid(char *group)
00150 {
00151     struct group *gr;
00152     gid_t gid;
00153     char *cp;
00154 
00155     gr = getgrnam(group);
00156     if (gr)
00157        return gr->gr_gid;
00158     gid = strtol(group, &cp, 0);
00159     if (gid == 0 && cp == group)
00160        fail("cannot find gid for %s", group);
00161     return gid;
00162 }
00163 
00164 static void
00165 copyfile( char *name, char *toname, mode_t mode, char *group, char *owner,
00166           int dotimes, uid_t uid, gid_t gid )
00167 {
00168   int fromfd, tofd, cc, wc, exists;
00169   char buf[BUFSIZ], *bp;
00170   struct stat sb, tosb;
00171   struct utimbuf utb;
00172 
00173   exists = (lstat(toname, &tosb) == 0);
00174 
00175   fromfd = open(name, O_RDONLY);
00176   if (fromfd < 0 || fstat(fromfd, &sb) < 0)
00177     fail("cannot access %s", name);
00178   if (exists && (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0))
00179     (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00180   tofd = open(toname, O_CREAT | O_WRONLY, 0666);
00181   if (tofd < 0)
00182     fail("cannot create %s", toname);
00183 
00184   bp = buf;
00185   while ((cc = read(fromfd, bp, sizeof buf)) > 0)
00186   {
00187     while ((wc = write(tofd, bp, (unsigned int)cc)) > 0)
00188     {
00189       if ((cc -= wc) == 0)
00190         break;
00191       bp += wc;
00192     }
00193     if (wc < 0)
00194       fail("cannot write to %s", toname);
00195   }
00196   if (cc < 0)
00197     fail("cannot read from %s", name);
00198 
00199   if (ftruncate(tofd, sb.st_size) < 0)
00200     fail("cannot truncate %s", toname);
00201 #if !defined(VMS)
00202   if (dotimes)
00203   {
00204     utb.actime = sb.st_atime;
00205     utb.modtime = sb.st_mtime;
00206     if (utime(toname, &utb) < 0)
00207       fail("cannot set times of %s", toname);
00208   }
00209 #ifdef HAVE_FCHMOD
00210   if (fchmod(tofd, mode) < 0)
00211 #else
00212   if (chmod(toname, mode) < 0)
00213 #endif
00214     fail("cannot change mode of %s", toname);
00215 #endif
00216   if ((owner || group) && fchown(tofd, uid, gid) < 0)
00217     fail("cannot change owner of %s", toname);
00218 
00219   /* Must check for delayed (NFS) write errors on close. */
00220   if (close(tofd) < 0)
00221     fail("cannot write to %s", toname);
00222   close(fromfd);
00223 #if defined(VMS)
00224   if (chmod(toname, (mode & (S_IREAD | S_IWRITE))) < 0)
00225     fail("cannot change mode of %s", toname);
00226   if (dotimes)
00227   {
00228     utb.actime = sb.st_atime;
00229     utb.modtime = sb.st_mtime;
00230     if (utime(toname, &utb) < 0)
00231       fail("cannot set times of %s", toname);
00232   }
00233 #endif
00234 }
00235 
00236 static void
00237 copydir( char *from, char *to, mode_t mode, char *group, char *owner,
00238          int dotimes, uid_t uid, gid_t gid)
00239 {
00240   int i;
00241   DIR *dir;
00242   struct dirent *ep;
00243   struct stat sb;
00244   char *base, *destdir, *direntry, *destentry;
00245 
00246   base = xbasename(from);
00247 
00248   /* create destination directory */
00249   destdir = xmalloc((unsigned int)(strlen(to) + 1 + strlen(base) + 1));
00250   sprintf(destdir, "%s%s%s", to, _DIRECTORY_SEPARATOR, base);
00251   if (mkdirs(destdir, mode) != 0) {
00252     fail("cannot make directory %s\n", destdir);
00253     return;
00254   }
00255 
00256   dir = opendir(from);
00257 
00258   direntry = xmalloc((unsigned int)PATH_MAX);
00259   destentry = xmalloc((unsigned int)PATH_MAX);
00260 
00261   while ((ep = readdir(dir)))
00262   {
00263     if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
00264       continue;
00265 
00266     sprintf(direntry, "%s/%s", from, ep->d_name);
00267     sprintf(destentry, "%s%s%s", destdir, _DIRECTORY_SEPARATOR, ep->d_name);
00268 
00269     if (stat(direntry, &sb) == 0 && S_ISDIR(sb.st_mode))
00270       copydir( direntry, destdir, mode, group, owner, dotimes, uid, gid );
00271     else
00272       copyfile( direntry, destentry, mode, group, owner, dotimes, uid, gid );
00273   }
00274 
00275   free(direntry);
00276   free(destentry);
00277   closedir(dir);
00278 }
00279 
00280 int
00281 main(int argc, char **argv)
00282 {
00283     int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
00284     mode_t mode = 0755;
00285     char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
00286     uid_t uid;
00287     gid_t gid;
00288     struct stat sb, tosb, fromsb;
00289     struct utimbuf utb;
00290 
00291     program = argv[0];
00292     cwd = linkname = linkprefix = owner = group = 0;
00293     onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
00294 
00295     while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
00296        switch (opt) {
00297          case 'C':
00298            cwd = optarg;
00299            break;
00300          case 'D':
00301            onlydir = 1;
00302            break;
00303          case 'd':
00304            dodir = 1;
00305            break;
00306          case 'l':
00307            dolink = 1;
00308            break;
00309          case 'L':
00310            linkprefix = optarg;
00311            lplen = strlen(linkprefix);
00312            dolink = 1;
00313            break;
00314      case 'R':
00315            dolink = dorelsymlink = 1;
00316            break;
00317          case 'm':
00318            mode = strtoul(optarg, &cp, 8);
00319            if (mode == 0 && cp == optarg)
00320               usage();
00321            break;
00322          case 'o':
00323            owner = optarg;
00324            break;
00325          case 'g':
00326            group = optarg;
00327            break;
00328          case 't':
00329            dotimes = 1;
00330            break;
00331          default:
00332            usage();
00333        }
00334     }
00335 
00336     argc -= optind;
00337     argv += optind;
00338     if (argc < 2 - onlydir)
00339        usage();
00340 
00341     todir = argv[argc-1];
00342     if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00343        mkdirs(todir, 0777) < 0) {
00344        fail("cannot make directory %s", todir);
00345     }
00346     if (onlydir)
00347        return 0;
00348 
00349     if (!cwd) {
00350 #ifndef NEEDS_GETCWD
00351 #ifndef GETCWD_CANT_MALLOC
00352        cwd = getcwd(0, PATH_MAX);
00353 #else
00354        cwd = malloc(PATH_MAX + 1);
00355        cwd = getcwd(cwd, PATH_MAX);
00356 #endif
00357 #else
00358        cwd = malloc(PATH_MAX + 1);
00359        cwd = getwd(cwd);
00360 #endif
00361     }
00362 
00363     xchdir(todir);
00364 #ifndef NEEDS_GETCWD
00365 #ifndef GETCWD_CANT_MALLOC
00366     todir = getcwd(0, PATH_MAX);
00367 #else
00368     todir = malloc(PATH_MAX + 1);
00369     todir = getcwd(todir, PATH_MAX);
00370 #endif
00371 #else
00372     todir = malloc(PATH_MAX + 1);
00373     todir = getwd(todir);
00374 #endif
00375     tdlen = strlen(todir);
00376     xchdir(cwd);
00377     tdlen = strlen(todir);
00378 
00379     uid = owner ? touid(owner) : (uid_t)(-1);
00380     gid = group ? togid(group) : (gid_t)(-1);
00381 
00382     while (--argc > 0) {
00383        name = *argv++;
00384        len = strlen(name);
00385        base = xbasename(name);
00386        bnlen = strlen(base);
00387        toname = xmalloc((unsigned int)(tdlen + 1 + bnlen + 1));
00388        sprintf(toname, "%s%s%s", todir, _DIRECTORY_SEPARATOR, base);
00389        exists = (lstat(toname, &tosb) == 0);
00390 
00391        if (dodir) {
00392            /* -d means create a directory, always */
00393            if (exists && !S_ISDIR(tosb.st_mode)) {
00394               (void) unlink(toname);
00395               exists = 0;
00396            }
00397            if (!exists && mkdir(toname, mode) < 0)
00398               fail("cannot make directory %s", toname);
00399            if ((owner || group) && chown(toname, uid, gid) < 0)
00400               fail("cannot change owner of %s", toname);
00401        } else if (dolink) {
00402             if (access(name, R_OK) != 0) {
00403                 fail("cannot access %s", name);
00404             }
00405            if (*name == '/') {
00406               /* source is absolute pathname, link to it directly */
00407               linkname = 0;
00408            } else {
00409               if (linkprefix) {
00410                   /* -L implies -l and prefixes names with a $cwd arg. */
00411                   len += lplen + 1;
00412                   linkname = xmalloc((unsigned int)(len + 1));
00413                   sprintf(linkname, "%s/%s", linkprefix, name);
00414               } else if (dorelsymlink) {
00415                   /* Symlink the relative path from todir to source name. */
00416                   linkname = xmalloc(PATH_MAX);
00417 
00418                   if (*todir == '/') {
00419                      /* todir is absolute: skip over common prefix. */
00420                      lplen = relatepaths(todir, cwd, linkname);
00421                      strcpy(linkname + lplen, name);
00422                   } else {
00423                      /* todir is named by a relative path: reverse it. */
00424                      reversepath(todir, name, len, linkname);
00425                      xchdir(cwd);
00426                   }
00427 
00428                   len = strlen(linkname);
00429               }
00430               name = linkname;
00431            }
00432 
00433            /* Check for a pre-existing symlink with identical content. */
00434            if ((exists && (!S_ISLNK(tosb.st_mode) ||
00435                                           readlink(toname, buf, sizeof buf) != len ||
00436                                           strncmp(buf, name, (unsigned int)len) != 0)) || 
00437                      ((stat(name, &fromsb) == 0) && 
00438                       (fromsb.st_mtime > tosb.st_mtime))) {
00439               (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00440               exists = 0;
00441            }
00442            if (!exists && symlink(name, toname) < 0)
00443               fail("cannot make symbolic link %s", toname);
00444 #ifdef HAVE_LCHOWN
00445            if ((owner || group) && lchown(toname, uid, gid) < 0)
00446               fail("cannot change owner of %s", toname);
00447 #endif
00448 
00449            if (linkname) {
00450               free(linkname);
00451               linkname = 0;
00452            }
00453        } else {
00454            /* Copy from name to toname, which might be the same file. */
00455       if( stat(name, &sb) == 0 && S_IFDIR & sb.st_mode )
00456       {
00457         /* then is directory: must explicitly create destination dir  */
00458         /*  and manually copy files over                              */
00459         copydir( name, todir, mode, group, owner, dotimes, uid, gid );
00460       } 
00461       else
00462       {
00463         copyfile(name, toname, mode, group, owner, dotimes, uid, gid);
00464       }
00465     }
00466 
00467        free(toname);
00468     }
00469 
00470     free(cwd);
00471     free(todir);
00472     return 0;
00473 }