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>
62 #include <sys/types.h>
91 static struct stat istat, ostat;
92 static int stripcalled = 0;
94 /* How many systems don't have strerror now? */
96 #if !defined(AFS_DARWIN60_ENV) && !defined(AFS_FBSD50_ENV)
99 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
100 extern char *sys_errlist[];
102 char *ErrorString(int aerrno);
104 #define ErrorString strerror
107 /* static prototypes */
108 int stripName(char *aname);
109 int atoo(register char *astr);
112 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
114 utimes(char *file, struct timeval tvp[2])
116 struct utimbuf times;
118 times.actime = tvp[0].tv_sec;
119 times.modtime = tvp[1].tv_sec;
120 return (utime(file, ×));
125 strrpbrk(char *s, char *set)
130 memset(sets, 0, sizeof(sets));
132 sets[(int)*set++] = 1;
135 if (sets[(int)s[--i]])
140 #ifndef HAVE_STRERROR
142 ErrorString(int aerrno)
144 static char tbuffer[100];
145 if (aerrno < 0 || aerrno >= sys_nerr) {
146 sprintf(tbuffer, "undefined error code %d", aerrno);
148 strcpy(tbuffer, sys_errlist[aerrno]);
155 stripName(char *aname)
157 if (strrchr(aname, '.') == 0)
164 atoo(register char *astr)
166 register afs_int32 value;
169 while ((tc = *astr++)) {
176 #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)
178 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
179 * reason why it wouldn't work.
182 quickStrip(char *iname, char *oname, int ignored, int copy_only)
186 static char *strip[] = {
189 static char *copy[] = {
194 * first, copy the `iname' to the `oname'
196 switch (pid = fork()) {
204 execve("/bin/cp", copy, NULL);
208 default: /* parent */
209 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
216 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
221 * need to do a chmod to guarantee that the perms will permit
222 * the strip. Perms are fixed up later.
224 if (chmod(oname, 0700)) {
228 #if !defined(AFS_OBSD_ENV) && !defined(AFS_NBSD_ENV)
230 * done the copy, now strip if desired.
235 switch (pid = fork()) {
243 #define STRIP_BIN "/usr/ccs/bin/strip"
244 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
245 #define STRIP_BIN "/usr/bin/strip"
247 #define STRIP_BIN "/bin/strip"
249 execve(STRIP_BIN, strip, NULL);
253 default: /* parent */
254 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
268 * whoa! back up and be a little more rational (every little bit helps in
272 quickStrip(char *iname, char *oname, int ignored, int copy_only)
275 static char *strip[] = {
278 static char *copy[] = {
283 * first, copy the `iname' to the `oname'
285 switch (pid = fork()) {
293 execve("/bin/cp", copy, 0);
297 default: /* parent */
298 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
305 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
310 * need to do a chmod to guarantee that the perms will permit
311 * the strip. Perms are fixed up later.
313 if (chmod(oname, 0700)) {
319 * done the copy, now strip if desired.
324 switch (pid = fork()) {
331 execve("/bin/strip", strip, 0);
332 perror("/bin/strip");
335 default: /* parent */
336 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
345 #endif /* AFS_AIX32_ENV */
346 #else /* !AFS_AIX_ENV */
351 quickStrip(int fd, afs_int32 asize)
355 int mysex, swapheader;
357 /* Read the file header, if it is one. */
358 if (lseek(fd, 0, L_SET) == -1) {
359 printf("Initial lseek failed while stripping file: %s\n",
363 dum = read(fd, (char *)&fheader, sizeof(fheader));
364 /* Fail on I/O error */
366 printf("Initial read failed while stripping: %s\n",
370 /* If the file is smaller than a file header, forget it. */
371 if (dum != sizeof(fheader))
373 #ifdef AFS_DECOSF_ENV
374 mysex = LITTLEENDIAN;
376 mysex = gethostsex();
377 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN)
381 if (fheader.f_magic == MIPSELMAGIC) {
382 if (mysex == BIGENDIAN)
384 } else if (fheader.f_magic == MIPSEBMAGIC) {
385 if (mysex == LITTLEENDIAN)
388 return 0; /* not executable */
389 #ifdef AFS_DECOSF_ENV
394 swap_filehdr(&fheader, gethostsex());
396 /* Already stripped? */
397 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0)
399 /* Strip it. Zero out the symbol pointers. */
400 newlen = fheader.f_symptr;
401 fheader.f_symptr = 0;
403 #ifndef AFS_DECOSF_ENV
405 swap_filehdr(&fheader, gethostsex());
407 if (lseek(fd, 0, L_SET) == -1)
409 if (write(fd, (char *)&fheader, sizeof(fheader)) != sizeof(fheader))
411 /* Now truncate the file itself. */
412 if (ftruncate(fd, newlen) != 0)
418 quickStrip(int afd, afs_int32 asize)
423 n = lseek(afd, 0, 0);
425 printf("Initial lseek failed while stripping file: %s\n",
429 n = read(afd, &buf, sizeof(buf));
431 printf("Initial read failed while stripping: %s\n",
436 if (n >= sizeof(*head) && !N_BADMAG(*head)) { /* This code lifted from strip.c. */
437 bytesLeft = (afs_int32) head->a_text + head->a_data;
438 head->a_syms = head->a_trsize = head->a_drsize = 0;
439 if (head->a_magic == ZMAGIC)
440 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
441 /* also include size of header */
442 bytesLeft += sizeof(*head);
443 n = lseek(afd, 0, 0);
445 printf("lseek failed while stripping file: %s\n",
449 n = write(afd, &buf, sizeof(buf));
451 printf("write failed while stripping file: %s\n",
458 /* check if size of stripped file is same as existing file */
459 if (bytesLeft != 0 && bytesLeft != asize) {
460 if (ftruncate(afd, bytesLeft) < 0) {
461 printf("ftruncate failed after stripping file: %s\n",
470 #endif /* AFS_HPUX_ENV */
472 #include "AFS_component_version_number.c"
475 main(int argc, char *argv[])
477 int setOwner, setMode, setGroup, ifd, ofd;
478 afs_int32 mode = 0, owner, group;
481 char *fnames[MAXFILES], *newNames[MAXFILES];
482 afs_int32 rcode, code;
485 #if defined (AFS_HPUX_ENV)
486 char pnameBusy[1024];
487 #endif /* AFS_HPUX_ENV */
491 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
492 char myHostName[100];
493 struct timeval tvp[2];
498 register afs_int32 i;
502 * The following signal action for AIX is necessary so that in case of a
503 * crash (i.e. core is generated) we can include the user's data section
504 * in the core dump. Unfortunately, by default, only a partial core is
505 * generated which, in many cases, isn't too useful.
507 struct sigaction nsa;
509 sigemptyset(&nsa.sa_mask);
510 nsa.sa_handler = SIG_DFL;
511 nsa.sa_flags = SA_FULLDUMP;
512 sigaction(SIGSEGV, &nsa, NULL);
516 strip = -1; /* don't know yet */
522 isDir = -1; /* don't know yet */
524 for (i = 1; i < argc; i++) {
526 if (tp[0] == '-') { /* a switch */
527 if (!strcmp(tp, "-m"))
528 mode = atoo(argv[++i]), setMode = 1;
529 else if (!strcmp(tp, "-s"))
531 else if (!strcmp(tp, "-ns"))
533 else if (!strcmp(tp, "-c")) /* nothing */
535 else if (!strcmp(tp, "-f"))
536 isDir = 0; /* => dest is file */
537 else if (!strcmp(tp, "-o")) { /* look up the dude */
538 tpw = getpwnam(argv[++i]);
540 printf("User %s not found in passwd database, ignored\n",
546 } else if (!strcmp(tp, "-g")) { /* look up the dude */
547 tgp = getgrnam(argv[++i]);
549 printf("Group %s not found in passwd database; ignored\n",
556 printf("Bad switch %s\n", argv[i]);
559 } else { /* a file name */
560 if (fptr >= MAXFILES) {
561 printf("Too many files on command line, max is %d\n",
565 fnames[fptr++] = argv[i];
569 /* we've parse the commands, now *do* them */
571 /* otherwise we are doing a local install, so we do the work for each file
572 * here the last name in the fname array is the dir in which to put all
576 printf("Not enough file names\n");
580 /* N file usage requires last argument to be a directory. If -f was
581 * specified it is an error. In the 2 file usage when -f is not specified
582 * use a heuristic. If the ends of the two pathnames are equal then assume
583 * the target is a file, otherwise assume it is a directory. */
585 if ((fptr > 2) && (isDir == 0)) {
587 ("target must be a directory, don't use multiple source files with -f switch\n");
591 else if (isDir != 0) {
595 targetSuffix = strrpbrk(fnames[1], "./");
596 sourceSuffix = strrpbrk(fnames[0], "./");
597 if (sourceSuffix == 0) {
598 sourceSuffix = fnames[0];
599 if (targetSuffix == 0)
600 targetSuffix = fnames[1];
603 } else if (targetSuffix == 0)
604 targetSuffix = fnames[1];
605 if (strcmp(targetSuffix, sourceSuffix) == 0)
609 dname = fnames[--fptr];
610 if (stat(dname, &istat) < 0) {
611 if ((errno == ENOENT) || (errno == ENOTDIR)) {
613 char protopath[BUFSIZ];
618 protopath[i] = dname[i];
619 c = dname[++i]; /* next char */
620 } while (!((c == 0) || (c == '/')));
623 /* don't mkdir last component if target is a file */
624 if ((c == 0) && (isDir == 0))
627 /* else create dir component if it doesn't exist */
628 code = stat(protopath, &istat);
629 if (code && (errno == ENOENT)) {
630 code = mkdir(protopath, 0755);
632 printf("Can't create destination path at %s\n",
637 } /* while dname not exhausted */
641 printf("Can't stat destination ``%s'': %s\n", dname,
646 if ((istat.st_mode & S_IFMT) == S_IFDIR)
652 /* either can be n files and one dir, or one file and one target */
653 if (!isDir && fptr != 1) {
654 printf("target for multiple files must be a dir\n");
658 for (i = 0; i < fptr; i++) { /* figure out name to put as entry name for file */
659 tp = strrchr(fnames[i], '/');
661 newNames[i] = tp + 1;
663 newNames[i] = fnames[i];
665 for (i = 0; i < fptr; i++) { /* copy newName[i] into directory dname */
667 /* pname is target file in either case */
669 strcpy(pname, dname);
671 strcat(pname, newNames[i]);
673 strcpy(pname, dname);
674 strcpy(pnametmp, pname);
675 /* Make up a temporary name for a destination */
676 pnamelen = strlen(pnametmp);
677 gethostname(myHostName, sizeof(myHostName) - 1); /* lv room for null */
678 if (pnamelen > 1020 - strlen(myHostName))
679 pnamelen = 1020 - strlen(myHostName);
680 pnametmp[pnamelen] = '.';
681 strcpy(&pnametmp[pnamelen + 1], myHostName);
682 if (strcmp(fnames[i], pnametmp) == 0)
683 strcpy(&pnametmp[pnamelen], ".NeW");
685 ifd = open(fnames[i], O_RDONLY, 0);
687 printf("Can't open source file ``%s'': %s\n", fnames[i],
692 if (fstat(ifd, &istat) < 0) {
693 printf("Cound not fstat input file ``%s'': %s; skipping it\n",
694 fnames[i], ErrorString(errno));
699 if (lstat(pname, &ostat) == 0) {
700 if ((ostat.st_size == istat.st_size)
701 && (ostat.st_mtime == istat.st_mtime) && ((!setMode)
706 && ((!setOwner) || (ostat.st_uid == owner)) && ((!setGroup)
711 printf("No changes to %s since %s installed\n", fnames[i],
716 #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)
718 if (strip == 1 || ((strip == -1 && ((istat.st_mode & 0111) == 0111)
719 && stripName(newNames[i]))))
722 /* Simply copy target to dest */
723 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
725 if (quickStrip(fnames[i], pnametmp, istat.st_size, 0) < 0) {
727 ("...strip failed for output temp file ``%s''; skipping it\n",
737 ofd = open(pnametmp, O_RDWR, 0);
739 printf("Could not open output temp file ``%s'': %s\n", pnametmp,
746 mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
747 #else /* AFS_AIX_ENV */
748 /* check to see if this file is hard to duplicate */
749 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
751 printf("Could not create output temp file ``%s'': %s\n", pnametmp,
758 mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
759 /* here both files are open and ready to go */
761 code = read(ifd, diskBuffer, BUFSIZE);
765 printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
769 newcode = write(ofd, diskBuffer, code);
770 if (newcode != code) {
771 printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
776 rcode = 1; /* an error occurred copying the file */
778 ("Warning: Error occurred writing output temp file %s; skipping it\n",
783 continue; /* to the next file */
785 /* strip the file? */
786 if (strip == 1 || (strip == -1 && ((istat.st_mode & 0111) == 0111)
787 && stripName(newNames[i])))
788 if (quickStrip(ofd, istat.st_size) < 0) {
790 ("...strip failed for output temp file ``%s''; skipping it\n",
798 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
801 #endif /* AFS_AIX_ENV */
802 if (fchmod(ofd, mode) < 0) {
803 printf("Couldn't chmod output temp file ``%s'': %s\n", pnametmp,
811 tvp[0].tv_sec = istat.st_atime;
813 tvp[1].tv_sec = istat.st_mtime;
815 if (utimes(pnametmp, tvp) < 0) {
816 printf("Couldn't utimes output temp file ``%s'': %s\n", pnametmp,
825 printf("Warning: Could not close output temp file %s (%s)\n",
826 pnametmp, ErrorString(errno));
828 rcode = 1; /* an error occurred closing the output file */
829 continue; /* to the next file */
832 /* do this later so vice doesn't see chown of unstored file */
833 if (setOwner || setGroup)
835 (pnametmp, (setOwner ? owner : -1),
836 (setGroup ? group : -1)) < 0) {
837 printf("Couldn't set %s for output temp file %s: %s\n",
838 (setOwner ? (setGroup ? "owner and group" : "owner") :
839 "group"), pnametmp, ErrorString(errno));
845 if (rename(pnametmp, pname) < 0) {
846 #if defined(AFS_HPUX_ENV)
847 if (errno == ETXTBSY) {
848 (void)strcpy(pnameBusy, pname);
849 (void)strcat(pnameBusy, ".BUSY");
850 if (rename(pname, pnameBusy) == 0) {
851 fprintf(stdout, "Had to leave old file: %s.\n",
854 "Please delete this file when the program using it is finished.\n");
855 if (rename(pnametmp, pname) < 0) {
856 #endif /* AFS_HPUX_ENV */
859 ("Couldn't rename temp file %s to be output file %s: %s\n",
860 pnametmp, pname, ErrorString(errno));
865 #if defined(AFS_HPUX_ENV)
869 "Couldn't move busy target file %s to make room for new version %s: %s\n",
870 pname, pnametmp, ErrorString(errno));
871 if (errno == ETXTBSY) {
873 "Try terminating any programs using the file %s and then re-run %s.\n",
881 #endif /* AFS_HPUX_ENV */