install.c

Go to the documentation of this file.
00001 /*      install 1.11 - install files.                   Author: Kees J. Bot
00002  *                                                              21 Feb 1993
00003  */
00004 #define nil 0
00005 #include <sys/types.h>
00006 #include <sys/stat.h>
00007 #include <sys/wait.h>
00008 #include <stdio.h>
00009 #include <stdlib.h>
00010 #include <unistd.h>
00011 #include <fcntl.h>
00012 #include <string.h>
00013 #include <errno.h>
00014 #include <a.out.h>
00015 #include <limits.h>
00016 #include <pwd.h>
00017 #include <grp.h>
00018 #include <utime.h>
00019 #include <signal.h>
00020 
00021 /* First line used on a self-decompressing executable. */
00022 char ZCAT[]=    "#!/usr/bin/zexec /usr/bin/zcat\n";
00023 char GZCAT[]=   "#!/usr/bin/zexec /usr/bin/gzcat\n";
00024 
00025 /* Compression filters. */
00026 char *COMPRESS[]=       { "compress", nil };
00027 char *GZIP[]=           { "gzip", "-#", nil };
00028 
00029 int excode= 0;          /* Exit code. */
00030 
00031 void report(char *label)
00032 {
00033         if (label == nil || label[0] == 0)
00034                 fprintf(stderr, "install: %s\n", strerror(errno));
00035         else
00036                 fprintf(stderr, "install: %s: %s\n", label, strerror(errno));
00037         excode= 1;
00038 }
00039 
00040 void fatal(char *label)
00041 {
00042         report(label);
00043         exit(1);
00044 }
00045 
00046 void *allocate(void *mem, size_t size)
00047 /* Safe malloc/realloc. */
00048 {
00049         mem= mem == nil ? malloc(size) : realloc(mem, size);
00050 
00051         if (mem == nil) fatal(nil);
00052         return mem;
00053 }
00054 
00055 void deallocate(void *mem)
00056 {
00057         if (mem != nil) free(mem);
00058 }
00059 
00060 int lflag= 0;           /* Make a hard link if possible. */
00061 int cflag= 0;           /* Copy if you can't link, otherwise symlink. */
00062 int dflag= 0;           /* Create a directory. */
00063 int strip= 0;           /* Strip the copy. */
00064 char **compress= nil;   /* Compress utility to make a compressed executable. */
00065 char *zcat= nil;        /* Line one to decompress. */
00066 
00067 long stack= -1;         /* Amount of heap + stack. */
00068 int wordpow= 1;         /* Must be multiplied with wordsize ** wordpow */
00069                         /* So 8kb for an 8086 and 16kb for the rest. */
00070 
00071 pid_t filter(int fd, char **command)
00072 /* Let a command filter the output to fd. */
00073 {
00074         pid_t pid;
00075         int pfd[2];
00076 
00077         if (pipe(pfd) < 0) {
00078                 report("pipe()");
00079                 return -1;
00080         }
00081 
00082         switch ((pid= fork())) {
00083         case -1:
00084                 report("fork()");
00085                 return -1;
00086         case 0:
00087                 /* Run the filter. */
00088                 dup2(pfd[0], 0);
00089                 dup2(fd, 1);
00090                 close(pfd[0]);
00091                 close(pfd[1]);
00092                 close(fd);
00093                 signal(SIGPIPE, SIG_DFL);
00094                 execvp(command[0], command);
00095                 fatal(command[0]);
00096         }
00097         /* Connect fd to the pipe. */
00098         dup2(pfd[1], fd);
00099         close(pfd[0]);
00100         close(pfd[1]);
00101         return pid;
00102 }
00103 
00104 int mkdirp(char *dir, int mode, int owner, int group)
00105 /* mkdir -p dir */
00106 {
00107         int keep;
00108         char *sep, *pref;
00109 
00110         sep= dir;
00111         while (*sep == '/') sep++;
00112         
00113         if (*sep == 0) {
00114                 errno= EINVAL;
00115                 return -1;
00116         }
00117 
00118         do {
00119                 while (*sep != '/' && *sep != 0) sep++;
00120                 pref= sep;
00121                 while (*sep == '/') sep++;
00122 
00123                 keep= *pref; *pref= 0;
00124 
00125                 if (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0) continue;
00126 
00127                 if (mkdir(dir, mode) < 0) {
00128                         if (errno != EEXIST || *sep == 0) {
00129                                 /* On purpose not doing: *pref= keep; */
00130                                 return -1;
00131                         }
00132                 } else {
00133                         if (chown(dir, owner, group) < 0 && errno != EPERM)
00134                                 return -1;
00135                 }
00136         } while (*pref= keep, *sep != 0);
00137         return 0;
00138 }
00139 
00140 void makedir(char *dir, int mode, int owner, int group)
00141 /* Install a directory, and set it's modes. */
00142 {
00143         struct stat st;
00144 
00145         if (stat(dir, &st) < 0) {
00146                 if (errno != ENOENT) { report(dir); return; }
00147 
00148                 /* The target doesn't exist, make it. */
00149                 if (mode == -1) mode= 0755;
00150                 if (owner == -1) owner= getuid();
00151                 if (group == -1) group= getgid();
00152 
00153                 if (mkdirp(dir, mode, owner, group) < 0) {
00154                         report(dir); return;
00155                 }
00156         } else {
00157                 /* The target does exist, change mode and ownership. */
00158                 if (mode == -1) mode= (st.st_mode & 07777) | 0555;
00159 
00160                 if ((st.st_mode & 07777) != mode) {
00161                         if (chmod(dir, mode) < 0) { report(dir); return; }
00162                 }
00163                 if (owner == -1) owner= st.st_uid;
00164                 if (group == -1) group= st.st_gid;
00165                 if (st.st_uid != owner || st.st_gid != group) {
00166                         if (chown(dir, owner, group) < 0 && errno != EPERM) {
00167                                 report(dir); return;
00168                         }
00169                         /* Set the mode again, chown may have wrecked it. */
00170                         (void) chmod(dir, mode);
00171                 }
00172         }
00173 }
00174 
00175 int setstack(struct exec *hdr)
00176 /* Set the stack size in a header.  Return true if something changed. */
00177 {
00178         long total;
00179 
00180         total= stack;
00181         while (wordpow > 0) {
00182                 total *= hdr->a_cpu == A_I8086 ? 2 : 4;
00183                 wordpow--;
00184         }
00185         total+= hdr->a_data + hdr->a_bss;
00186 
00187         if (!(hdr->a_flags & A_SEP)) {
00188                 total+= hdr->a_text;
00189 #ifdef A_PAL
00190                 if (hdr->a_flags & A_PAL) total+= hdr->a_hdrlen;
00191 #endif
00192         }
00193         if (hdr->a_cpu == A_I8086 && total > 0x10000L)
00194                 total= 0x10000L;
00195 
00196         if (hdr->a_total != total) {
00197                 /* Need to change stack allocation. */
00198                 hdr->a_total= total;
00199 
00200                 return 1;
00201         }
00202         return 0;
00203 }
00204 
00205 void copylink(char *source, char *dest, int mode, int owner, int group)
00206 {
00207         struct stat sst, dst;
00208         int sfd, dfd, n;
00209         int r, same= 0, change= 0, docopy= 1;
00210         char buf[4096];
00211 #       define hdr ((struct exec *) buf)
00212         pid_t pid = 0;
00213         int status = 0;
00214 
00215         /* Source must exist as a plain file, dest may exist as a plain file. */
00216 
00217         if (stat(source, &sst) < 0) { report(source); return; }
00218 
00219         if (mode == -1) {
00220                 mode= sst.st_mode & 07777;
00221                 if (!lflag || cflag) {
00222                         mode|= 0444;
00223                         if (mode & 0111) mode|= 0111;
00224                 }
00225         }
00226         if (owner == -1) owner= sst.st_uid;
00227         if (group == -1) group= sst.st_gid;
00228 
00229         if (!S_ISREG(sst.st_mode)) {
00230                 fprintf(stderr, "install: %s is not a regular file\n", source);
00231                 excode= 1;
00232                 return;
00233         }
00234         r= stat(dest, &dst);
00235         if (r < 0) {
00236                 if (errno != ENOENT) { report(dest); return; }
00237         } else {
00238                 if (!S_ISREG(dst.st_mode)) {
00239                         fprintf(stderr, "install: %s is not a regular file\n",
00240                                                                         dest);
00241                         excode= 1;
00242                         return;
00243                 }
00244 
00245                 /* Are the files the same? */
00246                 if (sst.st_dev == dst.st_dev && sst.st_ino == dst.st_ino) {
00247                         if (!lflag && cflag) {
00248                                 fprintf(stderr,
00249                                 "install: %s and %s are the same, can't copy\n",
00250                                         source, dest);
00251                                 excode= 1;
00252                                 return;
00253                         }
00254                         same= 1;
00255                 }
00256         }
00257 
00258         if (lflag && !same) {
00259                 /* Try to link the files. */
00260 
00261                 if (r >= 0 && unlink(dest) < 0) {
00262                         report(dest); return;
00263                 }
00264 
00265                 if (link(source, dest) >= 0) {
00266                         docopy= 0;
00267                 } else {
00268                         if (!cflag || errno != EXDEV) {
00269                                 fprintf(stderr,
00270                                         "install: can't link %s to %s: %s\n",
00271                                         source, dest, strerror(errno));
00272                                 excode= 1;
00273                                 return;
00274                         }
00275                 }
00276         }
00277 
00278         if (docopy && !same) {
00279                 /* Copy the files, stripping if necessary. */
00280                 long count= LONG_MAX;
00281                 int first= 1;
00282 
00283                 if ((sfd= open(source, O_RDONLY)) < 0) {
00284                         report(source); return;
00285                 }
00286 
00287                 /* Open for write is less simple, its mode may be 444. */
00288                 dfd= open(dest, O_WRONLY|O_CREAT|O_TRUNC, mode | 0600);
00289                 if (dfd < 0 && errno == EACCES) {
00290                         (void) chmod(dest, mode | 0600);
00291                         dfd= open(dest, O_WRONLY|O_TRUNC);
00292                 }
00293                 if (dfd < 0) {
00294                         report(dest);
00295                         close(sfd);
00296                         return;
00297                 }
00298 
00299                 pid= 0;
00300                 while (count > 0 && (n= read(sfd, buf, sizeof(buf))) > 0) {
00301                         if (first && n >= A_MINHDR && !BADMAG(*hdr)) {
00302                                 if (strip) {
00303                                         count= hdr->a_hdrlen
00304                                                 + hdr->a_text + hdr->a_data;
00305 #ifdef A_NSYM
00306                                         hdr->a_flags &= ~A_NSYM;
00307 #endif
00308                                         hdr->a_syms= 0;
00309                                 }
00310                                 if (stack != -1 && setstack(hdr)) change= 1;
00311 
00312                                 if (compress != nil) {
00313                                         /* Write first #! line. */
00314                                         (void) write(dfd, zcat, strlen(zcat));
00315 
00316                                         /* Put a compressor in between. */
00317                                         if ((pid= filter(dfd, compress)) < 0) {
00318                                                 close(sfd);
00319                                                 close(dfd);
00320                                                 return;
00321                                         }
00322                                         change= 1;
00323                                 }
00324                         }
00325                         if (count < n) n= count;
00326 
00327                         if (write(dfd, buf, n) < 0) {
00328                                 report(dest);
00329                                 close(sfd);
00330                                 close(dfd);
00331                                 if (pid != 0) (void) waitpid(pid, nil, 0);
00332                                 return;
00333                         }
00334                         count-= n;
00335                         first= 0;
00336                 }
00337                 if (n < 0) report(source);
00338                 close(sfd);
00339                 close(dfd);
00340                 if (pid != 0 && waitpid(pid, &status, 0) < 0 || status != 0) {
00341                         excode= 1;
00342                         return;
00343                 }
00344                 if (n < 0) return;
00345         } else {
00346                 if (stack != -1) {
00347                         /* The file has been linked into place.  Set the
00348                          * stack size.
00349                          */
00350                         if ((dfd= open(dest, O_RDWR)) < 0) {
00351                                 report(dest);
00352                                 return;
00353                         }
00354 
00355                         if ((n= read(dfd, buf, sizeof(*hdr))) < 0) {
00356                                 report(dest); return;
00357                         }
00358 
00359                         if (n >= A_MINHDR && !BADMAG(*hdr) && setstack(hdr)) {
00360                                 if (lseek(dfd, (off_t) 0, SEEK_SET) == -1
00361                                         || write(dfd, buf, n) < 0
00362                                 ) {
00363                                         report(dest);
00364                                         close(dfd);
00365                                         return;
00366                                 }
00367                                 change= 1;
00368                         }
00369                         close(dfd);
00370                 }
00371         }
00372 
00373         if (stat(dest, &dst) < 0) { report(dest); return; }
00374 
00375         if ((dst.st_mode & 07777) != mode) {
00376                 if (chmod(dest, mode) < 0) { report(dest); return; }
00377         }
00378         if (dst.st_uid != owner || dst.st_gid != group) {
00379                 if (chown(dest, owner, group) < 0 && errno != EPERM) {
00380                         report(dest); return;
00381                 }
00382                 /* Set the mode again, chown may have wrecked it. */
00383                 (void) chmod(dest, mode);
00384         }
00385         if (!change) {
00386                 struct utimbuf ubuf;
00387 
00388                 ubuf.actime= dst.st_atime;
00389                 ubuf.modtime= sst.st_mtime;
00390 
00391                 if (utime(dest, &ubuf) < 0 && errno != EPERM) {
00392                         report(dest); return;
00393                 }
00394         }
00395 }
00396 
00397 void usage(void)
00398 {
00399         fprintf(stderr, "\
00400 Usage:\n\
00401   install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] [file1] file2\n\
00402   install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] file ... dir\n\
00403   install -d [-o owner] [-g group] [-m mode] directory\n");
00404         exit(1);
00405 }
00406 
00407 void main(int argc, char **argv)
00408 {
00409         int i= 1;
00410         int mode= -1;           /* Mode of target. */
00411         int owner= -1;          /* Owner. */
00412         int group= -1;          /* Group. */
00413         int super = 0;
00414 #if NGROUPS_MAX > 0
00415         gid_t groups[NGROUPS_MAX];
00416         int ngroups;
00417         int g;
00418 #endif
00419 
00420         /* Only those in group 0 are allowed to set owner and group. */
00421         if (getgid() == 0) super = 1;
00422 #if NGROUPS_MAX > 0
00423         ngroups= getgroups(NGROUPS_MAX, groups);
00424         for (g= 0; g < ngroups; g++) if (groups[g] == 0) super= 1;
00425 #endif
00426         if (!super) {
00427                 setgid(getgid());
00428                 setuid(getuid());
00429         }
00430 
00431         /* May use a filter. */
00432         signal(SIGPIPE, SIG_IGN);
00433 
00434         while (i < argc && argv[i][0] == '-') {
00435                 char *p= argv[i++]+1;
00436                 char *end;
00437                 unsigned long num;
00438                 int wp;
00439                 struct passwd *pw;
00440                 struct group *gr;
00441 
00442                 if (strcmp(p, "-") == 0) break;
00443 
00444                 while (*p != 0) {
00445                         switch (*p++) {
00446                         case 'l':       lflag= 1;       break;
00447                         case 'c':       cflag= 1;       break;
00448                         case 's':       strip= 1;       break;
00449                         case 'd':       dflag= 1;       break;
00450                         case 'z':
00451                                 if (compress == nil) {
00452                                         compress= COMPRESS;
00453                                         zcat= ZCAT;
00454                                 }
00455                                 break;
00456                         case 'o':
00457                                 if (*p == 0) {
00458                                         if (i == argc) usage();
00459                                         p= argv[i++];
00460                                         if (*p == 0) usage();
00461                                 }
00462                                 num= strtoul(p, &end, 10);
00463                                 if (*end == 0) {
00464                                         if ((uid_t) num != num) usage();
00465                                         owner= num;
00466                                 } else {
00467                                         if ((pw= getpwnam(p)) == nil) {
00468                                                 fprintf(stderr,
00469                                                 "install: %s: unknown user\n",
00470                                                         p);
00471                                                 exit(1);
00472                                         }
00473                                         owner= pw->pw_uid;
00474                                 }
00475                                 p= "";
00476                                 break;
00477                         case 'g':
00478                                 if (*p == 0) {
00479                                         if (i == argc) usage();
00480                                         p= argv[i++];
00481                                         if (*p == 0) usage();
00482                                 }
00483                                 num= strtoul(p, &end, 10);
00484                                 if (*end == 0) {
00485                                         if ((gid_t) num != num) usage();
00486                                         group= num;
00487                                 } else {
00488                                         if ((gr= getgrnam(p)) == nil) {
00489                                                 fprintf(stderr,
00490                                                 "install: %s: unknown user\n",
00491                                                         p);
00492                                                 exit(1);
00493                                         }
00494                                         group= gr->gr_gid;
00495                                 }
00496                                 p= "";
00497                                 break;
00498                         case 'm':
00499                                 if (*p == 0) {
00500                                         if (i == argc) usage();
00501                                         p= argv[i++];
00502                                         if (*p == 0) usage();
00503                                 }
00504                                 num= strtoul(p, &end, 010);
00505                                 if (*end != 0 || (num & 07777) != num) usage();
00506                                 mode= num;
00507                                 if ((mode & S_ISUID) && super && owner == -1) {
00508                                         /* Setuid what?  Root most likely. */
00509                                         owner= 0;
00510                                 }
00511                                 if ((mode & S_ISGID) && super && group == -1) {
00512                                         group= 0;
00513                                 }
00514                                 p= "";
00515                                 break;
00516                         case 'S':
00517                                 if (*p == 0) {
00518                                         if (i == argc) usage();
00519                                         p= argv[i++];
00520                                         if (*p == 0) usage();
00521                                 }
00522                                 stack= strtol(p, &end, 0);
00523                                 wp= 0;
00524                                 if (end == p || stack < 0) usage();
00525                                 p= end;
00526                                 while (*p != 0) {
00527                                         switch (*p++) {
00528                                         case 'm':
00529                                         case 'M': num= 1024 * 1024L; break;
00530                                         case 'k':
00531                                         case 'K': num= 1024; break;
00532                                         case 'w':
00533                                         case 'W': num= 4; wp++; break;
00534                                         case 'b':
00535                                         case 'B': num= 1; break;
00536                                         default: usage();
00537                                         }
00538                                         if (stack > LONG_MAX / num) usage();
00539                                         stack*= num;
00540                                 }
00541                                 wordpow= 0;
00542                                 while (wp > 0) { stack /= 4; wordpow++; wp--; }
00543                                 break;
00544                         default:
00545                                 if ((unsigned) (p[-1] - '1') <= ('9' - '1')) {
00546                                         compress= GZIP;
00547                                         GZIP[1][1]= p[-1];
00548                                         zcat= GZCAT;
00549                                         break;
00550                                 }
00551                                 usage();
00552                         }
00553                 }
00554         }
00555         /* Some options don't mix. */
00556         if (dflag && (cflag || lflag || strip)) usage();
00557 
00558         /* Don't let the user umask interfere. */
00559         umask(000);
00560 
00561         if (dflag) {
00562                 /* install directory */
00563                 if ((argc - i) != 1) usage();
00564 
00565                 makedir(argv[i], mode, owner, group);
00566         } else {
00567                 struct stat st;
00568 
00569                 if ((argc - i) < 1) usage();
00570                 if ((lflag || cflag) && (argc - i) == 1) usage();
00571 
00572                 if (stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)) {
00573                         /* install file ... dir */
00574                         char *target= nil;
00575                         char *base;
00576 
00577                         if ((argc - i) == 1) usage();
00578 
00579                         while (i < argc-1) {
00580                                 if ((base= strrchr(argv[i], '/')) == nil)
00581                                         base= argv[i];
00582                                 else
00583                                         base++;
00584                                 target= allocate(target, strlen(argv[argc-1])
00585                                                 + 1 + strlen(base) + 1);
00586                                 strcpy(target, argv[argc-1]);
00587                                 strcat(target, "/");
00588                                 strcat(target, base);
00589 
00590                                 copylink(argv[i++], target, mode, owner, group);
00591                         }
00592                 } else {
00593                         /* install [file1] file2 */
00594 
00595                         copylink(argv[i], argv[argc-1], mode, owner, group);
00596                 }
00597         }
00598         exit(excode);
00599 }

Generated on Fri Apr 14 22:57:07 2006 for minix by  doxygen 1.4.6