Back to index

enigmail  1.4.3
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     for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--);
00115     if (cp && cp != path) {
00116        *cp = '\0';
00117        if ((lstat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00118            mkdirs(path, mode) < 0) {
00119            return -1;
00120        }
00121        *cp = '/';
00122     }
00123     
00124     res = mkdir(path, mode);
00125     if ((res != 0) && (errno == EEXIST))
00126       return 0;
00127     else
00128       return res;
00129 }
00130 
00131 static uid_t
00132 touid(char *owner)
00133 {
00134     struct passwd *pw;
00135     uid_t uid;
00136     char *cp;
00137 
00138     pw = getpwnam(owner);
00139     if (pw)
00140        return pw->pw_uid;
00141     uid = strtol(owner, &cp, 0);
00142     if (uid == 0 && cp == owner)
00143        fail("cannot find uid for %s", owner);
00144     return uid;
00145 }
00146 
00147 static gid_t
00148 togid(char *group)
00149 {
00150     struct group *gr;
00151     gid_t gid;
00152     char *cp;
00153 
00154     gr = getgrnam(group);
00155     if (gr)
00156        return gr->gr_gid;
00157     gid = strtol(group, &cp, 0);
00158     if (gid == 0 && cp == group)
00159        fail("cannot find gid for %s", group);
00160     return gid;
00161 }
00162 
00163 static void
00164 copyfile( char *name, char *toname, mode_t mode, char *group, char *owner,
00165           int dotimes, uid_t uid, gid_t gid )
00166 {
00167   int fromfd, tofd = -1, cc, wc, exists;
00168   char buf[BUFSIZ], *bp;
00169   struct stat sb, tosb;
00170   struct utimbuf utb;
00171 
00172   exists = (lstat(toname, &tosb) == 0);
00173 
00174   fromfd = open(name, O_RDONLY);
00175   if (fromfd < 0 || fstat(fromfd, &sb) < 0)
00176     fail("cannot access %s", name);
00177   if (exists) {
00178     if (S_ISREG(tosb.st_mode)) {
00179       /* See if we can open it. This is more reliable than 'access'. */
00180       tofd = open(toname, O_CREAT | O_WRONLY, 0666);
00181     }
00182     if (tofd < 0) {
00183       (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00184     }
00185   }
00186   if (tofd < 0) {
00187     tofd = open(toname, O_CREAT | O_WRONLY, 0666);
00188     if (tofd < 0)
00189       fail("cannot create %s", toname);
00190   }
00191 
00192   bp = buf;
00193   while ((cc = read(fromfd, bp, sizeof buf)) > 0)
00194   {
00195     while ((wc = write(tofd, bp, (unsigned int)cc)) > 0)
00196     {
00197       if ((cc -= wc) == 0)
00198         break;
00199       bp += wc;
00200     }
00201     if (wc < 0)
00202       fail("cannot write to %s", toname);
00203   }
00204   if (cc < 0)
00205     fail("cannot read from %s", name);
00206 
00207   if (ftruncate(tofd, sb.st_size) < 0)
00208     fail("cannot truncate %s", toname);
00209 #if !defined(VMS)
00210   if (dotimes)
00211   {
00212     utb.actime = sb.st_atime;
00213     utb.modtime = sb.st_mtime;
00214     if (utime(toname, &utb) < 0)
00215       fail("cannot set times of %s", toname);
00216   }
00217 #ifdef HAVE_FCHMOD
00218   if (fchmod(tofd, mode) < 0)
00219 #else
00220   if (chmod(toname, mode) < 0)
00221 #endif
00222     fail("cannot change mode of %s", toname);
00223 #endif
00224   if ((owner || group) && fchown(tofd, uid, gid) < 0)
00225     fail("cannot change owner of %s", toname);
00226 
00227   /* Must check for delayed (NFS) write errors on close. */
00228   if (close(tofd) < 0)
00229     fail("cannot write to %s", toname);
00230   close(fromfd);
00231 #if defined(VMS)
00232   if (chmod(toname, (mode & (S_IREAD | S_IWRITE))) < 0)
00233     fail("cannot change mode of %s", toname);
00234   if (dotimes)
00235   {
00236     utb.actime = sb.st_atime;
00237     utb.modtime = sb.st_mtime;
00238     if (utime(toname, &utb) < 0)
00239       fail("cannot set times of %s", toname);
00240   }
00241 #endif
00242 }
00243 
00244 static void
00245 copydir( char *from, char *to, mode_t mode, char *group, char *owner,
00246          int dotimes, uid_t uid, gid_t gid)
00247 {
00248   int i;
00249   DIR *dir;
00250   struct dirent *ep;
00251   struct stat sb;
00252   char *base, *destdir, *direntry, *destentry;
00253 
00254   base = xbasename(from);
00255 
00256   /* create destination directory */
00257   destdir = xmalloc((unsigned int)(strlen(to) + 1 + strlen(base) + 1));
00258   sprintf(destdir, "%s%s%s", to, _DIRECTORY_SEPARATOR, base);
00259   if (mkdirs(destdir, mode) != 0) {
00260     fail("cannot make directory %s\n", destdir);
00261     free(destdir);
00262     return;
00263   }
00264 
00265   if (!(dir = opendir(from))) {
00266     fail("cannot open directory %s\n", from);
00267     free(destdir);
00268     return;
00269   }
00270 
00271   direntry = xmalloc((unsigned int)PATH_MAX);
00272   destentry = xmalloc((unsigned int)PATH_MAX);
00273 
00274   while ((ep = readdir(dir)))
00275   {
00276     if (strcmp(ep->d_name, ".") == 0 || strcmp(ep->d_name, "..") == 0)
00277       continue;
00278 
00279     sprintf(direntry, "%s/%s", from, ep->d_name);
00280     sprintf(destentry, "%s%s%s", destdir, _DIRECTORY_SEPARATOR, ep->d_name);
00281 
00282     if (stat(direntry, &sb) == 0 && S_ISDIR(sb.st_mode))
00283       copydir( direntry, destdir, mode, group, owner, dotimes, uid, gid );
00284     else
00285       copyfile( direntry, destentry, mode, group, owner, dotimes, uid, gid );
00286   }
00287 
00288   free(destdir);
00289   free(direntry);
00290   free(destentry);
00291   closedir(dir);
00292 }
00293 
00294 int
00295 main(int argc, char **argv)
00296 {
00297     int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
00298     mode_t mode = 0755;
00299     char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
00300     uid_t uid;
00301     gid_t gid;
00302     struct stat sb, tosb, fromsb;
00303     struct utimbuf utb;
00304 
00305     program = argv[0];
00306     cwd = linkname = linkprefix = owner = group = 0;
00307     onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
00308 
00309     while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
00310        switch (opt) {
00311          case 'C':
00312            cwd = optarg;
00313            break;
00314          case 'D':
00315            onlydir = 1;
00316            break;
00317          case 'd':
00318            dodir = 1;
00319            break;
00320          case 'l':
00321            dolink = 1;
00322            break;
00323          case 'L':
00324            linkprefix = optarg;
00325            lplen = strlen(linkprefix);
00326            dolink = 1;
00327            break;
00328      case 'R':
00329            dolink = dorelsymlink = 1;
00330            break;
00331          case 'm':
00332            mode = strtoul(optarg, &cp, 8);
00333            if (mode == 0 && cp == optarg)
00334               usage();
00335            break;
00336          case 'o':
00337            owner = optarg;
00338            break;
00339          case 'g':
00340            group = optarg;
00341            break;
00342          case 't':
00343            dotimes = 1;
00344            break;
00345          default:
00346            usage();
00347        }
00348     }
00349 
00350     argc -= optind;
00351     argv += optind;
00352     if (argc < 2 - onlydir)
00353        usage();
00354 
00355     todir = argv[argc-1];
00356     if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
00357        mkdirs(todir, 0777) < 0) {
00358        fail("cannot make directory %s", todir);
00359     }
00360     if (onlydir)
00361        return 0;
00362 
00363     if (!cwd) {
00364 #ifndef NEEDS_GETCWD
00365 #ifndef GETCWD_CANT_MALLOC
00366        cwd = getcwd(0, PATH_MAX);
00367 #else
00368        cwd = malloc(PATH_MAX + 1);
00369        cwd = getcwd(cwd, PATH_MAX);
00370 #endif
00371 #else
00372        cwd = malloc(PATH_MAX + 1);
00373        cwd = getwd(cwd);
00374 #endif
00375     }
00376 
00377     xchdir(todir);
00378 #ifndef NEEDS_GETCWD
00379 #ifndef GETCWD_CANT_MALLOC
00380     todir = getcwd(0, PATH_MAX);
00381 #else
00382     todir = malloc(PATH_MAX + 1);
00383     todir = getcwd(todir, PATH_MAX);
00384 #endif
00385 #else
00386     todir = malloc(PATH_MAX + 1);
00387     todir = getwd(todir);
00388 #endif
00389     tdlen = strlen(todir);
00390     xchdir(cwd);
00391     tdlen = strlen(todir);
00392 
00393     uid = owner ? touid(owner) : (uid_t)(-1);
00394     gid = group ? togid(group) : (gid_t)(-1);
00395 
00396     while (--argc > 0) {
00397        name = *argv++;
00398        len = strlen(name);
00399        base = xbasename(name);
00400        bnlen = strlen(base);
00401        toname = xmalloc((unsigned int)(tdlen + 1 + bnlen + 1));
00402        sprintf(toname, "%s%s%s", todir, _DIRECTORY_SEPARATOR, base);
00403        exists = (lstat(toname, &tosb) == 0);
00404 
00405        if (dodir) {
00406            /* -d means create a directory, always */
00407            if (exists && !S_ISDIR(tosb.st_mode)) {
00408               (void) unlink(toname);
00409               exists = 0;
00410            }
00411            if (!exists && mkdir(toname, mode) < 0)
00412               fail("cannot make directory %s", toname);
00413            if ((owner || group) && chown(toname, uid, gid) < 0)
00414               fail("cannot change owner of %s", toname);
00415        } else if (dolink) {
00416             if (access(name, R_OK) != 0) {
00417                 fail("cannot access %s", name);
00418             }
00419            if (*name == '/') {
00420               /* source is absolute pathname, link to it directly */
00421               linkname = 0;
00422            } else {
00423               if (linkprefix) {
00424                   /* -L implies -l and prefixes names with a $cwd arg. */
00425                   len += lplen + 1;
00426                   linkname = xmalloc((unsigned int)(len + 1));
00427                   sprintf(linkname, "%s/%s", linkprefix, name);
00428               } else if (dorelsymlink) {
00429                   /* Symlink the relative path from todir to source name. */
00430                   linkname = xmalloc(PATH_MAX);
00431 
00432                   if (*todir == '/') {
00433                      /* todir is absolute: skip over common prefix. */
00434                      lplen = relatepaths(todir, cwd, linkname);
00435                      strcpy(linkname + lplen, name);
00436                   } else {
00437                      /* todir is named by a relative path: reverse it. */
00438                      reversepath(todir, name, len, linkname);
00439                      xchdir(cwd);
00440                   }
00441 
00442                   len = strlen(linkname);
00443               }
00444               name = linkname;
00445            }
00446 
00447            /* Check for a pre-existing symlink with identical content. */
00448            if ((exists && (!S_ISLNK(tosb.st_mode) ||
00449                                           readlink(toname, buf, sizeof buf) != len ||
00450                                           strncmp(buf, name, (unsigned int)len) != 0)) || 
00451                      ((stat(name, &fromsb) == 0) && 
00452                       (fromsb.st_mtime > tosb.st_mtime))) {
00453               (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
00454               exists = 0;
00455            }
00456            if (!exists && symlink(name, toname) < 0)
00457               fail("cannot make symbolic link %s", toname);
00458 #ifdef HAVE_LCHOWN
00459            if ((owner || group) && lchown(toname, uid, gid) < 0)
00460               fail("cannot change owner of %s", toname);
00461 #endif
00462 
00463            if (linkname) {
00464               free(linkname);
00465               linkname = 0;
00466            }
00467        } else {
00468            /* Copy from name to toname, which might be the same file. */
00469       if( stat(name, &sb) == 0 && S_IFDIR & sb.st_mode )
00470       {
00471         /* then is directory: must explicitly create destination dir  */
00472         /*  and manually copy files over                              */
00473         copydir( name, todir, mode, group, owner, dotimes, uid, gid );
00474       } 
00475       else
00476       {
00477         copyfile(name, toname, mode, group, owner, dotimes, uid, gid);
00478       }
00479     }
00480 
00481        free(toname);
00482     }
00483 
00484     free(cwd);
00485     free(todir);
00486     return 0;
00487 }