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>
85 struct stat istat, ostat;
89 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
90 extern char *sys_errlist[];
92 #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_FBSD_ENV)
93 extern struct passwd *getpwnam();
97 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
100 struct timeval tvp[2];
102 struct utimbuf times;
104 times.actime = tvp[0].tv_sec;
105 times.modtime = tvp[1].tv_sec;
106 return (utime(file,×));
110 static char *strrpbrk (s, set)
117 memset(sets, 0, sizeof(sets));
118 while (*set) sets[(int) *set++] = 1;
120 while (i > 0) if (sets[(int)s[--i]]) return &s[i];
124 char *ErrorString(aerrno)
126 static char tbuffer[100];
127 if (aerrno < 0 || aerrno >= sys_nerr) {
128 sprintf(tbuffer, "undefined error code %d", aerrno);
130 strcpy(tbuffer, sys_errlist[aerrno]);
138 {if (strrchr(aname, '.') == 0) return 1;
145 {register afs_int32 value;
148 while ((tc = *astr++))
155 #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)
157 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
158 * reason why it wouldn't work.
161 quickStrip (iname, oname, ignored, copy_only)
162 char *iname, *oname; {
164 static char *strip[] = {
167 static char *copy[] = {
172 * first, copy the `iname' to the `oname'
174 switch (pid = fork()) {
182 execve("/bin/cp", copy, (char **)0);
186 default: /* parent */
187 if (waitpid(pid, &status, 0) != pid) {
194 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
199 * need to do a chmod to guarantee that the perms will permit
200 * the strip. Perms are fixed up later.
202 if (chmod(oname, 0700)) {
208 * done the copy, now strip if desired.
213 switch (pid = fork()) {
221 #define STRIP_BIN "/usr/ccs/bin/strip"
222 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
223 #define STRIP_BIN "/usr/bin/strip"
225 #define STRIP_BIN "/bin/strip"
227 execve(STRIP_BIN, strip, (char **)0);
231 default: /* parent */
232 if (waitpid(pid, &status, 0) != pid) {
242 * AIXobject - lie about file type
248 * !0 indicating that the file in question is an XCOFF type file.
251 * Since /bin/strip will make that call for us, we will lie so that
264 * whoa! back up and be a little more rational (every little bit helps in
268 quickStrip (iname, oname, ignored, copy_only)
269 char *iname, *oname; {
271 static char *strip[] = {
274 static char *copy[] = {
279 * first, copy the `iname' to the `oname'
281 switch (pid = fork()) {
289 execve("/bin/cp", copy, 0);
293 default: /* parent */
294 if (waitpid(pid, &status, 0) != pid) {
301 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
306 * need to do a chmod to guarantee that the perms will permit
307 * the strip. Perms are fixed up later.
309 if (chmod(oname, 0700)) {
315 * done the copy, now strip if desired.
320 switch (pid = fork()) {
327 execve("/bin/strip", strip, 0);
328 perror("/bin/strip");
331 default: /* parent */
332 if (waitpid(pid, &status, 0) != pid) {
342 * AIXobject - lie about file type
348 * !0 indicating that the file in question is an XCOFF type file.
351 * Since /bin/strip will make that call for us, we will lie so that
358 #endif /* AFS_AIX32_ENV */
359 #else /* !AFS_AIX_ENV */
363 int quickStrip(fd, asize)
364 int fd; /* file descriptor */
365 afs_int32 asize; /* ignored */
369 int mysex, swapheader;
371 /* Read the file header, if it is one. */
372 if (lseek(fd, 0, L_SET) == -1) {
373 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
376 dum = read(fd, (char *) &fheader, sizeof(fheader));
377 /* Fail on I/O error */
379 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
382 /* If the file is smaller than a file header, forget it. */
383 if (dum != sizeof(fheader)) return 0;
384 #ifdef AFS_DECOSF_ENV
385 mysex = LITTLEENDIAN;
387 mysex = gethostsex();
388 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
391 if (fheader.f_magic == MIPSELMAGIC) {
392 if (mysex == BIGENDIAN) swapheader = 1;
393 } else if (fheader.f_magic == MIPSEBMAGIC) {
394 if (mysex == LITTLEENDIAN) swapheader = 1;
395 } else return 0; /* not executable */
396 #ifdef AFS_DECOSF_ENV
397 if (swapheader) return 0;
399 if (swapheader) swap_filehdr(&fheader, gethostsex());
401 /* Already stripped? */
402 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
403 /* Strip it. Zero out the symbol pointers. */
404 newlen = fheader.f_symptr;
405 fheader.f_symptr = 0;
407 #ifndef AFS_DECOSF_ENV
408 if (swapheader) swap_filehdr(&fheader, gethostsex());
410 if (lseek(fd, 0, L_SET) == -1) return -1;
411 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
412 /* Now truncate the file itself. */
413 if (ftruncate(fd, newlen) != 0) return -1;
417 static int quickStrip (afd, asize)
424 n = lseek(afd, 0, 0);
425 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
426 n = read(afd, &buf, sizeof(buf));
427 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
429 if (n >= sizeof(*head) && !N_BADMAG(*head))
430 {/* This code lifted from strip.c. */
431 bytesLeft = (afs_int32) head->a_text + head->a_data;
432 head->a_syms = head->a_trsize = head->a_drsize = 0;
433 if (head->a_magic == ZMAGIC)
434 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
435 /* also include size of header */
436 bytesLeft += sizeof(*head);
437 n = lseek(afd, 0, 0);
438 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
439 n = write(afd, &buf, sizeof(buf));
440 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
445 /* check if size of stripped file is same as existing file */
446 if (bytesLeft != 0 && bytesLeft != asize)
447 {if (ftruncate(afd, bytesLeft) < 0)
448 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
454 #endif /* AFS_HPUX_ENV */
456 #include "AFS_component_version_number.c"
463 int setOwner, setMode, setGroup, ifd, ofd;
464 afs_int32 mode=0, owner, group;
467 char *fnames[MAXFILES], *newNames[MAXFILES];
468 afs_int32 rcode, code;
471 #if defined (AFS_HPUX_ENV)
472 char pnameBusy[1024];
473 #endif /* AFS_HPUX_ENV */
476 #if defined (AFS_AIX_ENV) || defined(AFS_FBSD_ENV)
478 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
480 char myHostName[100];
481 struct timeval tvp[2];
486 register afs_int32 i;
490 * The following signal action for AIX is necessary so that in case of a
491 * crash (i.e. core is generated) we can include the user's data section
492 * in the core dump. Unfortunately, by default, only a partial core is
493 * generated which, in many cases, isn't too useful.
495 struct sigaction nsa;
497 sigemptyset(&nsa.sa_mask);
498 nsa.sa_handler = SIG_DFL;
499 nsa.sa_flags = SA_FULLDUMP;
500 sigaction(SIGSEGV, &nsa, NULL);
504 strip = -1; /* don't know yet */
510 isDir = -1; /* don't know yet */
512 for(i=1; i<argc; i++)
516 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
517 else if (!strcmp(tp, "-s")) strip = 1;
518 else if (!strcmp(tp, "-ns")) strip = 0;
519 else if (!strcmp(tp, "-c")) /* nothing */;
520 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
521 else if (!strcmp(tp, "-o"))
522 {/* look up the dude */
523 tpw = getpwnam(argv[++i]);
525 {printf("User %s not found in passwd database, ignored\n", argv[i]);
528 {owner = tpw->pw_uid;
532 else if (!strcmp(tp, "-g"))
533 {/* look up the dude */
534 tgp = getgrnam(argv[++i]);
536 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
539 {group = tgp->gr_gid;
544 printf("Bad switch %s\n", argv[i]);
550 if (fptr >= MAXFILES)
551 {printf("Too many files on command line, max is %d\n", MAXFILES);
554 fnames[fptr++] = argv[i];
558 /* we've parse the commands, now *do* them */
560 /* otherwise we are doing a local install, so we do the work for each file
561 here the last name in the fname array is the dir in which to put all
565 {printf("Not enough file names\n");
569 /* N file usage requires last argument to be a directory. If -f was
570 specified it is an error. In the 2 file usage when -f is not specified
571 use a heuristic. If the ends of the two pathnames are equal then assume
572 the target is a file, otherwise assume it is a directory. */
574 if ((fptr > 2) && (isDir == 0)) {
575 printf ("target must be a directory, don't use multiple source files with -f switch\n");
578 else if (fptr > 2) isDir = 1;
579 else if (isDir != 0) {
583 targetSuffix = strrpbrk (fnames[1], "./");
584 sourceSuffix = strrpbrk (fnames[0], "./");
585 if (sourceSuffix == 0) {
586 sourceSuffix = fnames[0];
587 if (targetSuffix == 0) targetSuffix = fnames[1];
590 else if (targetSuffix == 0) targetSuffix = fnames[1];
591 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
594 dname = fnames[--fptr];
595 if (stat(dname, &istat) < 0) {
596 if ((errno == ENOENT) || (errno == ENOTDIR)) {
598 char protopath[BUFSIZ];
603 protopath[i] = dname[i];
604 c = dname[++i]; /* next char */
605 } while (!((c == 0) || (c == '/')));
608 /* don't mkdir last component if target is a file */
609 if ((c == 0) && (isDir == 0)) break;
611 /* else create dir component if it doesn't exist */
612 code = stat(protopath, &istat);
613 if (code && (errno == ENOENT)) {
614 code = mkdir (protopath, 0755);
616 printf ("Can't create destination path at %s\n",
621 } /* while dname not exhausted */
622 if (isDir == -1) isDir = 1;
625 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
629 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
633 /* either can be n files and one dir, or one file and one target */
634 if (!isDir && fptr != 1) {
635 printf("target for multiple files must be a dir\n");
640 {/* figure out name to put as entry name for file */
641 tp = strrchr(fnames[i], '/');
642 if (tp) newNames[i] = tp+1;
643 else newNames[i] = fnames[i];
646 {/* copy newName[i] into directory dname */
648 /* pname is target file in either case */
650 {strcpy(pname, dname);
652 strcat(pname, newNames[i]);
654 else strcpy(pname, dname);
655 strcpy(pnametmp, pname);
656 /* Make up a temporary name for a destination */
657 pnamelen = strlen(pnametmp);
658 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
659 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
660 pnametmp[pnamelen] = '.';
661 strcpy(&pnametmp[pnamelen+1], myHostName);
662 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
664 ifd = open(fnames[i], O_RDONLY, 0);
666 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
670 if (fstat (ifd, &istat) < 0) {
671 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
676 if (lstat(pname, &ostat) == 0) {
677 if ((ostat.st_size == istat.st_size) &&
678 (ostat.st_mtime == istat.st_mtime) &&
679 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
680 ((!setOwner) || (ostat.st_uid == owner)) &&
681 ((!setGroup) || (ostat.st_gid == group))) {
683 printf("No changes to %s since %s installed\n", fnames[i], pname);
687 #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)
690 ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])) && AIXobject(fnames[i])))
693 /* Simply copy target to dest */
694 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
696 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
697 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
698 close(ifd); unlink(pnametmp);
705 ofd = open(pnametmp, O_RDWR, 0);
707 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
712 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
713 #else /* AFS_AIX_ENV */
714 /* check to see if this file is hard to duplicate */
715 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
717 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
722 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
723 /* here both files are open and ready to go */
725 {code = read(ifd, diskBuffer, BUFSIZE);
726 if (code == 0) break;
728 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
732 newcode = write(ofd, diskBuffer, code);
734 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
739 {rcode = 1; /* an error occurred copying the file */
740 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
742 close(ifd); unlink(pnametmp); close(ofd);
743 continue; /* to the next file */
745 /* strip the file? */
747 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
748 if (quickStrip(ofd,istat.st_size) < 0) {
749 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
750 close(ifd); unlink(pnametmp);
755 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
758 #endif /* AFS_AIX_ENV */
759 if (fchmod(ofd, mode) < 0)
760 {printf("Couldn't chmod output temp file ``%s'': %s\n",
761 pnametmp, ErrorString(errno));
762 unlink(pnametmp); close(ofd);
767 tvp[0].tv_sec = istat.st_atime;
769 tvp[1].tv_sec = istat.st_mtime;
771 if (utimes(pnametmp, tvp) < 0)
772 {printf("Couldn't utimes output temp file ``%s'': %s\n",
773 pnametmp, ErrorString(errno));
774 unlink(pnametmp); close(ofd);
780 {printf("Warning: Could not close output temp file %s (%s)\n",
781 pnametmp, ErrorString(errno));
783 rcode = 1; /* an error occurred closing the output file */
784 continue; /* to the next file */
787 /* do this later so vice doesn't see chown of unstored file */
788 if (setOwner || setGroup)
789 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
790 printf("Couldn't set %s for output temp file %s: %s\n",
791 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
792 pnametmp, ErrorString(errno));
798 if (rename(pnametmp, pname) < 0) {
799 #if defined(AFS_HPUX_ENV)
800 if (errno == ETXTBSY) {
801 (void)strcpy(pnameBusy, pname);
802 (void)strcat(pnameBusy, ".BUSY");
803 if (rename(pname, pnameBusy) == 0) {
804 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
806 "Please delete this file when the program using it is finished.\n");
807 if (rename(pnametmp, pname) < 0) {
808 #endif /* AFS_HPUX_ENV */
810 printf("Couldn't rename temp file %s to be output file %s: %s\n",
811 pnametmp, pname, ErrorString(errno));
816 #if defined(AFS_HPUX_ENV)
821 "Couldn't move busy target file %s to make room for new version %s: %s\n",
822 pname, pnametmp, ErrorString(errno));
823 if (errno == ETXTBSY) {
825 "Try terminating any programs using the file %s and then re-run %s.\n",
833 #endif /* AFS_HPUX_ENV */