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 ******************************************************************/
34 /* ALSO utimes and strip the file
36 Generic install command. Options are:
37 -s strip the file (default for executable files with no extension)
38 -ns do not strip the file (default for other files)
39 -c ignored for compatability
40 -m <mode> chmod to this value
41 -o <user> chown to this user
42 -g <group> chgrp to this group
43 -f target path is a file
44 -q be very, very quick and quiet
45 -l <envcwd> attempt symbolic link back from destination to source
46 with the current directory in the specified environment
52 #include <afs/param.h>
60 #include <sys/types.h>
81 struct stat istat, ostat;
85 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD40_ENV)
86 extern char *sys_errlist[];
88 #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)
89 extern struct passwd *getpwnam();
94 extern char *rindex(); /* this should always be defined, shouldn't it? */
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 bzero (sets, sizeof(sets));
118 while (*set) sets[*set++] = 1;
120 while (i > 0) if (sets[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);
131 return sys_errlist[aerrno];
136 {if (rindex(aname, '.') == 0) return 1;
142 {register afs_int32 value;
152 #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)
154 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
155 * reason why it wouldn't work.
158 quickStrip (iname, oname, ignored, copy_only)
159 char *iname, *oname; {
161 static char *strip[] = {
164 static char *copy[] = {
169 * first, copy the `iname' to the `oname'
171 switch (pid = fork()) {
179 execve("/bin/cp", copy, (char **)0);
183 default: /* parent */
184 if (waitpid(pid, &status, 0) != pid) {
191 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
196 * need to do a chmod to guarantee that the perms will permit
197 * the strip. Perms are fixed up later.
199 if (chmod(oname, 0700)) {
205 * done the copy, now strip if desired.
210 switch (pid = fork()) {
218 #define STRIP_BIN "/usr/ccs/bin/strip"
219 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV)
220 #define STRIP_BIN "/usr/bin/strip"
222 #define STRIP_BIN "/bin/strip"
224 execve(STRIP_BIN, strip, (char **)0);
228 default: /* parent */
229 if (waitpid(pid, &status, 0) != pid) {
239 * AIXobject - lie about file type
245 * !0 indicating that the file in question is an XCOFF type file.
248 * Since /bin/strip will make that call for us, we will lie so that
260 * whoa! back up and be a little more rational (every little bit helps in
264 quickStrip (iname, oname, ignored, copy_only)
265 char *iname, *oname; {
267 static char *strip[] = {
270 static char *copy[] = {
275 * first, copy the `iname' to the `oname'
277 switch (pid = fork()) {
285 execve("/bin/cp", copy, 0);
289 default: /* parent */
290 if (waitpid(pid, &status, 0) != pid) {
297 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
302 * need to do a chmod to guarantee that the perms will permit
303 * the strip. Perms are fixed up later.
305 if (chmod(oname, 0700)) {
311 * done the copy, now strip if desired.
316 switch (pid = fork()) {
323 execve("/bin/strip", strip, 0);
324 perror("/bin/strip");
327 default: /* parent */
328 if (waitpid(pid, &status, 0) != pid) {
338 * AIXobject - lie about file type
344 * !0 indicating that the file in question is an XCOFF type file.
347 * Since /bin/strip will make that call for us, we will lie so that
354 #endif /* AFS_AIX32_ENV */
355 #else /* !AFS_AIX_ENV */
359 int quickStrip(fd, asize)
360 int fd; /* file descriptor */
361 afs_int32 asize; /* ignored */
365 int mysex, swapheader;
367 /* Read the file header, if it is one. */
368 if (lseek(fd, 0, L_SET) == -1) {
369 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
372 dum = read(fd, (char *) &fheader, sizeof(fheader));
373 /* Fail on I/O error */
375 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
378 /* If the file is smaller than a file header, forget it. */
379 if (dum != sizeof(fheader)) return 0;
380 #ifdef AFS_DECOSF_ENV
381 mysex = LITTLEENDIAN;
383 mysex = gethostsex();
384 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
387 if (fheader.f_magic == MIPSELMAGIC) {
388 if (mysex == BIGENDIAN) swapheader = 1;
389 } else if (fheader.f_magic == MIPSEBMAGIC) {
390 if (mysex == LITTLEENDIAN) swapheader = 1;
391 } else return 0; /* not executable */
392 #ifdef AFS_DECOSF_ENV
393 if (swapheader) return 0;
395 if (swapheader) swap_filehdr(&fheader, gethostsex());
397 /* Already stripped? */
398 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
399 /* Strip it. Zero out the symbol pointers. */
400 newlen = fheader.f_symptr;
401 fheader.f_symptr = 0;
403 #ifndef AFS_DECOSF_ENV
404 if (swapheader) swap_filehdr(&fheader, gethostsex());
406 if (lseek(fd, 0, L_SET) == -1) return -1;
407 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
408 /* Now truncate the file itself. */
409 if (ftruncate(fd, newlen) != 0) return -1;
413 static int quickStrip (afd, asize)
420 n = lseek(afd, 0, 0);
421 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
422 n = read(afd, &buf, sizeof(buf));
423 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
425 if (n >= sizeof(*head) && !N_BADMAG(*head))
426 {/* This code lifted from strip.c. */
427 bytesLeft = (afs_int32) head->a_text + head->a_data;
428 head->a_syms = head->a_trsize = head->a_drsize = 0;
429 if (head->a_magic == ZMAGIC)
430 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
431 /* also include size of header */
432 bytesLeft += sizeof(*head);
433 n = lseek(afd, 0, 0);
434 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
435 n = write(afd, &buf, sizeof(buf));
436 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
441 /* check if size of stripped file is same as existing file */
442 if (bytesLeft != 0 && bytesLeft != asize)
443 {if (ftruncate(afd, bytesLeft) < 0)
444 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
450 #endif /* AFS_HPUX_ENV */
452 #include "AFS_component_version_number.c"
458 int setOwner, setMode, setGroup, ifd, ofd;
459 afs_int32 mode, owner, group;
462 char *fnames[MAXFILES], *newNames[MAXFILES];
463 afs_int32 rcode, code, newcode;
466 #if defined (AFS_HPUX_ENV)
467 char pnameBusy[1024];
468 #endif /* AFS_HPUX_ENV */
471 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
472 char myHostName[100];
473 struct timeval tvp[2];
478 register afs_int32 i;
482 * The following signal action for AIX is necessary so that in case of a
483 * crash (i.e. core is generated) we can include the user's data section
484 * in the core dump. Unfortunately, by default, only a partial core is
485 * generated which, in many cases, isn't too useful.
487 struct sigaction nsa;
489 sigemptyset(&nsa.sa_mask);
490 nsa.sa_handler = SIG_DFL;
491 nsa.sa_flags = SA_FULLDUMP;
492 sigaction(SIGSEGV, &nsa, NULL);
496 strip = -1; /* don't know yet */
502 isDir = -1; /* don't know yet */
504 for(i=1; i<argc; i++)
508 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
509 else if (!strcmp(tp, "-s")) strip = 1;
510 else if (!strcmp(tp, "-ns")) strip = 0;
511 else if (!strcmp(tp, "-c")) /* nothing */;
512 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
513 else if (!strcmp(tp, "-o"))
514 {/* look up the dude */
515 tpw = getpwnam(argv[++i]);
517 {printf("User %s not found in passwd database, ignored\n", argv[i]);
520 {owner = tpw->pw_uid;
524 else if (!strcmp(tp, "-g"))
525 {/* look up the dude */
526 tgp = getgrnam(argv[++i]);
528 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
531 {group = tgp->gr_gid;
536 printf("Bad switch %s\n", argv[i]);
542 if (fptr >= MAXFILES)
543 {printf("Too many files on command line, max is %d\n", MAXFILES);
546 fnames[fptr++] = argv[i];
550 /* we've parse the commands, now *do* them */
552 /* otherwise we are doing a local install, so we do the work for each file
553 here the last name in the fname array is the dir in which to put all
557 {printf("Not enough file names\n");
561 /* N file usage requires last argument to be a directory. If -f was
562 specified it is an error. In the 2 file usage when -f is not specified
563 use a heuristic. If the ends of the two pathnames are equal then assume
564 the target is a file, otherwise assume it is a directory. */
566 if ((fptr > 2) && (isDir == 0)) {
567 printf ("target must be a directory, don't use multiple source files with -f switch\n");
570 else if (fptr > 2) isDir = 1;
571 else if (isDir != 0) {
575 targetSuffix = strrpbrk (fnames[1], "./");
576 sourceSuffix = strrpbrk (fnames[0], "./");
577 if (sourceSuffix == 0) {
578 sourceSuffix = fnames[0];
579 if (targetSuffix == 0) targetSuffix = fnames[1];
582 else if (targetSuffix == 0) targetSuffix = fnames[1];
583 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
586 dname = fnames[--fptr];
587 if (stat(dname, &istat) < 0) {
588 if ((errno == ENOENT) || (errno == ENOTDIR)) {
590 char protopath[BUFSIZ];
595 protopath[i] = dname[i];
596 c = dname[++i]; /* next char */
597 } while (!((c == 0) || (c == '/')));
600 /* don't mkdir last component if target is a file */
601 if ((c == 0) && (isDir == 0)) break;
603 /* else create dir component if it doesn't exist */
604 code = stat(protopath, &istat);
605 if (code && (errno == ENOENT)) {
606 code = mkdir (protopath, 0755);
608 printf ("Can't create destination path at %s\n",
613 } /* while dname not exhausted */
614 if (isDir == -1) isDir = 1;
617 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
621 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
625 /* either can be n files and one dir, or one file and one target */
626 if (!isDir && fptr != 1) {
627 printf("target for multiple files must be a dir\n");
632 {/* figure out name to put as entry name for file */
633 tp = rindex(fnames[i], '/');
634 if (tp) newNames[i] = tp+1;
635 else newNames[i] = fnames[i];
638 {/* copy newName[i] into directory dname */
640 /* pname is target file in either case */
642 {strcpy(pname, dname);
644 strcat(pname, newNames[i]);
646 else strcpy(pname, dname);
647 strcpy(pnametmp, pname);
648 /* Make up a temporary name for a destination */
649 pnamelen = strlen(pnametmp);
650 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
651 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
652 pnametmp[pnamelen] = '.';
653 strcpy(&pnametmp[pnamelen+1], myHostName);
654 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
656 ifd = open(fnames[i], O_RDONLY, 0);
658 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
662 if (fstat (ifd, &istat) < 0) {
663 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
668 if (lstat(pname, &ostat) == 0) {
669 if ((ostat.st_size == istat.st_size) &&
670 (ostat.st_mtime == istat.st_mtime) &&
671 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
672 ((!setOwner) || (ostat.st_uid == owner)) &&
673 ((!setGroup) || (ostat.st_gid == group))) {
675 printf("No changes to %s since %s installed\n", fnames[i], pname);
679 #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)
682 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])) && AIXobject(fnames[i]))
685 /* Simply copy target to dest */
686 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
688 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
689 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
690 close(ifd); unlink(pnametmp);
697 ofd = open(pnametmp, O_RDWR, 0);
699 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
704 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
705 #else /* AFS_AIX_ENV */
706 /* check to see if this file is hard to duplicate */
707 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
709 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
714 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
715 /* here both files are open and ready to go */
717 {code = read(ifd, diskBuffer, BUFSIZE);
718 if (code == 0) break;
720 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
724 newcode = write(ofd, diskBuffer, code);
726 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
731 {rcode = 1; /* an error occurred copying the file */
732 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
734 close(ifd); unlink(pnametmp); close(ofd);
735 continue; /* to the next file */
737 /* strip the file? */
739 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
740 if (quickStrip(ofd,istat.st_size) < 0) {
741 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
742 close(ifd); unlink(pnametmp);
747 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
750 #endif /* AFS_AIX_ENV */
751 if (fchmod(ofd, mode) < 0)
752 {printf("Couldn't chmod output temp file ``%s'': %s\n",
753 pnametmp, ErrorString(errno));
754 unlink(pnametmp); close(ofd);
759 tvp[0].tv_sec = istat.st_atime;
761 tvp[1].tv_sec = istat.st_mtime;
763 if (utimes(pnametmp, tvp) < 0)
764 {printf("Couldn't utimes output temp file ``%s'': %s\n",
765 pnametmp, ErrorString(errno));
766 unlink(pnametmp); close(ofd);
772 {printf("Warning: Could not close output temp file %s (%s)\n",
773 pnametmp, ErrorString(errno));
775 rcode = 1; /* an error occurred closing the output file */
776 continue; /* to the next file */
779 /* do this later so vice doesn't see chown of unstored file */
780 if (setOwner || setGroup)
781 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
782 printf("Couldn't set %s for output temp file %s: %s\n",
783 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
784 pnametmp, ErrorString(errno));
790 if (rename(pnametmp, pname) < 0) {
791 #if defined(AFS_HPUX_ENV)
792 if (errno == ETXTBSY) {
793 (void)strcpy(pnameBusy, pname);
794 (void)strcat(pnameBusy, ".BUSY");
795 if (rename(pname, pnameBusy) == 0) {
796 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
798 "Please delete this file when the program using it is finished.\n");
799 if (rename(pnametmp, pname) < 0) {
800 #endif /* AFS_HPUX_ENV */
802 printf("Couldn't rename temp file %s to be output file %s: %s\n",
803 pnametmp, pname, ErrorString(errno));
808 #if defined(AFS_HPUX_ENV)
813 "Couldn't move busy target file %s to make room for new version %s: %s\n",
814 pname, pnametmp, ErrorString(errno));
815 if (errno == ETXTBSY) {
817 "Try terminating any programs using the file %s and then re-run %s.\n",
825 #endif /* AFS_HPUX_ENV */