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 #if !defined(AFS_DARWIN60_ENV) && !defined(AFS_FBSD50_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)
109 utimes(char *file, struct timeval tvp[2])
111 struct utimbuf times;
113 times.actime = tvp[0].tv_sec;
114 times.modtime = tvp[1].tv_sec;
115 return (utime(file, ×));
120 strrpbrk(char *s, char *set)
125 memset(sets, 0, sizeof(sets));
127 sets[(int)*set++] = 1;
130 if (sets[(int)s[--i]])
136 ErrorString(int aerrno)
138 static char tbuffer[100];
139 if (aerrno < 0 || aerrno >= sys_nerr) {
140 sprintf(tbuffer, "undefined error code %d", aerrno);
142 strcpy(tbuffer, sys_errlist[aerrno]);
148 stripName(char *aname)
150 if (strrchr(aname, '.') == 0)
157 atoo(register char *astr)
159 register afs_int32 value;
162 while ((tc = *astr++)) {
169 #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)
171 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
172 * reason why it wouldn't work.
175 quickStrip(char *iname, char *oname, int ignored, int copy_only)
179 static char *strip[] = {
182 static char *copy[] = {
187 * first, copy the `iname' to the `oname'
189 switch (pid = fork()) {
197 execve("/bin/cp", copy, NULL);
201 default: /* parent */
202 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
209 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
214 * need to do a chmod to guarantee that the perms will permit
215 * the strip. Perms are fixed up later.
217 if (chmod(oname, 0700)) {
221 #if !defined(AFS_OBSD_ENV) && !defined(AFS_NBSD_ENV)
223 * done the copy, now strip if desired.
228 switch (pid = fork()) {
236 #define STRIP_BIN "/usr/ccs/bin/strip"
237 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
238 #define STRIP_BIN "/usr/bin/strip"
240 #define STRIP_BIN "/bin/strip"
242 execve(STRIP_BIN, strip, NULL);
246 default: /* parent */
247 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
261 * whoa! back up and be a little more rational (every little bit helps in
265 quickStrip(char *iname, char *oname, int ignored, int copy_only)
268 static char *strip[] = {
271 static char *copy[] = {
276 * first, copy the `iname' to the `oname'
278 switch (pid = fork()) {
286 execve("/bin/cp", copy, 0);
290 default: /* parent */
291 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
298 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
303 * need to do a chmod to guarantee that the perms will permit
304 * the strip. Perms are fixed up later.
306 if (chmod(oname, 0700)) {
312 * done the copy, now strip if desired.
317 switch (pid = fork()) {
324 execve("/bin/strip", strip, 0);
325 perror("/bin/strip");
328 default: /* parent */
329 if (waitpid(pid, &status, 0) != pid && errno != ECHILD) {
338 #endif /* AFS_AIX32_ENV */
339 #else /* !AFS_AIX_ENV */
344 quickStrip(int fd, afs_int32 asize)
348 int mysex, swapheader;
350 /* Read the file header, if it is one. */
351 if (lseek(fd, 0, L_SET) == -1) {
352 printf("Initial lseek failed while stripping file: %s\n",
356 dum = read(fd, (char *)&fheader, sizeof(fheader));
357 /* Fail on I/O error */
359 printf("Initial read failed while stripping: %s\n",
363 /* If the file is smaller than a file header, forget it. */
364 if (dum != sizeof(fheader))
366 #ifdef AFS_DECOSF_ENV
367 mysex = LITTLEENDIAN;
369 mysex = gethostsex();
370 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN)
374 if (fheader.f_magic == MIPSELMAGIC) {
375 if (mysex == BIGENDIAN)
377 } else if (fheader.f_magic == MIPSEBMAGIC) {
378 if (mysex == LITTLEENDIAN)
381 return 0; /* not executable */
382 #ifdef AFS_DECOSF_ENV
387 swap_filehdr(&fheader, gethostsex());
389 /* Already stripped? */
390 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0)
392 /* Strip it. Zero out the symbol pointers. */
393 newlen = fheader.f_symptr;
394 fheader.f_symptr = 0;
396 #ifndef AFS_DECOSF_ENV
398 swap_filehdr(&fheader, gethostsex());
400 if (lseek(fd, 0, L_SET) == -1)
402 if (write(fd, (char *)&fheader, sizeof(fheader)) != sizeof(fheader))
404 /* Now truncate the file itself. */
405 if (ftruncate(fd, newlen) != 0)
411 quickStrip(int afd, afs_int32 asize)
416 n = lseek(afd, 0, 0);
418 printf("Initial lseek failed while stripping file: %s\n",
422 n = read(afd, &buf, sizeof(buf));
424 printf("Initial read failed while stripping: %s\n",
429 if (n >= sizeof(*head) && !N_BADMAG(*head)) { /* This code lifted from strip.c. */
430 bytesLeft = (afs_int32) head->a_text + head->a_data;
431 head->a_syms = head->a_trsize = head->a_drsize = 0;
432 if (head->a_magic == ZMAGIC)
433 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
434 /* also include size of header */
435 bytesLeft += sizeof(*head);
436 n = lseek(afd, 0, 0);
438 printf("lseek failed while stripping file: %s\n",
442 n = write(afd, &buf, sizeof(buf));
444 printf("write failed while stripping file: %s\n",
451 /* check if size of stripped file is same as existing file */
452 if (bytesLeft != 0 && bytesLeft != asize) {
453 if (ftruncate(afd, bytesLeft) < 0) {
454 printf("ftruncate failed after stripping file: %s\n",
463 #endif /* AFS_HPUX_ENV */
465 #include "AFS_component_version_number.c"
468 main(int argc, char *argv[])
470 int setOwner, setMode, setGroup, ifd, ofd;
471 afs_int32 mode = 0, owner, group;
474 char *fnames[MAXFILES], *newNames[MAXFILES];
475 afs_int32 rcode, code;
478 #if defined (AFS_HPUX_ENV)
479 char pnameBusy[1024];
480 #endif /* AFS_HPUX_ENV */
484 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
485 char myHostName[100];
486 struct timeval tvp[2];
491 register afs_int32 i;
495 * The following signal action for AIX is necessary so that in case of a
496 * crash (i.e. core is generated) we can include the user's data section
497 * in the core dump. Unfortunately, by default, only a partial core is
498 * generated which, in many cases, isn't too useful.
500 struct sigaction nsa;
502 sigemptyset(&nsa.sa_mask);
503 nsa.sa_handler = SIG_DFL;
504 nsa.sa_flags = SA_FULLDUMP;
505 sigaction(SIGSEGV, &nsa, NULL);
509 strip = -1; /* don't know yet */
515 isDir = -1; /* don't know yet */
517 for (i = 1; i < argc; i++) {
519 if (tp[0] == '-') { /* a switch */
520 if (!strcmp(tp, "-m"))
521 mode = atoo(argv[++i]), setMode = 1;
522 else if (!strcmp(tp, "-s"))
524 else if (!strcmp(tp, "-ns"))
526 else if (!strcmp(tp, "-c")) /* nothing */
528 else if (!strcmp(tp, "-f"))
529 isDir = 0; /* => dest is file */
530 else if (!strcmp(tp, "-o")) { /* look up the dude */
531 tpw = getpwnam(argv[++i]);
533 printf("User %s not found in passwd database, ignored\n",
539 } else if (!strcmp(tp, "-g")) { /* look up the dude */
540 tgp = getgrnam(argv[++i]);
542 printf("Group %s not found in passwd database; ignored\n",
549 printf("Bad switch %s\n", argv[i]);
552 } else { /* a file name */
553 if (fptr >= MAXFILES) {
554 printf("Too many files on command line, max is %d\n",
558 fnames[fptr++] = argv[i];
562 /* we've parse the commands, now *do* them */
564 /* otherwise we are doing a local install, so we do the work for each file
565 * here the last name in the fname array is the dir in which to put all
569 printf("Not enough file names\n");
573 /* N file usage requires last argument to be a directory. If -f was
574 * specified it is an error. In the 2 file usage when -f is not specified
575 * use a heuristic. If the ends of the two pathnames are equal then assume
576 * the target is a file, otherwise assume it is a directory. */
578 if ((fptr > 2) && (isDir == 0)) {
580 ("target must be a directory, don't use multiple source files with -f switch\n");
584 else if (isDir != 0) {
588 targetSuffix = strrpbrk(fnames[1], "./");
589 sourceSuffix = strrpbrk(fnames[0], "./");
590 if (sourceSuffix == 0) {
591 sourceSuffix = fnames[0];
592 if (targetSuffix == 0)
593 targetSuffix = fnames[1];
596 } else if (targetSuffix == 0)
597 targetSuffix = fnames[1];
598 if (strcmp(targetSuffix, sourceSuffix) == 0)
602 dname = fnames[--fptr];
603 if (stat(dname, &istat) < 0) {
604 if ((errno == ENOENT) || (errno == ENOTDIR)) {
606 char protopath[BUFSIZ];
611 protopath[i] = dname[i];
612 c = dname[++i]; /* next char */
613 } while (!((c == 0) || (c == '/')));
616 /* don't mkdir last component if target is a file */
617 if ((c == 0) && (isDir == 0))
620 /* else create dir component if it doesn't exist */
621 code = stat(protopath, &istat);
622 if (code && (errno == ENOENT)) {
623 code = mkdir(protopath, 0755);
625 printf("Can't create destination path at %s\n",
630 } /* while dname not exhausted */
634 printf("Can't stat destination ``%s'': %s\n", dname,
639 if ((istat.st_mode & S_IFMT) == S_IFDIR)
645 /* either can be n files and one dir, or one file and one target */
646 if (!isDir && fptr != 1) {
647 printf("target for multiple files must be a dir\n");
651 for (i = 0; i < fptr; i++) { /* figure out name to put as entry name for file */
652 tp = strrchr(fnames[i], '/');
654 newNames[i] = tp + 1;
656 newNames[i] = fnames[i];
658 for (i = 0; i < fptr; i++) { /* copy newName[i] into directory dname */
660 /* pname is target file in either case */
662 strcpy(pname, dname);
664 strcat(pname, newNames[i]);
666 strcpy(pname, dname);
667 strcpy(pnametmp, pname);
668 /* Make up a temporary name for a destination */
669 pnamelen = strlen(pnametmp);
670 gethostname(myHostName, sizeof(myHostName) - 1); /* lv room for null */
671 if (pnamelen > 1020 - strlen(myHostName))
672 pnamelen = 1020 - strlen(myHostName);
673 pnametmp[pnamelen] = '.';
674 strcpy(&pnametmp[pnamelen + 1], myHostName);
675 if (strcmp(fnames[i], pnametmp) == 0)
676 strcpy(&pnametmp[pnamelen], ".NeW");
678 ifd = open(fnames[i], O_RDONLY, 0);
680 printf("Can't open source file ``%s'': %s\n", fnames[i],
685 if (fstat(ifd, &istat) < 0) {
686 printf("Cound not fstat input file ``%s'': %s; skipping it\n",
687 fnames[i], ErrorString(errno));
692 if (lstat(pname, &ostat) == 0) {
693 if ((ostat.st_size == istat.st_size)
694 && (ostat.st_mtime == istat.st_mtime) && ((!setMode)
699 && ((!setOwner) || (ostat.st_uid == owner)) && ((!setGroup)
704 printf("No changes to %s since %s installed\n", fnames[i],
709 #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)
711 if (strip == 1 || ((strip == -1 && ((istat.st_mode & 0111) == 0111)
712 && stripName(newNames[i]))))
715 /* Simply copy target to dest */
716 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
718 if (quickStrip(fnames[i], pnametmp, istat.st_size, 0) < 0) {
720 ("...strip failed for output temp file ``%s''; skipping it\n",
730 ofd = open(pnametmp, O_RDWR, 0);
732 printf("Could not open output temp file ``%s'': %s\n", pnametmp,
739 mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
740 #else /* AFS_AIX_ENV */
741 /* check to see if this file is hard to duplicate */
742 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
744 printf("Could not create output temp file ``%s'': %s\n", pnametmp,
751 mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
752 /* here both files are open and ready to go */
754 code = read(ifd, diskBuffer, BUFSIZE);
758 printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
762 newcode = write(ofd, diskBuffer, code);
763 if (newcode != code) {
764 printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
769 rcode = 1; /* an error occurred copying the file */
771 ("Warning: Error occurred writing output temp file %s; skipping it\n",
776 continue; /* to the next file */
778 /* strip the file? */
779 if (strip == 1 || (strip == -1 && ((istat.st_mode & 0111) == 0111)
780 && stripName(newNames[i])))
781 if (quickStrip(ofd, istat.st_size) < 0) {
783 ("...strip failed for output temp file ``%s''; skipping it\n",
791 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
794 #endif /* AFS_AIX_ENV */
795 if (fchmod(ofd, mode) < 0) {
796 printf("Couldn't chmod output temp file ``%s'': %s\n", pnametmp,
804 tvp[0].tv_sec = istat.st_atime;
806 tvp[1].tv_sec = istat.st_mtime;
808 if (utimes(pnametmp, tvp) < 0) {
809 printf("Couldn't utimes output temp file ``%s'': %s\n", pnametmp,
818 printf("Warning: Could not close output temp file %s (%s)\n",
819 pnametmp, ErrorString(errno));
821 rcode = 1; /* an error occurred closing the output file */
822 continue; /* to the next file */
825 /* do this later so vice doesn't see chown of unstored file */
826 if (setOwner || setGroup)
828 (pnametmp, (setOwner ? owner : -1),
829 (setGroup ? group : -1)) < 0) {
830 printf("Couldn't set %s for output temp file %s: %s\n",
831 (setOwner ? (setGroup ? "owner and group" : "owner") :
832 "group"), pnametmp, ErrorString(errno));
838 if (rename(pnametmp, pname) < 0) {
839 #if defined(AFS_HPUX_ENV)
840 if (errno == ETXTBSY) {
841 (void)strcpy(pnameBusy, pname);
842 (void)strcat(pnameBusy, ".BUSY");
843 if (rename(pname, pnameBusy) == 0) {
844 fprintf(stdout, "Had to leave old file: %s.\n",
847 "Please delete this file when the program using it is finished.\n");
848 if (rename(pnametmp, pname) < 0) {
849 #endif /* AFS_HPUX_ENV */
852 ("Couldn't rename temp file %s to be output file %s: %s\n",
853 pnametmp, pname, ErrorString(errno));
858 #if defined(AFS_HPUX_ENV)
862 "Couldn't move busy target file %s to make room for new version %s: %s\n",
863 pname, pnametmp, ErrorString(errno));
864 if (errno == ETXTBSY) {
866 "Try terminating any programs using the file %s and then re-run %s.\n",
874 #endif /* AFS_HPUX_ENV */