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>
90 static struct stat istat, ostat;
91 static int stripcalled = 0;
94 #if !defined(AFS_DARWIN60_ENV)
97 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
98 extern char *sys_errlist[];
101 /* static prototypes */
102 char *ErrorString(int aerrno);
103 int stripName(char *aname);
104 int atoo(register char *astr);
107 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
108 int utimes(char *file, struct timeval tvp[2])
110 struct utimbuf times;
112 times.actime = tvp[0].tv_sec;
113 times.modtime = tvp[1].tv_sec;
114 return (utime(file,×));
118 static char *strrpbrk (char *s, char *set)
123 memset(sets, 0, sizeof(sets));
124 while (*set) sets[(int) *set++] = 1;
126 while (i > 0) if (sets[(int)s[--i]]) return &s[i];
130 char *ErrorString(int aerrno)
132 static char tbuffer[100];
133 if (aerrno < 0 || aerrno >= sys_nerr) {
134 sprintf(tbuffer, "undefined error code %d", aerrno);
136 strcpy(tbuffer, sys_errlist[aerrno]);
141 int stripName(char *aname)
143 if (strrchr(aname, '.') == 0) return 1;
147 int atoo(register char *astr)
149 register afs_int32 value;
152 while ((tc = *astr++))
159 #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) || defined(AFS_NBSD_ENV)
161 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
162 * reason why it wouldn't work.
164 static int quickStrip (char *iname, char *oname, int ignored, int copy_only)
167 static char *strip[] = {
170 static char *copy[] = {
175 * first, copy the `iname' to the `oname'
177 switch (pid = fork()) {
185 execve("/bin/cp", copy, NULL);
189 default: /* parent */
190 if (waitpid(pid, &status, 0) != pid) {
197 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
202 * need to do a chmod to guarantee that the perms will permit
203 * the strip. Perms are fixed up later.
205 if (chmod(oname, 0700)) {
210 #if !defined(AFS_OBSD_ENV) && !defined(AFS_NBSD_ENV)
212 * done the copy, now strip if desired.
217 switch (pid = fork()) {
225 #define STRIP_BIN "/usr/ccs/bin/strip"
226 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
227 #define STRIP_BIN "/usr/bin/strip"
229 #define STRIP_BIN "/bin/strip"
231 execve(STRIP_BIN, strip, NULL);
235 default: /* parent */
236 if (waitpid(pid, &status, 0) != pid) {
250 * whoa! back up and be a little more rational (every little bit helps in
253 static int quickStrip (char *iname, char *oname, int ignored, int copy_only)
256 static char *strip[] = {
259 static char *copy[] = {
264 * first, copy the `iname' to the `oname'
266 switch (pid = fork()) {
274 execve("/bin/cp", copy, 0);
278 default: /* parent */
279 if (waitpid(pid, &status, 0) != pid) {
286 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
291 * need to do a chmod to guarantee that the perms will permit
292 * the strip. Perms are fixed up later.
294 if (chmod(oname, 0700)) {
300 * done the copy, now strip if desired.
305 switch (pid = fork()) {
312 execve("/bin/strip", strip, 0);
313 perror("/bin/strip");
316 default: /* parent */
317 if (waitpid(pid, &status, 0) != pid) {
326 #endif /* AFS_AIX32_ENV */
327 #else /* !AFS_AIX_ENV */
331 int quickStrip(int fd, afs_int32 asize)
335 int mysex, swapheader;
337 /* Read the file header, if it is one. */
338 if (lseek(fd, 0, L_SET) == -1) {
339 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
342 dum = read(fd, (char *) &fheader, sizeof(fheader));
343 /* Fail on I/O error */
345 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
348 /* If the file is smaller than a file header, forget it. */
349 if (dum != sizeof(fheader)) return 0;
350 #ifdef AFS_DECOSF_ENV
351 mysex = LITTLEENDIAN;
353 mysex = gethostsex();
354 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
357 if (fheader.f_magic == MIPSELMAGIC) {
358 if (mysex == BIGENDIAN) swapheader = 1;
359 } else if (fheader.f_magic == MIPSEBMAGIC) {
360 if (mysex == LITTLEENDIAN) swapheader = 1;
361 } else return 0; /* not executable */
362 #ifdef AFS_DECOSF_ENV
363 if (swapheader) return 0;
365 if (swapheader) swap_filehdr(&fheader, gethostsex());
367 /* Already stripped? */
368 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
369 /* Strip it. Zero out the symbol pointers. */
370 newlen = fheader.f_symptr;
371 fheader.f_symptr = 0;
373 #ifndef AFS_DECOSF_ENV
374 if (swapheader) swap_filehdr(&fheader, gethostsex());
376 if (lseek(fd, 0, L_SET) == -1) return -1;
377 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
378 /* Now truncate the file itself. */
379 if (ftruncate(fd, newlen) != 0) return -1;
383 static int quickStrip (int afd, afs_int32 asize)
388 n = lseek(afd, 0, 0);
389 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
390 n = read(afd, &buf, sizeof(buf));
391 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
393 if (n >= sizeof(*head) && !N_BADMAG(*head))
394 {/* This code lifted from strip.c. */
395 bytesLeft = (afs_int32) head->a_text + head->a_data;
396 head->a_syms = head->a_trsize = head->a_drsize = 0;
397 if (head->a_magic == ZMAGIC)
398 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
399 /* also include size of header */
400 bytesLeft += sizeof(*head);
401 n = lseek(afd, 0, 0);
402 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
403 n = write(afd, &buf, sizeof(buf));
404 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
409 /* check if size of stripped file is same as existing file */
410 if (bytesLeft != 0 && bytesLeft != asize)
411 {if (ftruncate(afd, bytesLeft) < 0)
412 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
418 #endif /* AFS_HPUX_ENV */
420 #include "AFS_component_version_number.c"
422 int main (int argc, char *argv[])
424 int setOwner, setMode, setGroup, ifd, ofd;
425 afs_int32 mode=0, owner, group;
428 char *fnames[MAXFILES], *newNames[MAXFILES];
429 afs_int32 rcode, code;
432 #if defined (AFS_HPUX_ENV)
433 char pnameBusy[1024];
434 #endif /* AFS_HPUX_ENV */
438 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
439 char myHostName[100];
440 struct timeval tvp[2];
445 register afs_int32 i;
449 * The following signal action for AIX is necessary so that in case of a
450 * crash (i.e. core is generated) we can include the user's data section
451 * in the core dump. Unfortunately, by default, only a partial core is
452 * generated which, in many cases, isn't too useful.
454 struct sigaction nsa;
456 sigemptyset(&nsa.sa_mask);
457 nsa.sa_handler = SIG_DFL;
458 nsa.sa_flags = SA_FULLDUMP;
459 sigaction(SIGSEGV, &nsa, NULL);
463 strip = -1; /* don't know yet */
469 isDir = -1; /* don't know yet */
471 for(i=1; i<argc; i++)
475 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
476 else if (!strcmp(tp, "-s")) strip = 1;
477 else if (!strcmp(tp, "-ns")) strip = 0;
478 else if (!strcmp(tp, "-c")) /* nothing */;
479 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
480 else if (!strcmp(tp, "-o"))
481 {/* look up the dude */
482 tpw = getpwnam(argv[++i]);
484 {printf("User %s not found in passwd database, ignored\n", argv[i]);
487 {owner = tpw->pw_uid;
491 else if (!strcmp(tp, "-g"))
492 {/* look up the dude */
493 tgp = getgrnam(argv[++i]);
495 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
498 {group = tgp->gr_gid;
503 printf("Bad switch %s\n", argv[i]);
509 if (fptr >= MAXFILES)
510 {printf("Too many files on command line, max is %d\n", MAXFILES);
513 fnames[fptr++] = argv[i];
517 /* we've parse the commands, now *do* them */
519 /* otherwise we are doing a local install, so we do the work for each file
520 here the last name in the fname array is the dir in which to put all
524 {printf("Not enough file names\n");
528 /* N file usage requires last argument to be a directory. If -f was
529 specified it is an error. In the 2 file usage when -f is not specified
530 use a heuristic. If the ends of the two pathnames are equal then assume
531 the target is a file, otherwise assume it is a directory. */
533 if ((fptr > 2) && (isDir == 0)) {
534 printf ("target must be a directory, don't use multiple source files with -f switch\n");
537 else if (fptr > 2) isDir = 1;
538 else if (isDir != 0) {
542 targetSuffix = strrpbrk (fnames[1], "./");
543 sourceSuffix = strrpbrk (fnames[0], "./");
544 if (sourceSuffix == 0) {
545 sourceSuffix = fnames[0];
546 if (targetSuffix == 0) targetSuffix = fnames[1];
549 else if (targetSuffix == 0) targetSuffix = fnames[1];
550 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
553 dname = fnames[--fptr];
554 if (stat(dname, &istat) < 0) {
555 if ((errno == ENOENT) || (errno == ENOTDIR)) {
557 char protopath[BUFSIZ];
562 protopath[i] = dname[i];
563 c = dname[++i]; /* next char */
564 } while (!((c == 0) || (c == '/')));
567 /* don't mkdir last component if target is a file */
568 if ((c == 0) && (isDir == 0)) break;
570 /* else create dir component if it doesn't exist */
571 code = stat(protopath, &istat);
572 if (code && (errno == ENOENT)) {
573 code = mkdir (protopath, 0755);
575 printf ("Can't create destination path at %s\n",
580 } /* while dname not exhausted */
581 if (isDir == -1) isDir = 1;
584 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
588 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
592 /* either can be n files and one dir, or one file and one target */
593 if (!isDir && fptr != 1) {
594 printf("target for multiple files must be a dir\n");
599 {/* figure out name to put as entry name for file */
600 tp = strrchr(fnames[i], '/');
601 if (tp) newNames[i] = tp+1;
602 else newNames[i] = fnames[i];
605 {/* copy newName[i] into directory dname */
607 /* pname is target file in either case */
609 {strcpy(pname, dname);
611 strcat(pname, newNames[i]);
613 else strcpy(pname, dname);
614 strcpy(pnametmp, pname);
615 /* Make up a temporary name for a destination */
616 pnamelen = strlen(pnametmp);
617 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
618 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
619 pnametmp[pnamelen] = '.';
620 strcpy(&pnametmp[pnamelen+1], myHostName);
621 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
623 ifd = open(fnames[i], O_RDONLY, 0);
625 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
629 if (fstat (ifd, &istat) < 0) {
630 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
635 if (lstat(pname, &ostat) == 0) {
636 if ((ostat.st_size == istat.st_size) &&
637 (ostat.st_mtime == istat.st_mtime) &&
638 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
639 ((!setOwner) || (ostat.st_uid == owner)) &&
640 ((!setGroup) || (ostat.st_gid == group))) {
642 printf("No changes to %s since %s installed\n", fnames[i], pname);
646 #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) || defined(AFS_NBSD_ENV)
649 ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i]))))
652 /* Simply copy target to dest */
653 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
655 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
656 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
657 close(ifd); unlink(pnametmp);
664 ofd = open(pnametmp, O_RDWR, 0);
666 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
671 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
672 #else /* AFS_AIX_ENV */
673 /* check to see if this file is hard to duplicate */
674 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
676 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
681 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
682 /* here both files are open and ready to go */
684 {code = read(ifd, diskBuffer, BUFSIZE);
685 if (code == 0) break;
687 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
691 newcode = write(ofd, diskBuffer, code);
693 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
698 {rcode = 1; /* an error occurred copying the file */
699 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
701 close(ifd); unlink(pnametmp); close(ofd);
702 continue; /* to the next file */
704 /* strip the file? */
706 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
707 if (quickStrip(ofd,istat.st_size) < 0) {
708 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
709 close(ifd); unlink(pnametmp);
714 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
717 #endif /* AFS_AIX_ENV */
718 if (fchmod(ofd, mode) < 0)
719 {printf("Couldn't chmod output temp file ``%s'': %s\n",
720 pnametmp, ErrorString(errno));
721 unlink(pnametmp); close(ofd);
726 tvp[0].tv_sec = istat.st_atime;
728 tvp[1].tv_sec = istat.st_mtime;
730 if (utimes(pnametmp, tvp) < 0)
731 {printf("Couldn't utimes output temp file ``%s'': %s\n",
732 pnametmp, ErrorString(errno));
733 unlink(pnametmp); close(ofd);
739 {printf("Warning: Could not close output temp file %s (%s)\n",
740 pnametmp, ErrorString(errno));
742 rcode = 1; /* an error occurred closing the output file */
743 continue; /* to the next file */
746 /* do this later so vice doesn't see chown of unstored file */
747 if (setOwner || setGroup)
748 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
749 printf("Couldn't set %s for output temp file %s: %s\n",
750 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
751 pnametmp, ErrorString(errno));
757 if (rename(pnametmp, pname) < 0) {
758 #if defined(AFS_HPUX_ENV)
759 if (errno == ETXTBSY) {
760 (void)strcpy(pnameBusy, pname);
761 (void)strcat(pnameBusy, ".BUSY");
762 if (rename(pname, pnameBusy) == 0) {
763 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
765 "Please delete this file when the program using it is finished.\n");
766 if (rename(pnametmp, pname) < 0) {
767 #endif /* AFS_HPUX_ENV */
769 printf("Couldn't rename temp file %s to be output file %s: %s\n",
770 pnametmp, pname, ErrorString(errno));
775 #if defined(AFS_HPUX_ENV)
780 "Couldn't move busy target file %s to make room for new version %s: %s\n",
781 pname, pnametmp, ErrorString(errno));
782 if (errno == ETXTBSY) {
784 "Try terminating any programs using the file %s and then re-run %s.\n",
792 #endif /* AFS_HPUX_ENV */