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_XBSD_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) || defined(AFS_OBSD_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)) {
209 * done the copy, now strip if desired.
214 switch (pid = fork()) {
222 #define STRIP_BIN "/usr/ccs/bin/strip"
223 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
224 #define STRIP_BIN "/usr/bin/strip"
226 #define STRIP_BIN "/bin/strip"
228 execve(STRIP_BIN, strip, (char **)0);
232 default: /* parent */
233 if (waitpid(pid, &status, 0) != pid) {
244 * AIXobject - lie about file type
250 * !0 indicating that the file in question is an XCOFF type file.
253 * Since /bin/strip will make that call for us, we will lie so that
266 * whoa! back up and be a little more rational (every little bit helps in
270 quickStrip (iname, oname, ignored, copy_only)
271 char *iname, *oname; {
273 static char *strip[] = {
276 static char *copy[] = {
281 * first, copy the `iname' to the `oname'
283 switch (pid = fork()) {
291 execve("/bin/cp", copy, 0);
295 default: /* parent */
296 if (waitpid(pid, &status, 0) != pid) {
303 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
308 * need to do a chmod to guarantee that the perms will permit
309 * the strip. Perms are fixed up later.
311 if (chmod(oname, 0700)) {
317 * done the copy, now strip if desired.
322 switch (pid = fork()) {
329 execve("/bin/strip", strip, 0);
330 perror("/bin/strip");
333 default: /* parent */
334 if (waitpid(pid, &status, 0) != pid) {
344 * AIXobject - lie about file type
350 * !0 indicating that the file in question is an XCOFF type file.
353 * Since /bin/strip will make that call for us, we will lie so that
360 #endif /* AFS_AIX32_ENV */
361 #else /* !AFS_AIX_ENV */
365 int quickStrip(fd, asize)
366 int fd; /* file descriptor */
367 afs_int32 asize; /* ignored */
371 int mysex, swapheader;
373 /* Read the file header, if it is one. */
374 if (lseek(fd, 0, L_SET) == -1) {
375 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
378 dum = read(fd, (char *) &fheader, sizeof(fheader));
379 /* Fail on I/O error */
381 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
384 /* If the file is smaller than a file header, forget it. */
385 if (dum != sizeof(fheader)) return 0;
386 #ifdef AFS_DECOSF_ENV
387 mysex = LITTLEENDIAN;
389 mysex = gethostsex();
390 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
393 if (fheader.f_magic == MIPSELMAGIC) {
394 if (mysex == BIGENDIAN) swapheader = 1;
395 } else if (fheader.f_magic == MIPSEBMAGIC) {
396 if (mysex == LITTLEENDIAN) swapheader = 1;
397 } else return 0; /* not executable */
398 #ifdef AFS_DECOSF_ENV
399 if (swapheader) return 0;
401 if (swapheader) swap_filehdr(&fheader, gethostsex());
403 /* Already stripped? */
404 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
405 /* Strip it. Zero out the symbol pointers. */
406 newlen = fheader.f_symptr;
407 fheader.f_symptr = 0;
409 #ifndef AFS_DECOSF_ENV
410 if (swapheader) swap_filehdr(&fheader, gethostsex());
412 if (lseek(fd, 0, L_SET) == -1) return -1;
413 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
414 /* Now truncate the file itself. */
415 if (ftruncate(fd, newlen) != 0) return -1;
419 static int quickStrip (afd, asize)
426 n = lseek(afd, 0, 0);
427 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
428 n = read(afd, &buf, sizeof(buf));
429 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
431 if (n >= sizeof(*head) && !N_BADMAG(*head))
432 {/* This code lifted from strip.c. */
433 bytesLeft = (afs_int32) head->a_text + head->a_data;
434 head->a_syms = head->a_trsize = head->a_drsize = 0;
435 if (head->a_magic == ZMAGIC)
436 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
437 /* also include size of header */
438 bytesLeft += sizeof(*head);
439 n = lseek(afd, 0, 0);
440 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
441 n = write(afd, &buf, sizeof(buf));
442 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
447 /* check if size of stripped file is same as existing file */
448 if (bytesLeft != 0 && bytesLeft != asize)
449 {if (ftruncate(afd, bytesLeft) < 0)
450 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
456 #endif /* AFS_HPUX_ENV */
458 #include "AFS_component_version_number.c"
465 int setOwner, setMode, setGroup, ifd, ofd;
466 afs_int32 mode=0, owner, group;
469 char *fnames[MAXFILES], *newNames[MAXFILES];
470 afs_int32 rcode, code;
473 #if defined (AFS_HPUX_ENV)
474 char pnameBusy[1024];
475 #endif /* AFS_HPUX_ENV */
478 #if defined (AFS_AIX_ENV) || defined(AFS_XBSD_ENV)
480 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
482 char myHostName[100];
483 struct timeval tvp[2];
488 register afs_int32 i;
492 * The following signal action for AIX is necessary so that in case of a
493 * crash (i.e. core is generated) we can include the user's data section
494 * in the core dump. Unfortunately, by default, only a partial core is
495 * generated which, in many cases, isn't too useful.
497 struct sigaction nsa;
499 sigemptyset(&nsa.sa_mask);
500 nsa.sa_handler = SIG_DFL;
501 nsa.sa_flags = SA_FULLDUMP;
502 sigaction(SIGSEGV, &nsa, NULL);
506 strip = -1; /* don't know yet */
512 isDir = -1; /* don't know yet */
514 for(i=1; i<argc; i++)
518 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
519 else if (!strcmp(tp, "-s")) strip = 1;
520 else if (!strcmp(tp, "-ns")) strip = 0;
521 else if (!strcmp(tp, "-c")) /* nothing */;
522 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
523 else if (!strcmp(tp, "-o"))
524 {/* look up the dude */
525 tpw = getpwnam(argv[++i]);
527 {printf("User %s not found in passwd database, ignored\n", argv[i]);
530 {owner = tpw->pw_uid;
534 else if (!strcmp(tp, "-g"))
535 {/* look up the dude */
536 tgp = getgrnam(argv[++i]);
538 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
541 {group = tgp->gr_gid;
546 printf("Bad switch %s\n", argv[i]);
552 if (fptr >= MAXFILES)
553 {printf("Too many files on command line, max is %d\n", MAXFILES);
556 fnames[fptr++] = argv[i];
560 /* we've parse the commands, now *do* them */
562 /* otherwise we are doing a local install, so we do the work for each file
563 here the last name in the fname array is the dir in which to put all
567 {printf("Not enough file names\n");
571 /* N file usage requires last argument to be a directory. If -f was
572 specified it is an error. In the 2 file usage when -f is not specified
573 use a heuristic. If the ends of the two pathnames are equal then assume
574 the target is a file, otherwise assume it is a directory. */
576 if ((fptr > 2) && (isDir == 0)) {
577 printf ("target must be a directory, don't use multiple source files with -f switch\n");
580 else if (fptr > 2) isDir = 1;
581 else if (isDir != 0) {
585 targetSuffix = strrpbrk (fnames[1], "./");
586 sourceSuffix = strrpbrk (fnames[0], "./");
587 if (sourceSuffix == 0) {
588 sourceSuffix = fnames[0];
589 if (targetSuffix == 0) targetSuffix = fnames[1];
592 else if (targetSuffix == 0) targetSuffix = fnames[1];
593 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
596 dname = fnames[--fptr];
597 if (stat(dname, &istat) < 0) {
598 if ((errno == ENOENT) || (errno == ENOTDIR)) {
600 char protopath[BUFSIZ];
605 protopath[i] = dname[i];
606 c = dname[++i]; /* next char */
607 } while (!((c == 0) || (c == '/')));
610 /* don't mkdir last component if target is a file */
611 if ((c == 0) && (isDir == 0)) break;
613 /* else create dir component if it doesn't exist */
614 code = stat(protopath, &istat);
615 if (code && (errno == ENOENT)) {
616 code = mkdir (protopath, 0755);
618 printf ("Can't create destination path at %s\n",
623 } /* while dname not exhausted */
624 if (isDir == -1) isDir = 1;
627 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
631 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
635 /* either can be n files and one dir, or one file and one target */
636 if (!isDir && fptr != 1) {
637 printf("target for multiple files must be a dir\n");
642 {/* figure out name to put as entry name for file */
643 tp = strrchr(fnames[i], '/');
644 if (tp) newNames[i] = tp+1;
645 else newNames[i] = fnames[i];
648 {/* copy newName[i] into directory dname */
650 /* pname is target file in either case */
652 {strcpy(pname, dname);
654 strcat(pname, newNames[i]);
656 else strcpy(pname, dname);
657 strcpy(pnametmp, pname);
658 /* Make up a temporary name for a destination */
659 pnamelen = strlen(pnametmp);
660 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
661 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
662 pnametmp[pnamelen] = '.';
663 strcpy(&pnametmp[pnamelen+1], myHostName);
664 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
666 ifd = open(fnames[i], O_RDONLY, 0);
668 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
672 if (fstat (ifd, &istat) < 0) {
673 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
678 if (lstat(pname, &ostat) == 0) {
679 if ((ostat.st_size == istat.st_size) &&
680 (ostat.st_mtime == istat.st_mtime) &&
681 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
682 ((!setOwner) || (ostat.st_uid == owner)) &&
683 ((!setGroup) || (ostat.st_gid == group))) {
685 printf("No changes to %s since %s installed\n", fnames[i], pname);
689 #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)
692 ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])) && AIXobject(fnames[i])))
695 /* Simply copy target to dest */
696 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
698 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
699 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
700 close(ifd); unlink(pnametmp);
707 ofd = open(pnametmp, O_RDWR, 0);
709 printf("Could not open 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 #else /* AFS_AIX_ENV */
716 /* check to see if this file is hard to duplicate */
717 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
719 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
724 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
725 /* here both files are open and ready to go */
727 {code = read(ifd, diskBuffer, BUFSIZE);
728 if (code == 0) break;
730 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
734 newcode = write(ofd, diskBuffer, code);
736 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
741 {rcode = 1; /* an error occurred copying the file */
742 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
744 close(ifd); unlink(pnametmp); close(ofd);
745 continue; /* to the next file */
747 /* strip the file? */
749 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
750 if (quickStrip(ofd,istat.st_size) < 0) {
751 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
752 close(ifd); unlink(pnametmp);
757 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
760 #endif /* AFS_AIX_ENV */
761 if (fchmod(ofd, mode) < 0)
762 {printf("Couldn't chmod output temp file ``%s'': %s\n",
763 pnametmp, ErrorString(errno));
764 unlink(pnametmp); close(ofd);
769 tvp[0].tv_sec = istat.st_atime;
771 tvp[1].tv_sec = istat.st_mtime;
773 if (utimes(pnametmp, tvp) < 0)
774 {printf("Couldn't utimes output temp file ``%s'': %s\n",
775 pnametmp, ErrorString(errno));
776 unlink(pnametmp); close(ofd);
782 {printf("Warning: Could not close output temp file %s (%s)\n",
783 pnametmp, ErrorString(errno));
785 rcode = 1; /* an error occurred closing the output file */
786 continue; /* to the next file */
789 /* do this later so vice doesn't see chown of unstored file */
790 if (setOwner || setGroup)
791 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
792 printf("Couldn't set %s for output temp file %s: %s\n",
793 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
794 pnametmp, ErrorString(errno));
800 if (rename(pnametmp, pname) < 0) {
801 #if defined(AFS_HPUX_ENV)
802 if (errno == ETXTBSY) {
803 (void)strcpy(pnameBusy, pname);
804 (void)strcat(pnameBusy, ".BUSY");
805 if (rename(pname, pnameBusy) == 0) {
806 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
808 "Please delete this file when the program using it is finished.\n");
809 if (rename(pnametmp, pname) < 0) {
810 #endif /* AFS_HPUX_ENV */
812 printf("Couldn't rename temp file %s to be output file %s: %s\n",
813 pnametmp, pname, ErrorString(errno));
818 #if defined(AFS_HPUX_ENV)
823 "Couldn't move busy target file %s to make room for new version %s: %s\n",
824 pname, pnametmp, ErrorString(errno));
825 if (errno == ETXTBSY) {
827 "Try terminating any programs using the file %s and then re-run %s.\n",
835 #endif /* AFS_HPUX_ENV */