2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
13 Permission to use, copy, modify, and distribute this software and its
14 documentation for any purpose and without fee is hereby granted,
15 provided that the above copyright notice appear in all copies and that
16 both that copyright notice and this permission notice appear in
17 supporting documentation, and that the name of IBM not be
18 used in advertising or publicity pertaining to distribution of the
19 software without specific, written prior permission.
21 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 ******************************************************************/
31 /* ALSO utimes and strip the file
33 Generic install command. Options are:
34 -s strip the file (default for executable files with no extension)
35 -ns do not strip the file (default for other files)
36 -c ignored for compatability
37 -m <mode> chmod to this value
38 -o <user> chown to this user
39 -g <group> chgrp to this group
40 -f target path is a file
41 -q be very, very quick and quiet
42 -l <envcwd> attempt symbolic link back from destination to source
43 with the current directory in the specified environment
49 #include <afsconfig.h>
50 #include <afs/param.h>
61 #include <sys/types.h>
86 struct stat istat, ostat;
90 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
91 extern char *sys_errlist[];
93 #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) || defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
94 extern struct passwd *getpwnam();
98 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
101 struct timeval tvp[2];
103 struct utimbuf times;
105 times.actime = tvp[0].tv_sec;
106 times.modtime = tvp[1].tv_sec;
107 return (utime(file,×));
111 static char *strrpbrk (s, set)
118 memset(sets, 0, sizeof(sets));
119 while (*set) sets[(int) *set++] = 1;
121 while (i > 0) if (sets[(int)s[--i]]) return &s[i];
125 char *ErrorString(aerrno)
127 static char tbuffer[100];
128 if (aerrno < 0 || aerrno >= sys_nerr) {
129 sprintf(tbuffer, "undefined error code %d", aerrno);
131 strcpy(tbuffer, sys_errlist[aerrno]);
139 {if (strrchr(aname, '.') == 0) return 1;
146 {register afs_int32 value;
149 while ((tc = *astr++))
156 #if defined(AFS_HPUX_ENV) || defined(AFS_SUN5_ENV) || defined(AFS_DECOSF_ENV) || defined(AFS_SGI_ENV) || defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV)
158 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
159 * reason why it wouldn't work.
162 quickStrip (iname, oname, ignored, copy_only)
163 char *iname, *oname; {
165 static char *strip[] = {
168 static char *copy[] = {
173 * first, copy the `iname' to the `oname'
175 switch (pid = fork()) {
183 execve("/bin/cp", copy, (char **)0);
187 default: /* parent */
188 if (waitpid(pid, &status, 0) != pid) {
195 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
200 * need to do a chmod to guarantee that the perms will permit
201 * the strip. Perms are fixed up later.
203 if (chmod(oname, 0700)) {
210 * done the copy, now strip if desired.
215 switch (pid = fork()) {
223 #define STRIP_BIN "/usr/ccs/bin/strip"
224 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
225 #define STRIP_BIN "/usr/bin/strip"
227 #define STRIP_BIN "/bin/strip"
229 execve(STRIP_BIN, strip, (char **)0);
233 default: /* parent */
234 if (waitpid(pid, &status, 0) != pid) {
245 * AIXobject - lie about file type
251 * !0 indicating that the file in question is an XCOFF type file.
254 * Since /bin/strip will make that call for us, we will lie so that
267 * whoa! back up and be a little more rational (every little bit helps in
271 quickStrip (iname, oname, ignored, copy_only)
272 char *iname, *oname; {
274 static char *strip[] = {
277 static char *copy[] = {
282 * first, copy the `iname' to the `oname'
284 switch (pid = fork()) {
292 execve("/bin/cp", copy, 0);
296 default: /* parent */
297 if (waitpid(pid, &status, 0) != pid) {
304 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
309 * need to do a chmod to guarantee that the perms will permit
310 * the strip. Perms are fixed up later.
312 if (chmod(oname, 0700)) {
318 * done the copy, now strip if desired.
323 switch (pid = fork()) {
330 execve("/bin/strip", strip, 0);
331 perror("/bin/strip");
334 default: /* parent */
335 if (waitpid(pid, &status, 0) != pid) {
345 * AIXobject - lie about file type
351 * !0 indicating that the file in question is an XCOFF type file.
354 * Since /bin/strip will make that call for us, we will lie so that
361 #endif /* AFS_AIX32_ENV */
362 #else /* !AFS_AIX_ENV */
366 int quickStrip(fd, asize)
367 int fd; /* file descriptor */
368 afs_int32 asize; /* ignored */
372 int mysex, swapheader;
374 /* Read the file header, if it is one. */
375 if (lseek(fd, 0, L_SET) == -1) {
376 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
379 dum = read(fd, (char *) &fheader, sizeof(fheader));
380 /* Fail on I/O error */
382 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
385 /* If the file is smaller than a file header, forget it. */
386 if (dum != sizeof(fheader)) return 0;
387 #ifdef AFS_DECOSF_ENV
388 mysex = LITTLEENDIAN;
390 mysex = gethostsex();
391 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
394 if (fheader.f_magic == MIPSELMAGIC) {
395 if (mysex == BIGENDIAN) swapheader = 1;
396 } else if (fheader.f_magic == MIPSEBMAGIC) {
397 if (mysex == LITTLEENDIAN) swapheader = 1;
398 } else return 0; /* not executable */
399 #ifdef AFS_DECOSF_ENV
400 if (swapheader) return 0;
402 if (swapheader) swap_filehdr(&fheader, gethostsex());
404 /* Already stripped? */
405 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
406 /* Strip it. Zero out the symbol pointers. */
407 newlen = fheader.f_symptr;
408 fheader.f_symptr = 0;
410 #ifndef AFS_DECOSF_ENV
411 if (swapheader) swap_filehdr(&fheader, gethostsex());
413 if (lseek(fd, 0, L_SET) == -1) return -1;
414 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
415 /* Now truncate the file itself. */
416 if (ftruncate(fd, newlen) != 0) return -1;
420 static int quickStrip (afd, asize)
427 n = lseek(afd, 0, 0);
428 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
429 n = read(afd, &buf, sizeof(buf));
430 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
432 if (n >= sizeof(*head) && !N_BADMAG(*head))
433 {/* This code lifted from strip.c. */
434 bytesLeft = (afs_int32) head->a_text + head->a_data;
435 head->a_syms = head->a_trsize = head->a_drsize = 0;
436 if (head->a_magic == ZMAGIC)
437 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
438 /* also include size of header */
439 bytesLeft += sizeof(*head);
440 n = lseek(afd, 0, 0);
441 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
442 n = write(afd, &buf, sizeof(buf));
443 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
448 /* check if size of stripped file is same as existing file */
449 if (bytesLeft != 0 && bytesLeft != asize)
450 {if (ftruncate(afd, bytesLeft) < 0)
451 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
457 #endif /* AFS_HPUX_ENV */
459 #include "AFS_component_version_number.c"
466 int setOwner, setMode, setGroup, ifd, ofd;
467 afs_int32 mode=0, owner, group;
470 char *fnames[MAXFILES], *newNames[MAXFILES];
471 afs_int32 rcode, code;
474 #if defined (AFS_HPUX_ENV)
475 char pnameBusy[1024];
476 #endif /* AFS_HPUX_ENV */
479 #if defined (AFS_AIX_ENV) || defined(AFS_XBSD_ENV)
481 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
483 char myHostName[100];
484 struct timeval tvp[2];
489 register afs_int32 i;
493 * The following signal action for AIX is necessary so that in case of a
494 * crash (i.e. core is generated) we can include the user's data section
495 * in the core dump. Unfortunately, by default, only a partial core is
496 * generated which, in many cases, isn't too useful.
498 struct sigaction nsa;
500 sigemptyset(&nsa.sa_mask);
501 nsa.sa_handler = SIG_DFL;
502 nsa.sa_flags = SA_FULLDUMP;
503 sigaction(SIGSEGV, &nsa, NULL);
507 strip = -1; /* don't know yet */
513 isDir = -1; /* don't know yet */
515 for(i=1; i<argc; i++)
519 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
520 else if (!strcmp(tp, "-s")) strip = 1;
521 else if (!strcmp(tp, "-ns")) strip = 0;
522 else if (!strcmp(tp, "-c")) /* nothing */;
523 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
524 else if (!strcmp(tp, "-o"))
525 {/* look up the dude */
526 tpw = getpwnam(argv[++i]);
528 {printf("User %s not found in passwd database, ignored\n", argv[i]);
531 {owner = tpw->pw_uid;
535 else if (!strcmp(tp, "-g"))
536 {/* look up the dude */
537 tgp = getgrnam(argv[++i]);
539 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
542 {group = tgp->gr_gid;
547 printf("Bad switch %s\n", argv[i]);
553 if (fptr >= MAXFILES)
554 {printf("Too many files on command line, max is %d\n", MAXFILES);
557 fnames[fptr++] = argv[i];
561 /* we've parse the commands, now *do* them */
563 /* otherwise we are doing a local install, so we do the work for each file
564 here the last name in the fname array is the dir in which to put all
568 {printf("Not enough file names\n");
572 /* N file usage requires last argument to be a directory. If -f was
573 specified it is an error. In the 2 file usage when -f is not specified
574 use a heuristic. If the ends of the two pathnames are equal then assume
575 the target is a file, otherwise assume it is a directory. */
577 if ((fptr > 2) && (isDir == 0)) {
578 printf ("target must be a directory, don't use multiple source files with -f switch\n");
581 else if (fptr > 2) isDir = 1;
582 else if (isDir != 0) {
586 targetSuffix = strrpbrk (fnames[1], "./");
587 sourceSuffix = strrpbrk (fnames[0], "./");
588 if (sourceSuffix == 0) {
589 sourceSuffix = fnames[0];
590 if (targetSuffix == 0) targetSuffix = fnames[1];
593 else if (targetSuffix == 0) targetSuffix = fnames[1];
594 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
597 dname = fnames[--fptr];
598 if (stat(dname, &istat) < 0) {
599 if ((errno == ENOENT) || (errno == ENOTDIR)) {
601 char protopath[BUFSIZ];
606 protopath[i] = dname[i];
607 c = dname[++i]; /* next char */
608 } while (!((c == 0) || (c == '/')));
611 /* don't mkdir last component if target is a file */
612 if ((c == 0) && (isDir == 0)) break;
614 /* else create dir component if it doesn't exist */
615 code = stat(protopath, &istat);
616 if (code && (errno == ENOENT)) {
617 code = mkdir (protopath, 0755);
619 printf ("Can't create destination path at %s\n",
624 } /* while dname not exhausted */
625 if (isDir == -1) isDir = 1;
628 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
632 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
636 /* either can be n files and one dir, or one file and one target */
637 if (!isDir && fptr != 1) {
638 printf("target for multiple files must be a dir\n");
643 {/* figure out name to put as entry name for file */
644 tp = strrchr(fnames[i], '/');
645 if (tp) newNames[i] = tp+1;
646 else newNames[i] = fnames[i];
649 {/* copy newName[i] into directory dname */
651 /* pname is target file in either case */
653 {strcpy(pname, dname);
655 strcat(pname, newNames[i]);
657 else strcpy(pname, dname);
658 strcpy(pnametmp, pname);
659 /* Make up a temporary name for a destination */
660 pnamelen = strlen(pnametmp);
661 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
662 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
663 pnametmp[pnamelen] = '.';
664 strcpy(&pnametmp[pnamelen+1], myHostName);
665 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
667 ifd = open(fnames[i], O_RDONLY, 0);
669 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
673 if (fstat (ifd, &istat) < 0) {
674 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
679 if (lstat(pname, &ostat) == 0) {
680 if ((ostat.st_size == istat.st_size) &&
681 (ostat.st_mtime == istat.st_mtime) &&
682 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
683 ((!setOwner) || (ostat.st_uid == owner)) &&
684 ((!setGroup) || (ostat.st_gid == group))) {
686 printf("No changes to %s since %s installed\n", fnames[i], pname);
690 #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) || defined(AFS_DARWIN_ENV) || defined(AFS_OBSD_ENV)
693 ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])) && AIXobject(fnames[i])))
696 /* Simply copy target to dest */
697 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
699 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
700 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
701 close(ifd); unlink(pnametmp);
708 ofd = open(pnametmp, O_RDWR, 0);
710 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
715 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
716 #else /* AFS_AIX_ENV */
717 /* check to see if this file is hard to duplicate */
718 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
720 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
725 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
726 /* here both files are open and ready to go */
728 {code = read(ifd, diskBuffer, BUFSIZE);
729 if (code == 0) break;
731 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
735 newcode = write(ofd, diskBuffer, code);
737 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
742 {rcode = 1; /* an error occurred copying the file */
743 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
745 close(ifd); unlink(pnametmp); close(ofd);
746 continue; /* to the next file */
748 /* strip the file? */
750 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
751 if (quickStrip(ofd,istat.st_size) < 0) {
752 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
753 close(ifd); unlink(pnametmp);
758 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
761 #endif /* AFS_AIX_ENV */
762 if (fchmod(ofd, mode) < 0)
763 {printf("Couldn't chmod output temp file ``%s'': %s\n",
764 pnametmp, ErrorString(errno));
765 unlink(pnametmp); close(ofd);
770 tvp[0].tv_sec = istat.st_atime;
772 tvp[1].tv_sec = istat.st_mtime;
774 if (utimes(pnametmp, tvp) < 0)
775 {printf("Couldn't utimes output temp file ``%s'': %s\n",
776 pnametmp, ErrorString(errno));
777 unlink(pnametmp); close(ofd);
783 {printf("Warning: Could not close output temp file %s (%s)\n",
784 pnametmp, ErrorString(errno));
786 rcode = 1; /* an error occurred closing the output file */
787 continue; /* to the next file */
790 /* do this later so vice doesn't see chown of unstored file */
791 if (setOwner || setGroup)
792 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
793 printf("Couldn't set %s for output temp file %s: %s\n",
794 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
795 pnametmp, ErrorString(errno));
801 if (rename(pnametmp, pname) < 0) {
802 #if defined(AFS_HPUX_ENV)
803 if (errno == ETXTBSY) {
804 (void)strcpy(pnameBusy, pname);
805 (void)strcat(pnameBusy, ".BUSY");
806 if (rename(pname, pnameBusy) == 0) {
807 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
809 "Please delete this file when the program using it is finished.\n");
810 if (rename(pnametmp, pname) < 0) {
811 #endif /* AFS_HPUX_ENV */
813 printf("Couldn't rename temp file %s to be output file %s: %s\n",
814 pnametmp, pname, ErrorString(errno));
819 #if defined(AFS_HPUX_ENV)
824 "Couldn't move busy target file %s to make room for new version %s: %s\n",
825 pname, pnametmp, ErrorString(errno));
826 if (errno == ETXTBSY) {
828 "Try terminating any programs using the file %s and then re-run %s.\n",
836 #endif /* AFS_HPUX_ENV */