1 /***********************************************************
2 Copyright IBM Corporation 1987
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of IBM not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.
14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21 ******************************************************************/
27 /* ALSO utimes and strip the file
29 Generic install command. Options are:
30 -s strip the file (default for executable files with no extension)
31 -ns do not strip the file (default for other files)
32 -c ignored for compatability
33 -m <mode> chmod to this value
34 -o <user> chown to this user
35 -g <group> chgrp to this group
36 -f target path is a file
37 -q be very, very quick and quiet
38 -l <envcwd> attempt symbolic link back from destination to source
39 with the current directory in the specified environment
45 #include <afs/param.h>
53 #include <sys/types.h>
69 struct stat istat, ostat;
73 #ifndef AFS_LINUX20_ENV
74 extern char *sys_errlist[];
76 #if defined(AFS_AIX_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DECOSF_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX20_ENV)
77 extern struct passwd *getpwnam();
82 extern char *rindex(); /* this should always be defined, shouldn't it? */
85 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
88 struct timeval tvp[2];
92 times.actime = tvp[0].tv_sec;
93 times.modtime = tvp[1].tv_sec;
94 return (utime(file,×));
98 static char *strrpbrk (s, set)
105 bzero (sets, sizeof(sets));
106 while (*set) sets[*set++] = 1;
108 while (i > 0) if (sets[s[--i]]) return &s[i];
112 char *ErrorString(aerrno)
114 static char tbuffer[100];
115 if (aerrno < 0 || aerrno >= sys_nerr) {
116 sprintf(tbuffer, "undefined error code %d", aerrno);
119 return sys_errlist[aerrno];
124 {if (rindex(aname, '.') == 0) return 1;
130 {register afs_int32 value;
140 #if defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DECOSF_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX20_ENV)
142 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
143 * reason why it wouldn't work.
146 quickStrip (iname, oname, ignored, copy_only)
147 char *iname, *oname; {
149 static char *strip[] = {
152 static char *copy[] = {
157 * first, copy the `iname' to the `oname'
159 switch (pid = fork()) {
167 execve("/bin/cp", copy, (char **)0);
171 default: /* parent */
172 if (waitpid(pid, &status, 0) != pid) {
179 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
184 * need to do a chmod to guarantee that the perms will permit
185 * the strip. Perms are fixed up later.
187 if (chmod(oname, 0700)) {
193 * done the copy, now strip if desired.
198 switch (pid = fork()) {
206 #define STRIP_BIN "/usr/ccs/bin/strip"
207 #elif defined(AFS_LINUX20_ENV)
208 #define STRIP_BIN "/usr/bin/strip"
210 #define STRIP_BIN "/bin/strip"
212 execve(STRIP_BIN, strip, (char **)0);
216 default: /* parent */
217 if (waitpid(pid, &status, 0) != pid) {
227 * AIXobject - lie about file type
233 * !0 indicating that the file in question is an XCOFF type file.
236 * Since /bin/strip will make that call for us, we will lie so that
248 * whoa! back up and be a little more rational (every little bit helps in
252 quickStrip (iname, oname, ignored, copy_only)
253 char *iname, *oname; {
255 static char *strip[] = {
258 static char *copy[] = {
263 * first, copy the `iname' to the `oname'
265 switch (pid = fork()) {
273 execve("/bin/cp", copy, 0);
277 default: /* parent */
278 if (waitpid(pid, &status, 0) != pid) {
285 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
290 * need to do a chmod to guarantee that the perms will permit
291 * the strip. Perms are fixed up later.
293 if (chmod(oname, 0700)) {
299 * done the copy, now strip if desired.
304 switch (pid = fork()) {
311 execve("/bin/strip", strip, 0);
312 perror("/bin/strip");
315 default: /* parent */
316 if (waitpid(pid, &status, 0) != pid) {
326 * AIXobject - lie about file type
332 * !0 indicating that the file in question is an XCOFF type file.
335 * Since /bin/strip will make that call for us, we will lie so that
342 #endif /* AFS_AIX32_ENV */
343 #else /* !AFS_AIX_ENV */
347 int quickStrip(fd, asize)
348 int fd; /* file descriptor */
349 afs_int32 asize; /* ignored */
353 int mysex, swapheader;
355 /* Read the file header, if it is one. */
356 if (lseek(fd, 0, L_SET) == -1) {
357 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
360 dum = read(fd, (char *) &fheader, sizeof(fheader));
361 /* Fail on I/O error */
363 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
366 /* If the file is smaller than a file header, forget it. */
367 if (dum != sizeof(fheader)) return 0;
368 #ifdef AFS_DECOSF_ENV
369 mysex = LITTLEENDIAN;
371 mysex = gethostsex();
372 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
375 if (fheader.f_magic == MIPSELMAGIC) {
376 if (mysex == BIGENDIAN) swapheader = 1;
377 } else if (fheader.f_magic == MIPSEBMAGIC) {
378 if (mysex == LITTLEENDIAN) swapheader = 1;
379 } else return 0; /* not executable */
380 #ifdef AFS_DECOSF_ENV
381 if (swapheader) return 0;
383 if (swapheader) swap_filehdr(&fheader, gethostsex());
385 /* Already stripped? */
386 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
387 /* Strip it. Zero out the symbol pointers. */
388 newlen = fheader.f_symptr;
389 fheader.f_symptr = 0;
391 #ifndef AFS_DECOSF_ENV
392 if (swapheader) swap_filehdr(&fheader, gethostsex());
394 if (lseek(fd, 0, L_SET) == -1) return -1;
395 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
396 /* Now truncate the file itself. */
397 if (ftruncate(fd, newlen) != 0) return -1;
401 static int quickStrip (afd, asize)
408 n = lseek(afd, 0, 0);
409 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
410 n = read(afd, &buf, sizeof(buf));
411 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
413 if (n >= sizeof(*head) && !N_BADMAG(*head))
414 {/* This code lifted from strip.c. */
415 bytesLeft = (afs_int32) head->a_text + head->a_data;
416 head->a_syms = head->a_trsize = head->a_drsize = 0;
417 if (head->a_magic == ZMAGIC)
418 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
419 /* also include size of header */
420 bytesLeft += sizeof(*head);
421 n = lseek(afd, 0, 0);
422 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
423 n = write(afd, &buf, sizeof(buf));
424 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
429 /* check if size of stripped file is same as existing file */
430 if (bytesLeft != 0 && bytesLeft != asize)
431 {if (ftruncate(afd, bytesLeft) < 0)
432 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
438 #endif /* AFS_HPUX_ENV */
440 #include "AFS_component_version_number.c"
446 int setOwner, setMode, setGroup, ifd, ofd;
447 afs_int32 mode, owner, group;
450 char *fnames[MAXFILES], *newNames[MAXFILES];
451 afs_int32 rcode, code, newcode;
454 #if defined (AFS_HPUX_ENV)
455 char pnameBusy[1024];
456 #endif /* AFS_HPUX_ENV */
459 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
460 char myHostName[100];
461 struct timeval tvp[2];
466 register afs_int32 i;
470 * The following signal action for AIX is necessary so that in case of a
471 * crash (i.e. core is generated) we can include the user's data section
472 * in the core dump. Unfortunately, by default, only a partial core is
473 * generated which, in many cases, isn't too useful.
475 struct sigaction nsa;
477 sigemptyset(&nsa.sa_mask);
478 nsa.sa_handler = SIG_DFL;
479 nsa.sa_flags = SA_FULLDUMP;
480 sigaction(SIGSEGV, &nsa, NULL);
484 strip = -1; /* don't know yet */
490 isDir = -1; /* don't know yet */
492 for(i=1; i<argc; i++)
496 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
497 else if (!strcmp(tp, "-s")) strip = 1;
498 else if (!strcmp(tp, "-ns")) strip = 0;
499 else if (!strcmp(tp, "-c")) /* nothing */;
500 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
501 else if (!strcmp(tp, "-o"))
502 {/* look up the dude */
503 tpw = getpwnam(argv[++i]);
505 {printf("User %s not found in passwd database, ignored\n", argv[i]);
508 {owner = tpw->pw_uid;
512 else if (!strcmp(tp, "-g"))
513 {/* look up the dude */
514 tgp = getgrnam(argv[++i]);
516 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
519 {group = tgp->gr_gid;
524 printf("Bad switch %s\n", argv[i]);
530 if (fptr >= MAXFILES)
531 {printf("Too many files on command line, max is %d\n", MAXFILES);
534 fnames[fptr++] = argv[i];
538 /* we've parse the commands, now *do* them */
540 /* otherwise we are doing a local install, so we do the work for each file
541 here the last name in the fname array is the dir in which to put all
545 {printf("Not enough file names\n");
549 /* N file usage requires last argument to be a directory. If -f was
550 specified it is an error. In the 2 file usage when -f is not specified
551 use a heuristic. If the ends of the two pathnames are equal then assume
552 the target is a file, otherwise assume it is a directory. */
554 if ((fptr > 2) && (isDir == 0)) {
555 printf ("target must be a directory, don't use multiple source files with -f switch\n");
558 else if (fptr > 2) isDir = 1;
559 else if (isDir != 0) {
563 targetSuffix = strrpbrk (fnames[1], "./");
564 sourceSuffix = strrpbrk (fnames[0], "./");
565 if (sourceSuffix == 0) {
566 sourceSuffix = fnames[0];
567 if (targetSuffix == 0) targetSuffix = fnames[1];
570 else if (targetSuffix == 0) targetSuffix = fnames[1];
571 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
574 dname = fnames[--fptr];
575 if (stat(dname, &istat) < 0) {
576 if ((errno == ENOENT) || (errno == ENOTDIR)) {
578 char protopath[BUFSIZ];
583 protopath[i] = dname[i];
584 c = dname[++i]; /* next char */
585 } while (!((c == 0) || (c == '/')));
588 /* don't mkdir last component if target is a file */
589 if ((c == 0) && (isDir == 0)) break;
591 /* else create dir component if it doesn't exist */
592 code = stat(protopath, &istat);
593 if (code && (errno == ENOENT)) {
594 code = mkdir (protopath, 0755);
596 printf ("Can't create destination path at %s\n",
601 } /* while dname not exhausted */
602 if (isDir == -1) isDir = 1;
605 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
609 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
613 /* either can be n files and one dir, or one file and one target */
614 if (!isDir && fptr != 1) {
615 printf("target for multiple files must be a dir\n");
620 {/* figure out name to put as entry name for file */
621 tp = rindex(fnames[i], '/');
622 if (tp) newNames[i] = tp+1;
623 else newNames[i] = fnames[i];
626 {/* copy newName[i] into directory dname */
628 /* pname is target file in either case */
630 {strcpy(pname, dname);
632 strcat(pname, newNames[i]);
634 else strcpy(pname, dname);
635 strcpy(pnametmp, pname);
636 /* Make up a temporary name for a destination */
637 pnamelen = strlen(pnametmp);
638 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
639 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
640 pnametmp[pnamelen] = '.';
641 strcpy(&pnametmp[pnamelen+1], myHostName);
642 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
644 ifd = open(fnames[i], O_RDONLY, 0);
646 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
650 if (fstat (ifd, &istat) < 0) {
651 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
656 if (lstat(pname, &ostat) == 0) {
657 if ((ostat.st_size == istat.st_size) &&
658 (ostat.st_mtime == istat.st_mtime) &&
659 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
660 ((!setOwner) || (ostat.st_uid == owner)) &&
661 ((!setGroup) || (ostat.st_gid == group))) {
663 printf("No changes to %s since %s installed\n", fnames[i], pname);
667 #if defined(AFS_AIX_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DECOSF_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX20_ENV)
670 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])) && AIXobject(fnames[i]))
673 /* Simply copy target to dest */
674 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
676 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
677 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
678 close(ifd); unlink(pnametmp);
685 ofd = open(pnametmp, O_RDWR, 0);
687 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
692 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
693 #else /* AFS_AIX_ENV */
694 /* check to see if this file is hard to duplicate */
695 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
697 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
702 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
703 /* here both files are open and ready to go */
705 {code = read(ifd, diskBuffer, BUFSIZE);
706 if (code == 0) break;
708 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
712 newcode = write(ofd, diskBuffer, code);
714 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
719 {rcode = 1; /* an error occurred copying the file */
720 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
722 close(ifd); unlink(pnametmp); close(ofd);
723 continue; /* to the next file */
725 /* strip the file? */
727 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
728 if (quickStrip(ofd,istat.st_size) < 0) {
729 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
730 close(ifd); unlink(pnametmp);
735 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
738 #endif /* AFS_AIX_ENV */
739 if (fchmod(ofd, mode) < 0)
740 {printf("Couldn't chmod output temp file ``%s'': %s\n",
741 pnametmp, ErrorString(errno));
742 unlink(pnametmp); close(ofd);
747 tvp[0].tv_sec = istat.st_atime;
749 tvp[1].tv_sec = istat.st_mtime;
751 if (utimes(pnametmp, tvp) < 0)
752 {printf("Couldn't utimes output temp file ``%s'': %s\n",
753 pnametmp, ErrorString(errno));
754 unlink(pnametmp); close(ofd);
760 {printf("Warning: Could not close output temp file %s (%s)\n",
761 pnametmp, ErrorString(errno));
763 rcode = 1; /* an error occurred closing the output file */
764 continue; /* to the next file */
767 /* do this later so vice doesn't see chown of unstored file */
768 if (setOwner || setGroup)
769 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
770 printf("Couldn't set %s for output temp file %s: %s\n",
771 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
772 pnametmp, ErrorString(errno));
778 if (rename(pnametmp, pname) < 0) {
779 #if defined(AFS_HPUX_ENV)
780 if (errno == ETXTBSY) {
781 (void)strcpy(pnameBusy, pname);
782 (void)strcat(pnameBusy, ".BUSY");
783 if (rename(pname, pnameBusy) == 0) {
784 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
786 "Please delete this file when the program using it is finished.\n");
787 if (rename(pnametmp, pname) < 0) {
788 #endif /* AFS_HPUX_ENV */
790 printf("Couldn't rename temp file %s to be output file %s: %s\n",
791 pnametmp, pname, ErrorString(errno));
796 #if defined(AFS_HPUX_ENV)
801 "Couldn't move busy target file %s to make room for new version %s: %s\n",
802 pname, pnametmp, ErrorString(errno));
803 if (errno == ETXTBSY) {
805 "Try terminating any programs using the file %s and then re-run %s.\n",
813 #endif /* AFS_HPUX_ENV */