Back to index

lightning-sunbird  0.9+nobinonly
nsinstall.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038 ** Netscape portable install command.
00039 */
00040 #include <stdio.h>  /* OSF/1 requires this before grp.h, so put it first */
00041 #include <assert.h>
00042 #include <fcntl.h>
00043 #include <string.h>
00044 #if defined(_WINDOWS)
00045 #include <windows.h>
00046 typedef unsigned int mode_t;
00047 #else
00048 #include <grp.h>
00049 #include <pwd.h>
00050 #include <errno.h>
00051 #include <stdlib.h>
00052 #include <unistd.h>
00053 #include <utime.h>
00054 #endif
00055 #include <sys/types.h>
00056 #include <sys/stat.h>
00057 #include "pathsub.h"
00058 
00059 #define HAVE_LCHOWN
00060 
00061 #if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) || defined(VMS) || defined(NTO) || defined(DARWIN) || defined(BEOS)
00062 #undef HAVE_LCHOWN
00063 #endif
00064 
00065 #define HAVE_FCHMOD
00066 
00067 #if defined(BEOS)
00068 #undef HAVE_FCHMOD
00069 #endif
00070 
00071 #ifdef LINUX
00072 #include <getopt.h>
00073 #endif
00074 
00075 #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC)
00076 #if !defined(S_ISLNK) && defined(S_IFLNK)
00077 #define S_ISLNK(a)   (((a) & S_IFMT) == S_IFLNK)
00078 #endif
00079 #endif
00080 
00081 #if defined(SNI)
00082 extern int fchmod(int fildes, mode_t mode);
00083 #endif
00084 
00085 
00086 #ifdef GETCWD_CANT_MALLOC
00087 /*
00088  * this should probably go into a utility library in case other applications
00089  * need it.
00090  */
00091 static char *
00092 getcwd_do_malloc(char *path, int len) {
00093 
00094     if (!path) {
00095        path = malloc(PATH_MAX +1);
00096        if (!path) return NULL;
00097     }
00098     return getcwd(path, PATH_MAX);
00099 }
00100 #define GETCWD       getcwd_do_malloc
00101 #else
00102 #define GETCWD       getcwd
00103 #endif
00104 
00105 
00106 static void
00107 usage(void)
00108 {
00109     fprintf(stderr,
00110        "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
00111        "       %*s [-DdltR] file [file ...] directory\n",
00112        program, (int)strlen(program), "");
00113     exit(2);
00114 }
00115 
00116 /* this is more-or-less equivalent to mkdir -p */
00117 static int
00118 mkdirs(char *path, mode_t mode)
00119 {
00120     char *      cp;
00121     int         rv;
00122     struct stat sb;
00123     
00124     if (!path || !path[0]) 
00125        fail("Null pointer or empty string passed to mkdirs()");
00126     while (*path == '/' && path[1] == '/')
00127        path++;
00128     while ((cp = strrchr(path, '/')) && cp[1] == '\0')
00129        *cp = '\0';
00130     if (cp && cp != path) {
00131        *cp = '\0';
00132        if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00133            mkdirs(path, mode) < 0) {
00134            return -1;
00135        }
00136        *cp = '/';
00137     }
00138     rv = mkdir(path, mode);
00139     if (rv) {
00140        if (errno != EEXIST)
00141            fail("mkdirs cannot make %s", path);
00142        fprintf(stderr, "directory creation race: %s\n", path);
00143        if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) 
00144            rv = 0;
00145     }
00146     return rv;
00147 }
00148 
00149 static uid_t
00150 touid(char *owner)
00151 {
00152     struct passwd *pw;
00153     uid_t uid;
00154     char *cp;
00155 
00156     if (!owner || !owner[0]) 
00157        fail("Null pointer or empty string passed to touid()");
00158     pw = getpwnam(owner);
00159     if (pw)
00160        return pw->pw_uid;
00161     uid = strtol(owner, &cp, 0);
00162     if (uid == 0 && cp == owner)
00163        fail("cannot find uid for %s", owner);
00164     return uid;
00165 }
00166 
00167 static gid_t
00168 togid(char *group)
00169 {
00170     struct group *gr;
00171     gid_t gid;
00172     char *cp;
00173 
00174     if (!group || !group[0]) 
00175        fail("Null pointer or empty string passed to togid()");
00176     gr = getgrnam(group);
00177     if (gr)
00178        return gr->gr_gid;
00179     gid = strtol(group, &cp, 0);
00180     if (gid == 0 && cp == group)
00181        fail("cannot find gid for %s", group);
00182     return gid;
00183 }
00184 
00185 void * const uninit = (void *)0xdeadbeef;
00186 
00187 int
00188 main(int argc, char **argv)
00189 {
00190     char *    base          = uninit;
00191     char *    bp            = uninit;
00192     char *    cp            = uninit;
00193     char *    cwd           = 0;
00194     char *    group         = 0;
00195     char *    linkname      = 0;
00196     char *    linkprefix    = 0;
00197     char *    name          = uninit;
00198     char *    owner         = 0;
00199     char *    todir         = uninit;
00200     char *    toname        = uninit;
00201 
00202     int       bnlen         = -1;
00203     int       cc            = 0;
00204     int       dodir         = 0;
00205     int       dolink        = 0;
00206     int       dorelsymlink  = 0;
00207     int       dotimes              = 0;
00208     int       exists        = 0;
00209     int       fromfd        = -1;
00210     int       len           = -1;
00211     int       lplen         = 0;
00212     int              onlydir              = 0;
00213     int       opt           = -1;
00214     int       tdlen         = -1;
00215     int       tofd          = -1;
00216     int       wc            = -1;
00217 
00218     mode_t    mode          = 0755;
00219 
00220     uid_t     uid           = -1;
00221     gid_t     gid           = -1;
00222 
00223     struct stat sb;
00224     struct stat tosb;
00225     struct utimbuf utb;
00226     char      buf[BUFSIZ];
00227 
00228     program = strrchr(argv[0], '/');
00229     if (!program)
00230        program = strrchr(argv[0], '\\');
00231     program = program ? program+1 : argv[0];
00232 
00233 
00234     while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
00235        switch (opt) {
00236          case 'C': cwd = optarg;   break;
00237          case 'D': onlydir = 1;    break;
00238          case 'd': dodir = 1;             break;
00239          case 'l': dolink = 1;            break;
00240          case 'L':
00241            linkprefix = optarg;
00242            lplen = strlen(linkprefix);
00243            dolink = 1;
00244            break;
00245          case 'R': dolink = dorelsymlink = 1; break;
00246          case 'm':
00247            mode = strtoul(optarg, &cp, 8);
00248            if (mode == 0 && cp == optarg)
00249               usage();
00250            break;
00251          case 'o': owner = optarg;        break;
00252          case 'g': group = optarg;        break;
00253          case 't': dotimes = 1;    break;
00254          default:  usage();
00255        }
00256     }
00257 
00258     argc -= optind;
00259     argv += optind;
00260     if (argc < 2 - onlydir)
00261        usage();
00262 
00263     todir = argv[argc-1];
00264     if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00265        mkdirs(todir, 0777) < 0) {
00266        fail("cannot mkdir -p %s", todir);
00267     }
00268     if (onlydir)
00269        return 0;
00270 
00271     if (!cwd) {
00272        cwd = GETCWD(0, PATH_MAX);
00273        if (!cwd)
00274            fail("could not get CWD");
00275     }
00276 
00277     /* make sure we can get into todir. */
00278     xchdir(todir);
00279     todir = GETCWD(0, PATH_MAX);
00280     if (!todir)
00281        fail("could not get CWD in todir");
00282     tdlen = strlen(todir);
00283 
00284     /* back to original directory. */
00285     xchdir(cwd);
00286 
00287     uid = owner ? touid(owner) : -1;
00288     gid = group ? togid(group) : -1;
00289 
00290     while (--argc > 0) {
00291        name   = *argv++;
00292        len    = strlen(name);
00293        base   = xbasename(name);
00294        bnlen  = strlen(base);
00295        toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
00296        sprintf(toname, "%s/%s", todir, base);
00297 retry:
00298        exists = (lstat(toname, &tosb) == 0);
00299 
00300        if (dodir) {
00301            /* -d means create a directory, always */
00302            if (exists && !S_ISDIR(tosb.st_mode)) {
00303               int rv = unlink(toname);
00304               if (rv)
00305                   fail("cannot unlink %s", toname);
00306               exists = 0;
00307            }
00308            if (!exists && mkdir(toname, mode) < 0) {
00309               /* we probably have two nsinstall programs in a race here. */
00310               if (errno == EEXIST && !stat(toname, &sb) && 
00311                   S_ISDIR(sb.st_mode)) {
00312                   fprintf(stderr, "directory creation race: %s\n", toname);
00313                   goto retry;
00314               }
00315               fail("cannot make directory %s", toname);
00316            }
00317            if ((owner || group) && chown(toname, uid, gid) < 0)
00318               fail("cannot change owner of %s", toname);
00319        } else if (dolink) {
00320            if (*name == '/') {
00321               /* source is absolute pathname, link to it directly */
00322               linkname = 0;
00323            } else {
00324               if (linkprefix) {
00325                   /* -L implies -l and prefixes names with a $cwd arg. */
00326                   len += lplen + 1;
00327                   linkname = (char*)xmalloc(len + 1);
00328                   sprintf(linkname, "%s/%s", linkprefix, name);
00329               } else if (dorelsymlink) {
00330                   /* Symlink the relative path from todir to source name. */
00331                   linkname = (char*)xmalloc(PATH_MAX);
00332 
00333                   if (*todir == '/') {
00334                      /* todir is absolute: skip over common prefix. */
00335                      lplen = relatepaths(todir, cwd, linkname);
00336                      strcpy(linkname + lplen, name);
00337                   } else {
00338                      /* todir is named by a relative path: reverse it. */
00339                      reversepath(todir, name, len, linkname);
00340                      xchdir(cwd);
00341                   }
00342 
00343                   len = strlen(linkname);
00344               }
00345               name = linkname;
00346            }
00347 
00348            /* Check for a pre-existing symlink with identical content. */
00349            if (exists &&
00350               (!S_ISLNK(tosb.st_mode) ||
00351                readlink(toname, buf, sizeof buf) != len ||
00352                strncmp(buf, name, len) != 0)) {
00353               int rmrv;
00354               rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00355               if (rmrv < 0) {
00356                   fail("destination exists, cannot remove %s", toname);
00357               }
00358               exists = 0;
00359            }
00360            if (!exists && symlink(name, toname) < 0) {
00361               if (errno == EEXIST) {
00362                   fprintf(stderr, "symlink creation race: %s\n", toname);
00363                   goto retry;
00364               }
00365               diagnosePath(toname);
00366               fail("cannot make symbolic link %s", toname);
00367            }
00368 #ifdef HAVE_LCHOWN
00369            if ((owner || group) && lchown(toname, uid, gid) < 0)
00370               fail("cannot change owner of %s", toname);
00371 #endif
00372 
00373            if (linkname) {
00374               free(linkname);
00375               linkname = 0;
00376            }
00377        } else {
00378            /* Copy from name to toname, which might be the same file. */
00379            fromfd = open(name, O_RDONLY);
00380            if (fromfd < 0 || fstat(fromfd, &sb) < 0)
00381               fail("cannot access %s", name);
00382            if (exists && 
00383                (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) {
00384               int rmrv;
00385               rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00386               if (rmrv < 0) {
00387                   fail("destination exists, cannot remove %s", toname);
00388               }
00389            }
00390            tofd = open(toname, O_CREAT | O_WRONLY, 0666);
00391            if (tofd < 0)
00392               fail("cannot create %s", toname);
00393 
00394            bp = buf;
00395            while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
00396               while ((wc = write(tofd, bp, cc)) > 0) {
00397                   if ((cc -= wc) == 0)
00398                      break;
00399                   bp += wc;
00400               }
00401               if (wc < 0)
00402                   fail("cannot write to %s", toname);
00403            }
00404            if (cc < 0)
00405               fail("cannot read from %s", name);
00406 
00407            if (ftruncate(tofd, sb.st_size) < 0)
00408               fail("cannot truncate %s", toname);
00409            /*
00410            ** On OpenVMS we can't chmod() until the file is closed, and we
00411            ** have to utime() last since fchown/chmod alter the timestamps.
00412            */
00413 #ifndef VMS
00414            if (dotimes) {
00415               utb.actime = sb.st_atime;
00416               utb.modtime = sb.st_mtime;
00417               if (utime(toname, &utb) < 0)
00418                   fail("cannot set times of %s", toname);
00419            }
00420 #ifdef HAVE_FCHMOD
00421            if (fchmod(tofd, mode) < 0)
00422 #else
00423            if (chmod(toname, mode) < 0)
00424 #endif
00425               fail("cannot change mode of %s", toname);
00426 #endif
00427            if ((owner || group) && fchown(tofd, uid, gid) < 0)
00428               fail("cannot change owner of %s", toname);
00429 
00430            /* Must check for delayed (NFS) write errors on close. */
00431            if (close(tofd) < 0)
00432               fail("close reports write error on %s", toname);
00433            close(fromfd);
00434 #ifdef VMS
00435            if (chmod(toname, mode) < 0)
00436               fail("cannot change mode of %s", toname);
00437            if (dotimes) {
00438               utb.actime = sb.st_atime;
00439               utb.modtime = sb.st_mtime;
00440               if (utime(toname, &utb) < 0)
00441                   fail("cannot set times of %s", toname);
00442            }
00443 #endif
00444        }
00445 
00446        free(toname);
00447     }
00448 
00449     free(cwd);
00450     free(todir);
00451     return 0;
00452 }
00453