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;
93 #if !defined(AFS_DARWIN60_ENV) && !defined(AFS_FBSD50_ENV)
96 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_XBSD_ENV)
97 extern char *sys_errlist[];
100 /* static prototypes */
101 char *ErrorString(int aerrno);
102 int stripName(char *aname);
103 int atoo(register char *astr);
106 #if defined(AFS_HPUX_ENV) && !defined(AFS_HPUX102_ENV)
107 int utimes(char *file, struct timeval tvp[2])
109 struct utimbuf times;
111 times.actime = tvp[0].tv_sec;
112 times.modtime = tvp[1].tv_sec;
113 return (utime(file,×));
117 static char *strrpbrk (char *s, char *set)
122 memset(sets, 0, sizeof(sets));
123 while (*set) sets[(int) *set++] = 1;
125 while (i > 0) if (sets[(int)s[--i]]) return &s[i];
129 char *ErrorString(int aerrno)
131 static char tbuffer[100];
132 if (aerrno < 0 || aerrno >= sys_nerr) {
133 sprintf(tbuffer, "undefined error code %d", aerrno);
135 strcpy(tbuffer, sys_errlist[aerrno]);
140 int stripName(char *aname)
142 if (strrchr(aname, '.') == 0) return 1;
146 int atoo(register char *astr)
148 register afs_int32 value;
151 while ((tc = *astr++))
158 #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)
160 * Implementation lifted from that for AIX 3.1, since there didn't seem to be any
161 * reason why it wouldn't work.
163 static int quickStrip (char *iname, char *oname, int ignored, int copy_only)
166 static char *strip[] = {
169 static char *copy[] = {
174 * first, copy the `iname' to the `oname'
176 switch (pid = fork()) {
184 execve("/bin/cp", copy, NULL);
188 default: /* parent */
189 if (waitpid(pid, &status, 0) != pid) {
196 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
201 * need to do a chmod to guarantee that the perms will permit
202 * the strip. Perms are fixed up later.
204 if (chmod(oname, 0700)) {
209 #if !defined(AFS_OBSD_ENV) && !defined(AFS_NBSD_ENV)
211 * done the copy, now strip if desired.
216 switch (pid = fork()) {
224 #define STRIP_BIN "/usr/ccs/bin/strip"
225 #elif defined(AFS_LINUX20_ENV) || defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
226 #define STRIP_BIN "/usr/bin/strip"
228 #define STRIP_BIN "/bin/strip"
230 execve(STRIP_BIN, strip, NULL);
234 default: /* parent */
235 if (waitpid(pid, &status, 0) != pid) {
249 * whoa! back up and be a little more rational (every little bit helps in
252 static int quickStrip (char *iname, char *oname, int ignored, int copy_only)
255 static char *strip[] = {
258 static char *copy[] = {
263 * first, copy the `iname' to the `oname'
265 switch (pid = fork()) {
273 execve("/bin/cp", copy, 0);
277 default: /* parent */
278 if (waitpid(pid, &status, 0) != pid) {
285 fprintf(stderr, "Bad exit code from /bin/cp: %d\n", status);
290 * need to do a chmod to guarantee that the perms will permit
291 * the strip. Perms are fixed up later.
293 if (chmod(oname, 0700)) {
299 * done the copy, now strip if desired.
304 switch (pid = fork()) {
311 execve("/bin/strip", strip, 0);
312 perror("/bin/strip");
315 default: /* parent */
316 if (waitpid(pid, &status, 0) != pid) {
325 #endif /* AFS_AIX32_ENV */
326 #else /* !AFS_AIX_ENV */
330 int quickStrip(int fd, afs_int32 asize)
334 int mysex, swapheader;
336 /* Read the file header, if it is one. */
337 if (lseek(fd, 0, L_SET) == -1) {
338 printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno));
341 dum = read(fd, (char *) &fheader, sizeof(fheader));
342 /* Fail on I/O error */
344 printf("Initial read failed while stripping: %s\n", ErrorString(errno));
347 /* If the file is smaller than a file header, forget it. */
348 if (dum != sizeof(fheader)) return 0;
349 #ifdef AFS_DECOSF_ENV
350 mysex = LITTLEENDIAN;
352 mysex = gethostsex();
353 if (mysex != BIGENDIAN && mysex != LITTLEENDIAN) return 0;
356 if (fheader.f_magic == MIPSELMAGIC) {
357 if (mysex == BIGENDIAN) swapheader = 1;
358 } else if (fheader.f_magic == MIPSEBMAGIC) {
359 if (mysex == LITTLEENDIAN) swapheader = 1;
360 } else return 0; /* not executable */
361 #ifdef AFS_DECOSF_ENV
362 if (swapheader) return 0;
364 if (swapheader) swap_filehdr(&fheader, gethostsex());
366 /* Already stripped? */
367 if (fheader.f_symptr == 0 || fheader.f_nsyms == 0) return 0;
368 /* Strip it. Zero out the symbol pointers. */
369 newlen = fheader.f_symptr;
370 fheader.f_symptr = 0;
372 #ifndef AFS_DECOSF_ENV
373 if (swapheader) swap_filehdr(&fheader, gethostsex());
375 if (lseek(fd, 0, L_SET) == -1) return -1;
376 if (write(fd, (char *) &fheader, sizeof(fheader)) != sizeof(fheader)) return -1;
377 /* Now truncate the file itself. */
378 if (ftruncate(fd, newlen) != 0) return -1;
382 static int quickStrip (int afd, afs_int32 asize)
387 n = lseek(afd, 0, 0);
388 if (n < 0) {printf("Initial lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
389 n = read(afd, &buf, sizeof(buf));
390 if (n < 0) {printf("Initial read failed while stripping: %s\n", ErrorString(errno)); return -1;}
392 if (n >= sizeof(*head) && !N_BADMAG(*head))
393 {/* This code lifted from strip.c. */
394 bytesLeft = (afs_int32) head->a_text + head->a_data;
395 head->a_syms = head->a_trsize = head->a_drsize = 0;
396 if (head->a_magic == ZMAGIC)
397 bytesLeft += N_TXTOFF(*head) - sizeof(*head);
398 /* also include size of header */
399 bytesLeft += sizeof(*head);
400 n = lseek(afd, 0, 0);
401 if (n < 0) {printf("lseek failed while stripping file: %s\n", ErrorString(errno)); return -1;}
402 n = write(afd, &buf, sizeof(buf));
403 if (n < 0) {printf("write failed while stripping file: %s\n", ErrorString(errno)); return -1;}
408 /* check if size of stripped file is same as existing file */
409 if (bytesLeft != 0 && bytesLeft != asize)
410 {if (ftruncate(afd, bytesLeft) < 0)
411 {printf("ftruncate failed after stripping file: %s\n", ErrorString(errno)); return -1;}
417 #endif /* AFS_HPUX_ENV */
419 #include "AFS_component_version_number.c"
421 int main (int argc, char *argv[])
423 int setOwner, setMode, setGroup, ifd, ofd;
424 afs_int32 mode=0, owner, group;
427 char *fnames[MAXFILES], *newNames[MAXFILES];
428 afs_int32 rcode, code;
431 #if defined (AFS_HPUX_ENV)
432 char pnameBusy[1024];
433 #endif /* AFS_HPUX_ENV */
437 static char diskBuffer[BUFSIZE]; /* must be static to avoid compiler bugs for large stuff */
438 char myHostName[100];
439 struct timeval tvp[2];
444 register afs_int32 i;
448 * The following signal action for AIX is necessary so that in case of a
449 * crash (i.e. core is generated) we can include the user's data section
450 * in the core dump. Unfortunately, by default, only a partial core is
451 * generated which, in many cases, isn't too useful.
453 struct sigaction nsa;
455 sigemptyset(&nsa.sa_mask);
456 nsa.sa_handler = SIG_DFL;
457 nsa.sa_flags = SA_FULLDUMP;
458 sigaction(SIGSEGV, &nsa, NULL);
462 strip = -1; /* don't know yet */
468 isDir = -1; /* don't know yet */
470 for(i=1; i<argc; i++)
474 if (!strcmp(tp, "-m")) mode = atoo(argv[++i]), setMode=1;
475 else if (!strcmp(tp, "-s")) strip = 1;
476 else if (!strcmp(tp, "-ns")) strip = 0;
477 else if (!strcmp(tp, "-c")) /* nothing */;
478 else if (!strcmp(tp, "-f")) isDir = 0; /* => dest is file */
479 else if (!strcmp(tp, "-o"))
480 {/* look up the dude */
481 tpw = getpwnam(argv[++i]);
483 {printf("User %s not found in passwd database, ignored\n", argv[i]);
486 {owner = tpw->pw_uid;
490 else if (!strcmp(tp, "-g"))
491 {/* look up the dude */
492 tgp = getgrnam(argv[++i]);
494 {printf("Group %s not found in passwd database; ignored\n", argv[i]);
497 {group = tgp->gr_gid;
502 printf("Bad switch %s\n", argv[i]);
508 if (fptr >= MAXFILES)
509 {printf("Too many files on command line, max is %d\n", MAXFILES);
512 fnames[fptr++] = argv[i];
516 /* we've parse the commands, now *do* them */
518 /* otherwise we are doing a local install, so we do the work for each file
519 here the last name in the fname array is the dir in which to put all
523 {printf("Not enough file names\n");
527 /* N file usage requires last argument to be a directory. If -f was
528 specified it is an error. In the 2 file usage when -f is not specified
529 use a heuristic. If the ends of the two pathnames are equal then assume
530 the target is a file, otherwise assume it is a directory. */
532 if ((fptr > 2) && (isDir == 0)) {
533 printf ("target must be a directory, don't use multiple source files with -f switch\n");
536 else if (fptr > 2) isDir = 1;
537 else if (isDir != 0) {
541 targetSuffix = strrpbrk (fnames[1], "./");
542 sourceSuffix = strrpbrk (fnames[0], "./");
543 if (sourceSuffix == 0) {
544 sourceSuffix = fnames[0];
545 if (targetSuffix == 0) targetSuffix = fnames[1];
548 else if (targetSuffix == 0) targetSuffix = fnames[1];
549 if (strcmp(targetSuffix, sourceSuffix) == 0) isDir = 0;
552 dname = fnames[--fptr];
553 if (stat(dname, &istat) < 0) {
554 if ((errno == ENOENT) || (errno == ENOTDIR)) {
556 char protopath[BUFSIZ];
561 protopath[i] = dname[i];
562 c = dname[++i]; /* next char */
563 } while (!((c == 0) || (c == '/')));
566 /* don't mkdir last component if target is a file */
567 if ((c == 0) && (isDir == 0)) break;
569 /* else create dir component if it doesn't exist */
570 code = stat(protopath, &istat);
571 if (code && (errno == ENOENT)) {
572 code = mkdir (protopath, 0755);
574 printf ("Can't create destination path at %s\n",
579 } /* while dname not exhausted */
580 if (isDir == -1) isDir = 1;
583 printf("Can't stat destination ``%s'': %s\n", dname, ErrorString(errno));
587 if ((istat.st_mode & S_IFMT) == S_IFDIR) isDir = 1;
591 /* either can be n files and one dir, or one file and one target */
592 if (!isDir && fptr != 1) {
593 printf("target for multiple files must be a dir\n");
598 {/* figure out name to put as entry name for file */
599 tp = strrchr(fnames[i], '/');
600 if (tp) newNames[i] = tp+1;
601 else newNames[i] = fnames[i];
604 {/* copy newName[i] into directory dname */
606 /* pname is target file in either case */
608 {strcpy(pname, dname);
610 strcat(pname, newNames[i]);
612 else strcpy(pname, dname);
613 strcpy(pnametmp, pname);
614 /* Make up a temporary name for a destination */
615 pnamelen = strlen(pnametmp);
616 gethostname(myHostName, sizeof(myHostName)-1); /* lv room for null */
617 if (pnamelen > 1020 - strlen(myHostName)) pnamelen = 1020 - strlen(myHostName);
618 pnametmp[pnamelen] = '.';
619 strcpy(&pnametmp[pnamelen+1], myHostName);
620 if (strcmp(fnames[i], pnametmp) == 0) strcpy(&pnametmp[pnamelen], ".NeW");
622 ifd = open(fnames[i], O_RDONLY, 0);
624 {printf("Can't open source file ``%s'': %s\n", fnames[i], ErrorString(errno));
628 if (fstat (ifd, &istat) < 0) {
629 printf("Cound not fstat input file ``%s'': %s; skipping it\n", fnames[i], ErrorString(errno));
634 if (lstat(pname, &ostat) == 0) {
635 if ((ostat.st_size == istat.st_size) &&
636 (ostat.st_mtime == istat.st_mtime) &&
637 ((!setMode) || ((ostat.st_mode & S_IFMT) == mode)) &&
638 ((!setOwner) || (ostat.st_uid == owner)) &&
639 ((!setGroup) || (ostat.st_gid == group))) {
641 printf("No changes to %s since %s installed\n", fnames[i], pname);
645 #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)
648 ((strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i]))))
651 /* Simply copy target to dest */
652 quickStrip(fnames[i], pnametmp, istat.st_size, 1);
654 if (quickStrip(fnames[i],pnametmp,istat.st_size, 0) < 0) {
655 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
656 close(ifd); unlink(pnametmp);
663 ofd = open(pnametmp, O_RDWR, 0);
665 printf("Could not open output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
670 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
671 #else /* AFS_AIX_ENV */
672 /* check to see if this file is hard to duplicate */
673 ofd = open(pnametmp, O_RDWR | O_TRUNC | O_CREAT, 0666);
675 {printf("Could not create output temp file ``%s'': %s\n", pnametmp, ErrorString(errno));
680 if (!setMode) mode = istat.st_mode; /* Was 0755:> this is the default for our rcs to work */
681 /* here both files are open and ready to go */
683 {code = read(ifd, diskBuffer, BUFSIZE);
684 if (code == 0) break;
686 {printf("READ ERROR %d: %s\n", errno, ErrorString(errno));
690 newcode = write(ofd, diskBuffer, code);
692 {printf("WRITE ERROR %d: %s\n", errno, ErrorString(errno));
697 {rcode = 1; /* an error occurred copying the file */
698 printf("Warning: Error occurred writing output temp file %s; skipping it\n",
700 close(ifd); unlink(pnametmp); close(ofd);
701 continue; /* to the next file */
703 /* strip the file? */
705 (strip == -1 && ((istat.st_mode & 0111) == 0111) && stripName(newNames[i])))
706 if (quickStrip(ofd,istat.st_size) < 0) {
707 printf("...strip failed for output temp file ``%s''; skipping it\n", pnametmp);
708 close(ifd); unlink(pnametmp);
713 /* do the chmod, etc calls before closing the file for max parallelism on store behind */
716 #endif /* AFS_AIX_ENV */
717 if (fchmod(ofd, mode) < 0)
718 {printf("Couldn't chmod output temp file ``%s'': %s\n",
719 pnametmp, ErrorString(errno));
720 unlink(pnametmp); close(ofd);
725 tvp[0].tv_sec = istat.st_atime;
727 tvp[1].tv_sec = istat.st_mtime;
729 if (utimes(pnametmp, tvp) < 0)
730 {printf("Couldn't utimes output temp file ``%s'': %s\n",
731 pnametmp, ErrorString(errno));
732 unlink(pnametmp); close(ofd);
738 {printf("Warning: Could not close output temp file %s (%s)\n",
739 pnametmp, ErrorString(errno));
741 rcode = 1; /* an error occurred closing the output file */
742 continue; /* to the next file */
745 /* do this later so vice doesn't see chown of unstored file */
746 if (setOwner || setGroup)
747 if (chown(pnametmp, (setOwner? owner : -1), (setGroup? group : -1)) < 0) {
748 printf("Couldn't set %s for output temp file %s: %s\n",
749 (setOwner? (setGroup? "owner and group" : "owner") : "group"),
750 pnametmp, ErrorString(errno));
756 if (rename(pnametmp, pname) < 0) {
757 #if defined(AFS_HPUX_ENV)
758 if (errno == ETXTBSY) {
759 (void)strcpy(pnameBusy, pname);
760 (void)strcat(pnameBusy, ".BUSY");
761 if (rename(pname, pnameBusy) == 0) {
762 fprintf(stdout, "Had to leave old file: %s.\n", pnameBusy);
764 "Please delete this file when the program using it is finished.\n");
765 if (rename(pnametmp, pname) < 0) {
766 #endif /* AFS_HPUX_ENV */
768 printf("Couldn't rename temp file %s to be output file %s: %s\n",
769 pnametmp, pname, ErrorString(errno));
774 #if defined(AFS_HPUX_ENV)
779 "Couldn't move busy target file %s to make room for new version %s: %s\n",
780 pname, pnametmp, ErrorString(errno));
781 if (errno == ETXTBSY) {
783 "Try terminating any programs using the file %s and then re-run %s.\n",
791 #endif /* AFS_HPUX_ENV */