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;
95 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
96 extern char *sys_errlist[];
99 /* static prototypes */
100 char *ErrorString(int aerrno);
101 int stripName(char *aname);
102 int atoo(register char *astr);
105 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
106 int utimes(char *file, struct timeval tvp[2])
108 struct utimbuf times;
110 times.actime = tvp[0].tv_sec;
111 times.modtime = tvp[1].tv_sec;
112 return (utime(file,×));
116 static char *strrpbrk (char *s, char *set)
121 memset(sets, 0, sizeof(sets));
122 while (*set) sets[(int) *set++] = 1;
124 while (i > 0) if (sets[(int)s[--i]]) return &s[i];
128 char *ErrorString(int aerrno)
130 static char tbuffer[100];
131 if (aerrno < 0 || aerrno >= sys_nerr) {
132 sprintf(tbuffer, "undefined error code %d", aerrno);
134 strcpy(tbuffer, sys_errlist[aerrno]);
139 int stripName(char *aname)
141 if (strrchr(aname, '.') == 0) return 1;
145 int atoo(register char *astr)
147 register afs_int32 value;
150 while ((tc = *astr++))
157 #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)
159 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
160 * reason why it wouldn't work.
162 static int quickStrip (char *iname, char *oname, int ignored, int copy_only)
165 static char *strip[] = {
168 static char *copy[] = {
173 * first, copy the `iname' to the `oname'
175 switch (pid = fork()) {
183 execve("/bin/cp", copy, NULL);
187 default: /* parent */
188 if (waitpid(pid, &status, 0) != pid) {
195 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
200 * need to do a chmod to guarantee that the perms will permit
201 * the strip. Perms are fixed up later.
203 if (chmod(oname, 0700)) {
208 #if !defined(AFS_OBSD_ENV) && !defined(AFS_NBSD_ENV)
210 * done the copy, now strip if desired.
215 switch (pid = fork()) {
223 #define STRIP_BIN "/usr/ccs/bin/strip"
224 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
225 #define STRIP_BIN "/usr/bin/strip"
227 #define STRIP_BIN "/bin/strip"
229 execve(STRIP_BIN, strip, NULL);
233 default: /* parent */
234 if (waitpid(pid, &status, 0) != pid) {
248 * whoa! back up and be a little more rational (every little bit helps in
251 static int quickStrip (char *iname, char *oname, int ignored, int copy_only)
254 static char *strip[] = {
257 static char *copy[] = {
262 * first, copy the `iname' to the `oname'
264 switch (pid = fork()) {
272 execve("/bin/cp", copy, 0);
276 default: /* parent */
277 if (waitpid(pid, &status, 0) != pid) {
284 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
289 * need to do a chmod to guarantee that the perms will permit
290 * the strip. Perms are fixed up later.
292 if (chmod(oname, 0700)) {
298 * done the copy, now strip if desired.
303 switch (pid = fork()) {
310 execve("/bin/strip", strip, 0);
311 perror("/bin/strip");
314 default: /* parent */
315 if (waitpid(pid, &status, 0) != pid) {
324 #endif /* AFS_AIX32_ENV */
325 #else /* !AFS_AIX_ENV */
329 int quickStrip(int fd, afs_int32 asize)
333 int mysex, swapheader;
335 /* Read the file header, if it is one. */
336 if (lseek(fd, 0, L_SET) == -1) {
337 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
340 dum = read(fd, (char *) &fheader, sizeof(fheader));
341 /* Fail on I/O error */
343 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
346 /* If the file is smaller than a file header, forget it. */
347 if (dum != sizeof(fheader)) return 0;
348 #ifdef AFS_DECOSF_ENV
349 mysex = LITTLEENDIAN;
351 mysex = gethostsex();
352 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
355 if (fheader.f_magic == MIPSELMAGIC) {
356 if (mysex == BIGENDIAN) swapheader = 1;
357 } else if (fheader.f_magic == MIPSEBMAGIC) {
358 if (mysex == LITTLEENDIAN) swapheader = 1;
359 } else return 0; /* not executable */
360 #ifdef AFS_DECOSF_ENV
361 if (swapheader) return 0;
363 if (swapheader) swap_filehdr(&fheader, gethostsex());
365 /* Already stripped? */
366 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
367 /* Strip it. Zero out the symbol pointers. */
368 newlen = fheader.f_symptr;
369 fheader.f_symptr = 0;
371 #ifndef AFS_DECOSF_ENV
372 if (swapheader) swap_filehdr(&fheader, gethostsex());
374 if (lseek(fd, 0, L_SET) == -1) return -1;
375 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
376 /* Now truncate the file itself. */
377 if (ftruncate(fd, newlen) != 0) return -1;
381 static int quickStrip (int afd, afs_int32 asize)
386 n = lseek(afd, 0, 0);
387 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
388 n = read(afd, &buf, sizeof(buf));
389 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
391 if (n >= sizeof(*head) && !N_BADMAG(*head))
392 {/* This code lifted from strip.c. */
393 bytesLeft = (afs_int32) head->a_text + head->a_data;
394 head->a_syms = head->a_trsize = head->a_drsize = 0;
395 if (head->a_magic == ZMAGIC)
396 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
397 /* also include size of header */
398 bytesLeft += sizeof(*head);
399 n = lseek(afd, 0, 0);
400 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
401 n = write(afd, &buf, sizeof(buf));
402 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
407 /* check if size of stripped file is same as existing file */
408 if (bytesLeft != 0 && bytesLeft != asize)
409 {if (ftruncate(afd, bytesLeft) < 0)
410 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
416 #endif /* AFS_HPUX_ENV */
418 #include "AFS_component_version_number.c"
420 int main (int argc, char *argv[])
422 int setOwner, setMode, setGroup, ifd, ofd;
423 afs_int32 mode=0, owner, group;
426 char *fnames[MAXFILES], *newNames[MAXFILES];
427 afs_int32 rcode, code;
430 #if defined (AFS_HPUX_ENV)
431 char pnameBusy[1024];
432 #endif /* AFS_HPUX_ENV */
436 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
437 char myHostName[100];
438 struct timeval tvp[2];
443 register afs_int32 i;
447 * The following signal action for AIX is necessary so that in case of a
448 * crash (i.e. core is generated) we can include the user's data section
449 * in the core dump. Unfortunately, by default, only a partial core is
450 * generated which, in many cases, isn't too useful.
452 struct sigaction nsa;
454 sigemptyset(&nsa.sa_mask);
455 nsa.sa_handler = SIG_DFL;
456 nsa.sa_flags = SA_FULLDUMP;
457 sigaction(SIGSEGV, &nsa, NULL);
461 strip = -1; /* don't know yet */
467 isDir = -1; /* don't know yet */
469 for(i=1; i<argc; i++)
473 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
474 else if (!strcmp(tp, "-s")) strip = 1;
475 else if (!strcmp(tp, "-ns")) strip = 0;
476 else if (!strcmp(tp, "-c")) /* nothing */;
477 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
478 else if (!strcmp(tp, "-o"))
479 {/* look up the dude */
480 tpw = getpwnam(argv[++i]);
482 {printf("User %s not found in passwd database, ignored\n", argv[i]);
485 {owner = tpw->pw_uid;
489 else if (!strcmp(tp, "-g"))
490 {/* look up the dude */
491 tgp = getgrnam(argv[++i]);
493 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
496 {group = tgp->gr_gid;
501 printf("Bad switch %s\n", argv[i]);
507 if (fptr >= MAXFILES)
508 {printf("Too many files on command line, max is %d\n", MAXFILES);
511 fnames[fptr++] = argv[i];
515 /* we've parse the commands, now *do* them */
517 /* otherwise we are doing a local install, so we do the work for each file
518 here the last name in the fname array is the dir in which to put all
522 {printf("Not enough file names\n");
526 /* N file usage requires last argument to be a directory. If -f was
527 specified it is an error. In the 2 file usage when -f is not specified
528 use a heuristic. If the ends of the two pathnames are equal then assume
529 the target is a file, otherwise assume it is a directory. */
531 if ((fptr > 2) && (isDir == 0)) {
532 printf ("target must be a directory, don't use multiple source files with -f switch\n");
535 else if (fptr > 2) isDir = 1;
536 else if (isDir != 0) {
540 targetSuffix = strrpbrk (fnames[1], "./");
541 sourceSuffix = strrpbrk (fnames[0], "./");
542 if (sourceSuffix == 0) {
543 sourceSuffix = fnames[0];
544 if (targetSuffix == 0) targetSuffix = fnames[1];
547 else if (targetSuffix == 0) targetSuffix = fnames[1];
548 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
551 dname = fnames[--fptr];
552 if (stat(dname, &istat) < 0) {
553 if ((errno == ENOENT) || (errno == ENOTDIR)) {
555 char protopath[BUFSIZ];
560 protopath[i] = dname[i];
561 c = dname[++i]; /* next char */
562 } while (!((c == 0) || (c == '/')));
565 /* don't mkdir last component if target is a file */
566 if ((c == 0) && (isDir == 0)) break;
568 /* else create dir component if it doesn't exist */
569 code = stat(protopath, &istat);
570 if (code && (errno == ENOENT)) {
571 code = mkdir (protopath, 0755);
573 printf ("Can't create destination path at %s\n",
578 } /* while dname not exhausted */
579 if (isDir == -1) isDir = 1;
582 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
586 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
590 /* either can be n files and one dir, or one file and one target */
591 if (!isDir && fptr != 1) {
592 printf("target for multiple files must be a dir\n");
597 {/* figure out name to put as entry name for file */
598 tp = strrchr(fnames[i], '/');
599 if (tp) newNames[i] = tp+1;
600 else newNames[i] = fnames[i];
603 {/* copy newName[i] into directory dname */
605 /* pname is target file in either case */
607 {strcpy(pname, dname);
609 strcat(pname, newNames[i]);
611 else strcpy(pname, dname);
612 strcpy(pnametmp, pname);
613 /* Make up a temporary name for a destination */
614 pnamelen = strlen(pnametmp);
615 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
616 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
617 pnametmp[pnamelen] = '.';
618 strcpy(&pnametmp[pnamelen+1], myHostName);
619 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
621 ifd = open(fnames[i], O_RDONLY, 0);
623 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
627 if (fstat (ifd, &istat) < 0) {
628 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
633 if (lstat(pname, &ostat) == 0) {
634 if ((ostat.st_size == istat.st_size) &&
635 (ostat.st_mtime == istat.st_mtime) &&
636 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
637 ((!setOwner) || (ostat.st_uid == owner)) &&
638 ((!setGroup) || (ostat.st_gid == group))) {
640 printf("No changes to %s since %s installed\n", fnames[i], pname);
644 #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)
647 ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i]))))
650 /* Simply copy target to dest */
651 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
653 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
654 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
655 close(ifd); unlink(pnametmp);
662 ofd = open(pnametmp, O_RDWR, 0);
664 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
669 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
670 #else /* AFS_AIX_ENV */
671 /* check to see if this file is hard to duplicate */
672 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
674 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
679 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
680 /* here both files are open and ready to go */
682 {code = read(ifd, diskBuffer, BUFSIZE);
683 if (code == 0) break;
685 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
689 newcode = write(ofd, diskBuffer, code);
691 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
696 {rcode = 1; /* an error occurred copying the file */
697 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
699 close(ifd); unlink(pnametmp); close(ofd);
700 continue; /* to the next file */
702 /* strip the file? */
704 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
705 if (quickStrip(ofd,istat.st_size) < 0) {
706 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
707 close(ifd); unlink(pnametmp);
712 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
715 #endif /* AFS_AIX_ENV */
716 if (fchmod(ofd, mode) < 0)
717 {printf("Couldn't chmod output temp file ``%s'': %s\n",
718 pnametmp, ErrorString(errno));
719 unlink(pnametmp); close(ofd);
724 tvp[0].tv_sec = istat.st_atime;
726 tvp[1].tv_sec = istat.st_mtime;
728 if (utimes(pnametmp, tvp) < 0)
729 {printf("Couldn't utimes output temp file ``%s'': %s\n",
730 pnametmp, ErrorString(errno));
731 unlink(pnametmp); close(ofd);
737 {printf("Warning: Could not close output temp file %s (%s)\n",
738 pnametmp, ErrorString(errno));
740 rcode = 1; /* an error occurred closing the output file */
741 continue; /* to the next file */
744 /* do this later so vice doesn't see chown of unstored file */
745 if (setOwner || setGroup)
746 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
747 printf("Couldn't set %s for output temp file %s: %s\n",
748 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
749 pnametmp, ErrorString(errno));
755 if (rename(pnametmp, pname) < 0) {
756 #if defined(AFS_HPUX_ENV)
757 if (errno == ETXTBSY) {
758 (void)strcpy(pnameBusy, pname);
759 (void)strcat(pnameBusy, ".BUSY");
760 if (rename(pname, pnameBusy) == 0) {
761 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
763 "Please delete this file when the program using it is finished.\n");
764 if (rename(pnametmp, pname) < 0) {
765 #endif /* AFS_HPUX_ENV */
767 printf("Couldn't rename temp file %s to be output file %s: %s\n",
768 pnametmp, pname, ErrorString(errno));
773 #if defined(AFS_HPUX_ENV)
778 "Couldn't move busy target file %s to make room for new version %s: %s\n",
779 pname, pnametmp, ErrorString(errno));
780 if (errno == ETXTBSY) {
782 "Try terminating any programs using the file %s and then re-run %s.\n",
790 #endif /* AFS_HPUX_ENV */