rsalz@bbn.com (Rich Salz) (03/21/91)
Submitted-by: Kevin Braunsdorf <ksb@cc.purdue.edu> Posting-number: Volume 24, Issue 66 Archive-name: pucc-install/part04 #!/bin/sh # This is part 04 of pucc-1b # ============= install.d/install.1l ============== if test ! -d 'install.d'; then echo 'x - creating directory install.d' mkdir 'install.d' fi if test -f 'install.d/install.1l' -a X"$1" != X"-c"; then echo 'x - skipping install.d/install.1l (File already exists)' else echo 'x - extracting install.d/install.1l (Text)' sed 's/^X//' << 'Purdue' > 'install.d/install.1l' && .\" $Id: install.1l,v 7.0 90/09/17 09:41:50 ksb Exp $ .\" Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana .\" 47907. All rights reserved. .\" .\" Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb .\" Jeff Smith, jsmith@cc.purdue.edu, purdue!jsmith .\" .\" This software is not subject to any license of the American Telephone .\" and Telegraph Company or the Regents of the University of California. .\" .\" Permission is granted to anyone to use this software for any purpose on .\" any computer system, and to alter it and redistribute it freely, subject .\" to the following restrictions: .\" .\" 1. Neither the authors nor Purdue University are responsible for any .\" consequences of the use of this software. .\" .\" 2. The origin of this software must not be misrepresented, either by .\" explicit claim or by omission. Credit to the authors and Purdue .\" University must appear in documentation and sources. .\" .\" 3. Altered versions must be plainly marked as such, and must not be .\" misrepresented as being the original software. .\" .\" 4. This notice may not be removed or altered. .\" .\" $Laser: ${tbl-tbl} %f | ${ltroff-ltroff} -man .\" $Compile: ${tbl-tbl} %f | ${nroff-nroff} -man | ${PAGER-${more-more}} .TH INSTALL 1L PUCC .SH NAME install \- update files or directories in a controlled environment .SH SYNOPSIS \fBinstall\fP [\-\fB1Dclnpqsv\fP] [\-\fBC\fP\fIchecklist\fP] [\-\fBH\fP\fIhardlinks\fP] [\-\fBS\fP\fIsymlinks\fP] [\-\fBg\fP\fIgroup\fP] [\-\fBm\fP\fImode\fP] [\-\fBo\fP\fIowner\fP] \fIfiles\fP \fIdestination\fP .sp 1 \fBinstall\fP \-\fBd\fP [\-\fBhnqrv\fP] [\-\fBC\fP\fIchecklist\fP] [\-\fBg\fP\fIgroup\fP] [\-\fBm\fP\fImode\fP] [\-\fBo\fP\fIowner\fP] \fIdirectory\fP .sp 1 \fBinstall\fP \-[\fBhV\fP] [\-\fBC\fP\fIchecklist\fP] .sp 1 \fBinstall\fP \-\fBR\fP [\-\fB1dlnqsv\fP] [\-\fBC\fP\fIchecklist\fP] [\-\fBH\fP\fIhardlinks\fP] [\-\fBS\fP\fIsymlinks\fP] [\-\fBg\fP\fIgroup\fP] [\-\fBm\fP\fImode\fP] [\-\fBo\fP\fIowner\fP] \fIdestination\fP .SH DESCRIPTION .PP .I Install is a tool for updating system files in a controlled environment. The design philosophy of .I install is to automate the installation process and put it as much out of reach of human carelessness as possible, while providing a flexible, easy to use tool. .I Install provides the following features: .IP \(bu .I Install increases system security by providing a controlled installation environment. It checks the actions of the superuser against a configuration file that you build (either by hand or using .IR instck (1L)), and can prevent grievous mistakes such as installing a shell setuid to a privileged user, or installing a world\(hywritable password file. An appropriate configuration file can guarantee that files are installed with correct owner, group and mode, that .IR strip (1) is run on binaries and .IR ranlib (1) is run on libraries. Regardless of whether you create a configuration file, .I install warns you of many possible errors, unless you make it quiet with its .B \-q option. For instance, if you install a new version of the .IR ex (1) editor and forget to update its links, .I install will notice the extra links to the existing version and warn you that you might be making a mistake. .IP \(bu Installed files do not overwrite current versions. The current version is backed up to a subdirectory of its dot directory (named \*(lqOLD\*(rq by default), where it may be easily re\(hyinstalled in the case of an unforeseen bug. The companion program .IR purge (1L) removes these backed\(hyup files after a user\(hyspecified interval. By default, if you repeatedly install new versions of a file, .I install creates a series of backups, providing a primitive form of version control. .IP \(bu .I Install increases accountability by logging the actions of the superuser. This makes it easier to track down errors after the fact. .IP \(bu .I Install simplifies Makefiles by allowing you to combine operations that would require several steps into a single one (e.g., you can specify in a single command line a file's ownership, group, mode, whether to run .IR strip (1) or .IR ranlib (1), and which hard or soft links should be made). .IP \(bu Despite its power and potential complexity, .I install is easy to use interactively because it intuits appropriate installation parameters for you, either by using those associated with an existing file, or its compilation\(hydependent defaults. In most cases you do not have to specify a file's owner, group, or mode unless you want them to be different from an existing file or the compilation\(hydependent defaults. Typical interactive usage is simply \*(lqinstall file destination.\*(rq .IP \(bu .I Install is as careful as it can be to complete an installation once it is begun. There is a point in the code where .IR unlink (2) and .IR rename (2) must be executed in close succession, and that is the only window in which a system crash or a signal might leave system files in an inconsistent state (unfortunately, this is not true under operating systems that do not provide an atomic .IR rename (2) system call). This is true even when .I install must copy files across file system boundaries. .IP \(bu .I Install may also be used to remove (de\(hyinstall) files in a controlled manner. .IP \(bu Finally, .I install currently runs on a variety of architectures and operating systems and is easy to port to new platforms. .SH USAGE .SS Terminology .PP The user specifies one or more .I files to install, and a .IR destination , which may be either a full or relative pathname ending in a file name or an existing directory name (if the directory does not exist, .I install thinks you mean to create a new file). The special name \*(lq\-\*(rq may be used for the .I file argument to indicate stdin (see EXAMPLES). In this case, .I destination .B must not be a directory, since .I install cannot guess the name the file should have when installed. .PP Because the user may specify more than one .IR files , and .I destination may be an existing directory or a file which may or may not exist, .I install must also internally keep track of a .I destdir (\*(lqdestination directory\*(rq, i.e., the directory in which to place the file to install), and a .IR target (the full pathname that each file to install will have when it is installed in .IR destdir ). The .I target and .I destdir are constructed from the .I files and .I destination arguments as described below. .PP For each name in .IR files , .I install determines a .I target name as follows: If .I destination is an existing directory, .I install catenates the last component of .I file to .I destination to arrive at the .I target name. If .I destination does not exist or is an existing file, .I install takes .I destination to be the .IR target . In the latter case .I destdir is simply .I destination minus its last component. If this reduction leaves .I destdir empty then it is set to \*(lq.\*(rq. (E.g., if .I destination were .I /etc/motd then .I destdir would be .IR /etc , but if .I destination were just .I motd then .I destdir would be \*(lq.\*(rq.) N.B.: If more than one .I files are specified, .I destination .B must be an existing directory. .PP Examples are the easiest way to clarify this terminology. .RS .sp 1 In the command \*(lqinstall motd /etc/motd\*(rq: .sp 1 .RS .TS l l . file: motd destination: /etc/motd destdir: /etc target: /etc/motd .TE .RE .sp 1 In the command \*(lqinstall motd /etc\*(rq: .sp 1 .RS .TS l l . file: motd destination: /etc destdir: /etc target: /etc/motd .TE .RE .sp 1 In the command sequence \*(lqcd /etc; install motd.new motd\*(rq: .sp 1 .RS .TS l l . file: motd.new destination: motd destdir: . (dot) target: ./motd .TE .RE .RE .sp 1 .SS Installation Parameters .PP If the file permissions, ownership or group ownership are not specified on the command line and .I target exists, .I install duplicates its group, ownership, and mode. Otherwise, if the .I target doesn't exist and the invoker is the superuser, .I install uses its compilation\(hydependent defaults. Otherwise, .I install uses the effective uid, the effective gid, and a compilation\(hydependent mode. .I Install may also be configured to inherit the mode and ownerships from the .IR destdir . (Use the .B \-V option to view the compilation\(hydependent defaults.) .PP Note: .I install can only change ownership if invoked by the superuser; however, any user may specify a different group as long as the group is allowed by .IR chgrp (1). .SS Method of Operation .PP .I Install first checks the proposed owner, group, and mode against the configuration file. It also checks whether the .I target should have .IR strip (1) or .IR ranlib (1) run on it after installation. .I Install aborts if it finds discrepancies between the configuration file and the proposed installation parameters. (If necessary, you can override the configuration file with \*(lq\fB\-C\fP \fI/dev/null\fP.\*(rq) .PP .I Install then looks for an existing .I target and backs it up to a subdirectory of .I destdir named \*(lqOLD\*(rq, which .I install will create if it doesn't exist. The backup is actually just a hard link to the existing .IR target . (If a backup file of the same name already exists in \*(lqOLD\*(rq, .I install first renames it by appending its process id). For security reasons, .I install drops setuid and setgid bits when files are moved to the \*(lqOLD\*(rq directory. After backing up an existing .IR target , .I install temporarily moves .I file to .IR destdir/OLD/random-name . This step is taken to ensure that both .I file and .I target are in the same file system so that .IR rename (2) may be used for the final installation. This reduces the window in which files might be left in an inconsistent state due to a system crash or signal. Consequently the \*(lqOLD\*(rq subdirectory must not be a file system mount point, since the .IR rename (2) would fail. .PP .I Install then unlinks the existing .I target (leaving the backup) and renames .IR destdir/OLD/random-name to .IR target . .PP Next .I install updates any hard links and soft (symbolic) links given under the .B \-H or .B \-S options. All links point at the installed .IR target . Existing symbolic links which point to the correct file are left unchanged, otherwise removed and replaced with the correct spelling. Existing correct hard links are unlinked and replaced, without a backup. Links which point to a file other than the .I target are backed up to \*(lqOLD\*(rq and linked. .I Install prints a warning in this case. .SH OPTIONS .TP .B \-1 After a successful installation .I install removes any previous backup in \*(lqOLD\*(rq. Thus .I install will keep exactly one previous revision of the installed file. .TP .B \-c Do not unlink the .IR files . .TP .BI \-C checkfile Search .I checkfile for an expression that matches the .IR target . If .I install finds such an expression it will check the proposed modes (etc.) against those listed in the checkfile; any differences cause .I install to abort the installation. This mechanism is provided to protect the superuser from installing, for instance, the shell setuid root, as in: .br .sp 1 .in 1.5i install \-m7555 \-o root \-g wheel sh /bin .sp 1 .in -.5i .br (note the extra \*(lq5\*(rq). See .IR install.cf (5L) and .IR instck (1L). .TP .BI \-d Build a directory rather than a plain file. If the directory is an OLD directory it is built with appropriate modes (see .B \-V below). .TP .B \-D Don't back up an existing .I target (the \*(lqDestroy\*(rq option). This is useful when a trivially correctable problem such as a spelling error in a print statement is found in a recently installed product, or when the .I target can be regenerated easily and is installed frequently. Sites that do not wish to keep backups but still want to take advantage of the checkfile could set this option in the environment. .TP .BI \-g group Install the file with group ownership .IR group . If no .B \-g option is given .I install will decide the group to use: .br .in 1.5i \(bu if there is an existing .I target use its group .br \(bu if we are the superuser use a compilation\(hydependent group .br \(bu else use the effective group id .TP .in -.5i .B \-h Print a summary of .IR install 's usage (the \*(lqhelp\*(rq option). .TP .BI \-H hardlinks Specify a colon separated list of hard links that should be made to the .I target after it is installed. (See EXAMPLES below.) .TP .BI \-l Run .IR ranlib ( 1 ) on the installed .IR targets . Under System V this option has no significance, but .B must be given to pass the default checkfile (see .IR install.cf (5L)). This allows Makefiles to work under all versions of UNIX. .TP .BI \-m mode Install the file with permissions .IR mode . .I Mode may be given in either octal mode or in the symbolic form used by .IR ls (1) (e.g., \*(lq755\*(rq) is equivalent to \*(lqrwxr-xr-x\*(rq). If the .B \-m option is not given, .I install will decide the mode to use: .br .in 1.5i \(bu if there is an existing .I target use its mode .br \(bu if we are the superuser use a compilation\(hydependent mode .br \(bu else use a compilation\(hydependent user mode .in -.5i .TP .B \-n Give an approximate execution trace by printing the (almost) equivalent shell commands, but don't do anything. This is useful for debugging .I install or seeing what a difficult command line would do if you ran it. .TP .BI \-o owner Change ownership of .I file to .I owner (superuser only). If no .B \-o option is given .I install will decide the owner to use: .br .in 1.5i \(bu if there is an existing .I target use its owner .br \(bu if we are the superuser use a compilation\(hydependent owner .br \(bu else use our effective uid .TP .in -.5i .B \-p Preserve the time stamp of .I files in .IR targets . .TP .B \-q Normally .I install informs you about a variety of possible errors. This option turns off that behavior and is not recommended except for special circumstances. Caveat emptor. .TP .B \-r Under .B \-d build all intervening directories between \*(lq/\*(rq and .IR destination. .TP .B \-R Remove (de\(hyinstall) a file by moving it into the OLD subdirectory. A temporary shell script is created to replace the .IR target , installed (which removes the existing .I target to \*(lqOLD\*(rq), and removed. .TP .B \-s Run .IR strip (1) on the installed .IR targets . .TP .BI \-S symlinks Specify a colon separated list of symbolic links that .I install should point at the installed file. (See EXAMPLES below.) .TP .B \-v Be verbose. Run .IR ls (1) on the backed up file and the .IR target . Notify the user of all side effects of this installation. .TP .B \-V View .IR install 's version and compilation\(hydependent owner, group and mode tables. .SH EXAMPLES .TP install motd /etc Install .I motd as .IR /etc/motd . If .I /etc/motd exists move it to .I /etc/OLD/motd and duplicate its ownership, group and mode; otherwise, use defaults. Create the directory .I /etc/OLD if it does not exist. .TP install \-c1 \-m 755 foo.sh /etc/foo Install .IR foo.sh . as .IR /etc/foo . Do not unlink .I foo.sh after the installation. Set permissions appropriate for a shell script on .IR /etc/foo . If .I /etc/OLD/foo exists, overwrite it instead of moving it to a new name (i.e., retain a single backup of .I /etc/foo in .IR /etc/OLD ). .TP install foo bar baz /usr/lib Install the files .IR foo , .I bar and .I baz as .IR /usr/lib/foo , .IR /usr/lib/bar , and .IR /usr/lib/baz . Use the modes of the existing files or defaults. .TP install \-vsm6751 \-oroot \-gkmem sendmail /usr/lib Install .I sendmail as .IR /usr/lib/sendmail , owned by root, grouped to kmem, and with the setuid and setgid permission bits set. Strip .I /usr/lib/sendmail after its installation and be verbose. .TP install \-d \-m \-rwxrwxrwt /tmp Build the directory .I /tmp with the default owner and group, mode 777, and with the \*(lqsticky\*(rq bit set. .TP install \-c \-m1755 \-Hview:vi:edit:e:/usr/bin/ex a.out /usr/ucb/ex Install the .I ex editor with all of its hard links .RI ( /usr/ucb/view , .IR /usr/ucb/vi , .IR /usr/ucb/edit , .IR /usr/ucb/e , .RI and /usr/bin/ex ). Replacing .B \-H with .B \-S would cause .I install to build symbolic links on machines which support them. .TP install \-d \-r /usr/local/lib/mk Recursively build any and all of the directories .IR /usr , .IR /usr/local , .IR /usr/local/lib , and .I /usr/local/lib/mk that do not already exist. Silently do nothing if they already exist (useful in Makefiles). .TP install \-V Output the version of install and a table of compiled in defaults. Output when run as the superuser might look similar to this, depending on the compilation defaults: .RS .TS l s s l s s l l l. install: version: $\&Id: main.c,v 6.7 64/02/15 16:21:41 ksb Exp $ install: configuration file: /usr/local/etc/install.cf install: syslog facility: 144 install: superuser defaults: install: owner is file=root dir=root OLD=root install: group is file=binary dir=binary OLD=binary install: mode is file=0755 dir=0755 OLD=inherited .TE .RE .TP rsh some.other.host install \- /etc/motd < motd.some.other.host This example shows a way to use \*(lq\-\*(rq to advantage. It is often useful when .IR rdist (1) is overkill or otherwise inappropriate. For instance, if you had a Makefile that generated files named .IR host1.motd , .IR host2.motd , .IR host3.motd , etc., and wanted to install them on those hosts, you could do something like this: .sp 1 .nf .na .KS .RS for host in host1 host2 host3; do .RS rsh $host install \- /etc/motd < $host.motd .RE done .KE .RE .fi .ad .sp 1 .SH DIAGNOSTICS .KS .PP Unless made quiet by .BR \-q , .I install will warn the installer if: .RS .br \(bu the owner, group, or mode changes .br \(bu the setuid, setgid, or sticky bits change .br \(bu a setuid program is loaded with the \*(lq#!\*(rq magic number .br \(bu \fItarget\fP does not exist (this is a prophylactic against typographical errors\(emsee CAVEATS below) .KE .RE .PP .KS .I Install will abort the installation if: .RS .br \(bu .I install cannot make a backup of an existing .I target .br \(bu a setuid program\'s owner was not specified with the mode .br \(bu a setgid program\'s group was not specified with the mode .br \(bu the specified owner, group, or mode failed to match the checkfile .br \(bu the superuser installs a setuid program that is not in the checkfile (this is a compile time option) .RE .KE .SH ENVIRONMENT .PP The environment variable .B INSTALL may be used to set command line options. Such options are read before any explicit command line options, e.g. .br .RS .sp 1 INSTALL=-v ; export INSTALL .sp 1 .RE .br will turn on \*(lqverbose\*(rq mode for all subsequent invocations of .IR install . .SH BUGS .PP .I Install does not use file locking, so it's possible for two competing .I install processes to lose data. .PP The trace option .RB ( \-n ) doesn't always show exactly what .I install would do. It will show OLD directories being made several times, and inherited modes don't propagate correctly under .BR \-drn . .SH CAVEATS .PP \(bu .I Install will not build character special files. Use .IR mknod (8) instead. .PP If /bin doesn't exist, the command: .sp 1 .RS install ls /bin .RE .sp 1 will make .I /bin be a copy of the binary .IR ls . This is an unavoidable consequence of allowing the .I destination to be a directory. You can avoid this by using \*(lqinstall \-d \fIdirectory\fP\*(rq in Makefiles to ensure that destination directories exist, e.g., .sp 1 .nf .na .RS install: product .RS install -d /bin install product /bin .RE .RE .fi .ad .sp 1 .I Install does nothing and exits normally if the directory already exists, so it's safe to include lines like this in your Makefiles. You can also use .IR instck (1L) to ensure that all system directories are built correctly. .PP \(bu .I Install can cover up mistakes of omission in Makefiles by copying the modes on a previously installed target. For example, the invocation of .I install in a Makefile might not specify that the .I destination should be setuid root, but as long as the .I target existed .I install would hide the error by copying the modes of the existing .IR target . The problem in the Makefile would not be found unless the .I target were removed prior to installation. Use .IR instck (1L) to generate a configuration file to avoid this problem. .PP \(bu .I Install and .IR purge (1L) assume that they own the file namespace in the \*(lqOLD\*(rq subdirectories. If other programs create, modify or delete files in the \*(lqOLD\*(rq subdirectories, they will probably collide with one of .I install or .IR purge (1L) eventually. .SH FILES .TS l l. /usr/local/lib/install.cf the default check list file .../OLD/#inst* temporary files made for installations .../OLD/#bogus* links made to running binaries to avoid ETXTBSY .../OLD/* copies of previously installed files .TE .SH AUTHORS Kevin Braunsdorf, Purdue University Computing Center (ksb@cc.purdue.edu) .br Jeff Smith, Purdue University Computing Center (jsmith@cc.purdue.edu) .br Copyright \*(co 1990 Purdue Research Foundation. All rights reserved. .SH "SEE ALSO" chgrp(1), instck(1L), ls(1), make(1), ranlib(1), strip(1), intro(2), syslog(3), install.cf(5L), chown(8), mknod(8), purge(8L) Purdue chmod 0444 install.d/install.1l || echo 'restore of install.d/install.1l failed' Wc_c="`wc -c < 'install.d/install.1l'`" test 21337 -eq "$Wc_c" || echo 'install.d/install.1l: original size 21337, current size' "$Wc_c" fi # ============= purge/purge.c ============== if test ! -d 'purge'; then echo 'x - creating directory purge' mkdir 'purge' fi if test -f 'purge/purge.c' -a X"$1" != X"-c"; then echo 'x - skipping purge/purge.c (File already exists)' else echo 'x - extracting purge/purge.c (Text)' sed 's/^X//' << 'Purdue' > 'purge/purge.c' && /* X * a C version of purge(8L) (ksb) X * X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana X * 47907. All rights reserved. X * X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb X * X * This software is not subject to any license of the American Telephone X * and Telegraph Company or the Regents of the University of California. X * X * Permission is granted to anyone to use this software for any purpose on X * any computer system, and to alter it and redistribute it freely, subject X * to the following restrictions: X * X * 1. Neither the authors nor Purdue University are responsible for any X * consequences of the use of this software. X * X * 2. The origin of this software must not be misrepresented, either by X * explicit claim or by omission. Credit to the authors and Purdue X * University must appear in documentation and sources. X * X * 3. Altered versions must be plainly marked as such, and must not be X * misrepresented as being the original software. X * X * 4. This notice may not be removed or altered. X * X * X * The old shell version of purge suffered from 4 problems: X * X * 1\ it used the -ctime option to find that is local to PUCC X * X * 2\ it didn't handle symbolic links correctly X * X * 3\ it didn't handle error conditions well (st_nlink > 1) X * X * 4\ it was not useful to general users X * X * READ THIS! N.B. if you want to change purge: X * X * Do *NOT* change purge to delete empty OLD dirs. This will race X * with install and cause install the fail. (As is won't build the X * OLD dir, the dir will go away, install will try to build a link X * in it and fail.) X */ #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/file.h> #include <sys/time.h> X #include <pwd.h> #include <grp.h> #include <errno.h> #include <ctype.h> #include <stdio.h> X #include "configure.h" #include "install.h" #include "purge.h" #include "filedup.h" #include "main.h" X #if BSDDIR #include <sys/dir.h> #else #include <ndir.h> #endif X #if !defined(ENAMETOOLONG) #define ENAMETOOLONG E2BIG #endif X extern char *malloc(), *realloc(), *strcpy(), *strrchr(); X #ifndef lint static char copyright[] = "@(#) Copyright 1990 Purdue Research Foundation.\nAll rights reserved.\n"; #endif X static time_t X tCutOff, /* the last install time we keep */ X tCopyKeep; /* last temp copy to keep */ static char acRoot[] = /* could be "root" "lroot" or "0" */ X DEFDIROWNER; static char acOld[] = /* the name of our OLD dirs */ X OLDDIR; static char acBogus[] = /* component key for busy links */ X TMPBOGUS; static char acCopy[] = /* component key for temp copies */ X TMPINST; static FILEDUPS FDLinks; /* hard links database */ static int X bHaveRoot, /* are effective uid lets us be root */ X iCopy, /* strlen of acCopy */ X iBogus; /* strlen of acBogus */ X #define LSTAT lstat X struct { X int size; /* the size of the uid array itself */ X int count; /* number of ids we are keeping */ X uid_t *puids; /* the uid array we are keeping */ X char **ppclogins; /* the login names we found */ } ids = {0, 0, (uid_t *)0}; X /* this routine should check for all the OLD dir owners (ksb) X * (like `root', `news', `ingress', etc) X */ int isSysId(uid) uid_t uid; { X register int j; X X if (fAnyOwner) X return 1; X for (j = 0; j < ids.count; ++j) { X if (ids.puids[j] == uid) { X return 1; X } X } X return 0; } X /* X * is a string all digits (ksb) X */ int AllDigits(pcTemp) char *pcTemp; { X for (; '\000' != *pcTemp; ++pcTemp) { X if (!isdigit(*pcTemp)) X return 0; X } X return 1; } X /* X * this routine adds a new valid OLD dir owner to the list (ksb) X */ int AddHer(pcLogin) char *pcLogin; { X register int j; X register struct passwd *pwdAdd; X X if (AllDigits(pcLogin)) { X if ((struct passwd *)0 == (pwdAdd = getpwuid(atoi(pcLogin)))) { X fprintf(stderr, "%s: getpwuid: %s: %s\n", progname, pcLogin, strerror(errno)); X return 1; X } X } else if ((struct passwd *)0 == (pwdAdd = getpwnam(pcLogin))) { X fprintf(stderr, "%s: getpwname: %s: %s\n", progname, pcLogin, strerror(errno)); X return 1; X } X if (ids.size < ids.count+1) { X ids.size += 8; X if ((uid_t *)0 == ids.puids) { X ids.puids = (uid_t *)malloc(ids.size*sizeof(uid_t)); X } else { X ids.puids = (uid_t *)realloc((char *)ids.puids, ids.size*sizeof(uid_t)); X } X if ((char **)0 == ids.ppclogins) { X ids.ppclogins = (char **)malloc(ids.size*sizeof(char *)); X } else { X ids.ppclogins = (char **)realloc((char *)ids.ppclogins, ids.size*sizeof(char *)); X } X if ((uid_t *)0 == ids.puids || (char **)0 == ids.ppclogins) { X fprintf(stderr, "%s: out of memory in realloc\n", progname); X exit(2); X } X } X for (j = 0; j < ids.count; ++j) { X if (ids.puids[j] != pwdAdd->pw_uid) { X continue; X } X return 0; X } X ids.puids[j] = pwdAdd->pw_uid; X ids.ppclogins[j] = pcLogin; X ++ids.count; X return 0; } X /* X * this routine added the default user (the envoker) to the OLD (ksb) X * owners list (set up the time flags too) X */ void InitAll() { X register char *pcUser, *pcX; X struct passwd *pwdUser; X extern char *getenv(); X extern long time(); X X /* remove the XXXXX from the mktemp templates X */ X pcX = acBogus + (sizeof(acBogus)-2); X while ('X' == *pcX) X *pcX-- = '\000'; X pcX = acCopy + (sizeof(acCopy)-2); X while ('X' == *pcX) X *pcX-- = '\000'; X iBogus = strlen(acBogus); X iCopy = strlen(acCopy); X X FDInit(& FDLinks); X if (0 == getuid()) X fSuperUser = 1; X (void)time(&tCutOff); X tCopyKeep = tCutOff - ((time_t)(60*60*24)); X tCutOff -= ((time_t)(60*60*24))*iDays; X X (void)setpwent(); X X bHaveRoot = 0 == geteuid(); X #if defined(INST_FACILITY) X if (bHaveRoot && fExec) { X openlog(progname, 0, INST_FACILITY); X } #endif X if (fSuperUser) { X (void)AddHer(acRoot); X return; X } X X pcUser = getenv("USER"); X if ((char *)0 == pcUser || '\000' == pcUser[0]) { X pcUser = getenv("LOGNAME"); X } X if ((char *)0 == pcUser || '\000' == pcUser[0]) { X if ((struct passwd *)0 == (pwdUser = getpwuid(getuid()))) { X fprintf(stderr, "%s: getpwuid: %d: %s\n", progname, getuid(), strerror(errno)); X exit(1); X } X pcUser = pwdUser->pw_name; X } X pcUser = strcpy(malloc((strlen(pcUser)|7)+1), pcUser); X (void)AddHer(pcUser); } X X /* X * show the user what we are compiled to do, as best we can (ksb) X */ int Version() { X register int i; X X printf("%s: $Id: purge.c,v 3.1 90/11/26 12:26:41 ksb Exp $\n", progname); X printf("%s: default superuser login name: %s\n", progname, acRoot); X printf("%s: backup directory name: %s\n", progname, acOld); X printf("%s: valid backup directory owner", progname); X if (fAnyOwner) { X printf(": <any>"); X } else { X if (ids.count > 1) X printf("s"); X for (i = 0; i < ids.count; ++i) X printf("%c %s", i == 0 ? ':' : ',', ids.ppclogins[i]); X } #if defined(INST_FACILITY) X printf("\n%s: syslog facility %d\n", progname, INST_FACILITY); #else X printf("\n"); #endif X return 0; } X X /* X * get the user-level name for this node `plain file' or `socket' (ksb) X * return string, ref-out single char X */ char * NodeType(mType, pcChar) unsigned int mType; char *pcChar; { X auto char acWaste[2]; X X if ((char *)0 == pcChar) X pcChar = acWaste; X X switch (mType & S_IFMT) { X case S_IFDIR: X *pcChar = 'd'; X return "directory"; #if defined(S_IFSOCK) X case S_IFSOCK: X *pcChar = 's'; X return "socket"; #endif /* no sockets */ #if defined(S_IFIFO) X case S_IFIFO: X *pcChar = 'p'; X return "fifo"; #endif #if HAVE_SLINKS X case S_IFLNK: X *pcChar = 'l'; X return "symbolic link"; #endif X case S_IFBLK: X *pcChar = 'b'; X return "block device"; X case S_IFCHR: X *pcChar = 'c'; X return "character device"; X case S_IFREG: X case 0: X *pcChar = '-'; X return "plain file"; X default: X break; X } X *pcChar = '?'; X return "type unknown"; } X /* X * avoid putting . and .. in the dirs to check, we would loop (ksb) X */ int DotSel(pDE) struct direct *pDE; { X register char *pcName = pDE->d_name; X X return ! ('.' == pcName[0] && ('\000' == pcName[1] || '.' == pcName[1] && '\000' == pcName[2])); } X /* X * remove the file, or fake the removal (ksb) X */ void Remove(pcFile) char *pcFile; { X if (fVerbose) { X printf("%s: rm -f %s\n", progname, pcFile); X } X if (fExec && -1 == unlink(pcFile)) { #if defined(INST_FACILITY) X if (EBUSY == errno && fExec && bHaveRoot) { X syslog(LOG_INFO, "still busy `%s\'", pcFile); X } #endif X fprintf(stderr, "%s: unlink: %s: %s\n", progname, pcFile, strerror(errno)); X return; X } #if defined(INST_FACILITY) X if (fExec && bHaveRoot) { X syslog(LOG_INFO, "rm `%s\'", pcFile); X } #endif } X /* X * purge an OLD directory of outdated backup files (ksb) X * remove bogus links on any age X * do not touch #inst$$ files unless ctime is > boot time? X */ void Purge(pcOld, pstOld) char *pcOld; struct stat *pstOld; { X register struct direct *pDE; X register int i; X auto struct direct **ppDEDir; X auto char acFile[MAXPATHLEN+2], *pcLast; X auto int iLen; X auto struct stat stFile; X X iLen = MAXPATHLEN - strlen(pcOld); X if (iLen <= 0) { X fprintf(stderr, "%s: Purge: %.40s... %s\n", progname, pcOld, strerror(ENAMETOOLONG)); X return; X } X (void)strcpy(acFile, pcOld); X pcLast = acFile + (MAXPATHLEN - iLen); X if ('/' != pcLast[-1]) X *pcLast++ = '/', --iLen; X if (-1 == (i = scandir(pcOld, &ppDEDir, DotSel, (int (*)())0))) { X fprintf(stderr, "%s: scandir: %s: %s\n", progname, pcOld, strerror(errno)); X return; X } X for (; i > 0; free((char *)pDE)) { X pDE = ppDEDir[--i]; X if (pDE->d_namlen > iLen) { X fprintf(stderr, "%s: Purge: ...%s/%.20s... %s\n", progname, ((20 < MAXPATHLEN - iLen) ? pcOld : pcLast-20), pDE->d_name, strerror(ENAMETOOLONG)); X continue; X } X (void)strcpy(pcLast, pDE->d_name); X if (-1 == LSTAT(acFile, & stFile)) { X if (errno != ENOENT) { X fprintf(stderr, "%s: stat: %s: %s\n", progname, acFile, strerror(errno)); X } X continue; X } X switch (stFile.st_mode & S_IFMT) { X default: X fprintf(stderr, "%s: %s: is a %s?\n", progname, acFile, NodeType(stFile.st_mode, (char *)0)); X continue; #if HAVE_SLINKS X case S_IFLNK: #endif X case S_IFREG: X case 0: X break; X } X /* if the file is a bogus link to a running program, X * try to remove it always... X */ X if (0 == strncmp(pcLast, acBogus, iBogus)) { X Remove(acFile); X continue; X } X X /* if the file is a temp file used by install ignore it X * for at least 1 day, then remove it X */ X if (0 == strncmp(pcLast, acCopy, iCopy)) { X if (tCopyKeep <= stFile.st_ctime) { X continue; X } X } else if (tCutOff <= stFile.st_ctime) { X continue; X } X X /* sticky problem -- we should not remove the evidence of a X * poor install (the product might still be installed in a X * binary directory) so we need to keep this node here... X * but if we find it again we can remove it, see? X */ X if (stFile.st_nlink > 1) { X register char *pcLink; X X pcLink = FDAdd(&FDLinks, acFile, &stFile); X if (0 == strcmp(acFile, pcLink)) { X continue; X } X } X Remove(acFile); X } X free((char *)ppDEDir); } X /* X * scan under a directory of all the OLD dirs and purge them (ksb) X */ void Scan(pcDir) char *pcDir; { X register struct direct *pDE; X register int i; X auto struct direct **ppDEDir; X auto char acDown[MAXPATHLEN+2], *pcLast; X auto struct stat stDown; X auto int iLen; X X if ((char *)0 == pcDir || '\000' == *pcDir) X pcDir = "."; X iLen = MAXPATHLEN - strlen(pcDir); X if (iLen <= 0) { X fprintf(stderr, "%s: Scan: %.40s... %s\n", progname, pcDir, strerror(ENAMETOOLONG)); X return; X } X (void)strcpy(acDown, pcDir); X pcLast = acDown + (MAXPATHLEN - iLen); X if ('/' != pcLast[-1]) X *pcLast++ = '/', --iLen; X if (-1 == (i = scandir(pcDir, &ppDEDir, DotSel, (int (*)())0))) { X fprintf(stderr, "%s: scandir: %s: %s\n", progname, pcDir, strerror(errno)); X return; X } X for (; i > 0; free((char *)pDE)) { X pDE = ppDEDir[--i]; X if (pDE->d_namlen > iLen) { X fprintf(stderr, "%s: Scan: ...%s/%.20s... %s\n", progname, ((20 < MAXPATHLEN - iLen) ? pcDir : pcLast-20), pDE->d_name, strerror(ENAMETOOLONG)); X continue; X } X (void)strcpy(pcLast, pDE->d_name); X if (-1 == LSTAT(acDown, & stDown)) { X if (errno != ENOENT) { X fprintf(stderr, "%s: stat: %s: %s\n", progname, acDown, strerror(errno)); X } X continue; X } X if (0 == strcmp(acOld, pDE->d_name)) { X if (isSysId((uid_t)stDown.st_uid)) { X Purge(acDown, & stDown); X } X continue; X } X /* if this directory an nfs (NFS) mount point? X * We used to use: X * (255 == major(stDown.st_dev)) X */ X if (0 != (0x80 & major(stDown.st_dev))) { X continue; X } X if (S_IFDIR == (stDown.st_mode & S_IFMT)) { X Scan(acDown); X } X } X free((char *)ppDEDir); } X /* X * let purge read paths to purge from find(1) output (ksb) X */ static void DoStdin() { X static int fBeenHere = 0; X register char *pcNl; X auto char acIn[MAXPATHLEN+2]; X auto int c; X X if (fBeenHere++) { X fprintf(stderr, "%s: will not recurse on stdin (skipped)\n", progname); X return; X } X while ((char *)0 != fgets(acIn, MAXPATHLEN+1, stdin)) { X if ((char *)0 == (pcNl = strchr(acIn, '\n'))) { X fprintf(stderr, "%s: line too long on stdin\n", progname); X while (EOF != (c = getc(stdin))) { X if ('\n' == c) X break; X } X continue; X } X *pcNl = '\000'; X Which(acIn); X } X clearerr(stdin); X --fBeenHere; } X /* X * the user may have either given us /bin or /bin/OLD to purge. (ksb) X * choose the correct routine and purge it. X * X * if an old script gives us a number of days do / with that number (yucko). X */ void Which(pcDir) char *pcDir; { X register char *pcSlash; X auto struct stat stDir; X X if (AllDigits(pcDir) && -1 == access(pcDir, 0)) { X fprintf(stderr, "%s: warning: old style [ndays] option\n", progname); X iDays = atoi(pcDir); X pcDir = "/"; X } X if ('-' == pcDir[0] && '\000' == pcDir[1]) { X DoStdin(); X return; X } X if ((char *)0 == (pcSlash = strrchr(pcDir, '/'))) { X pcSlash = pcDir; X } else { X ++pcSlash; X } X if (0 == strcmp(acOld, pcSlash)) { X if (-1 == LSTAT(pcDir, & stDir)) { X fprintf(stderr, "%s: stat: %s: %s\n", progname, pcDir, strerror(errno)); X } else if (!isSysId((uid_t)stDir.st_uid)) { X fprintf(stderr, "%s: %s: %d: not a user to purge\n", progname, pcDir, stDir.st_uid); X } else { X Purge(pcDir, &stDir); X } X } else { X Scan(pcDir); X } } X /* X * this checks to see if we recovered from the errors we found (ksb) X */ static int StillBad(pAE) AE_ELEMENT *pAE; { X auto struct stat stThis; X register char *pcSlash; X X if (-1 != LSTAT(pAE->pcname, &stThis) && stThis.st_nlink > 1) { X if ((char *)0 != (pcSlash = strrchr(pAE->pcname, '/'))) { X *pcSlash = '\000'; X fprintf(stderr, "%s: %s/%s: link count %d, use `instck -i %s\' to repair\n", progname, pAE->pcname, pcSlash+1, stThis.st_nlink, pAE->pcname); X *pcSlash = '/'; X } else { X fprintf(stderr, "%s: %s: has a link count > 1, use instck to repair\n", progname, pAE->pcname); X } X } else { X Remove(pAE->pcname); X } X return 0; } X /* X * When done scan any file that have bad link counts and see if we (ksB) X * removed the other links via purging OLD dirs X */ void Done() { X (void)FDScan(FDLinks, StillBad); X #if defined(INST_FACILITY) X if (bHaveRoot && fExec) { X closelog(); X } #endif } Purdue chmod 0444 purge/purge.c || echo 'restore of purge/purge.c failed' Wc_c="`wc -c < 'purge/purge.c'`" test 14969 -eq "$Wc_c" || echo 'purge/purge.c: original size 14969, current size' "$Wc_c" fi # ============= install.d/special.i ============== if test -f 'install.d/special.i' -a X"$1" != X"-c"; then echo 'x - skipping install.d/special.i (File already exists)' else echo 'x - extracting install.d/special.i (Text)' sed 's/^X//' << 'Purdue' > 'install.d/special.i' && /* X * $Id: special.i,v 7.2 90/10/22 11:47:15 ksb Exp $ X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana X * 47907. All rights reserved. X * X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb X * X * This software is not subject to any license of the American Telephone X * and Telegraph Company or the Regents of the University of California. X * X * Permission is granted to anyone to use this software for any purpose on X * any computer system, and to alter it and redistribute it freely, subject X * to the following restrictions: X * X * 1. Neither the authors nor Purdue University are responsible for any X * consequences of the use of this software. X * X * 2. The origin of this software must not be misrepresented, either by X * explicit claim or by omission. Credit to the authors and Purdue X * University must appear in documentation and sources. X * X * 3. Altered versions must be plainly marked as such, and must not be X * misrepresented as being the original software. X * X * 4. This notice may not be removed or altered. X */ X /* X * this (long) C routine is expanded twice in instck, and only once X * in install. It is the one that searchs a config file. X */ X #if defined(INSTCK) /* X * Scan the gien dirs for pats in the check file list? (ksb) X * config file format: X * #file mode owner group strip message X * /unix -r??r--r-- root * n X * `*' = any X * `"' = last X * `=' = the default for this col X * `!' = not (!mode -> not exist, !group/!owner -> any but the one listed) X */ void DirCk(iCount, ppcDirs, pcConfig, pCL) int iCount; /* who many dirs to search */ char **ppcDirs; /* dirs to search */ #else /* install */ void /* X * Is the named file in the check file list? If so is it OK? (ksb) X * FAIL is file it in list and modes are bad X * else SUCCESS X * config file format: X * #file mode owner group strip message X * /unix -r??r--r-- root * n X * core !r?-?--?-- * * n X * `*' = any X * `"' = last X * `=' = the default for this col X * `!' = not (!mode -> not exist, !group/!owner -> any but the one listed) X */ Special(pcFile, pcConfig, pCL) char *pcFile; /* file we are searching for */ #endif char *pcConfig; /* check list file name to read */ CHLIST *pCL; /* pointer to the check list to build/use */ { X extern char *malloc(); X static FILE *fpCnf = (FILE *)0; X static char *pcCnf = "jms & ksb"; X static char acLine[MAXCNFLINE]; X static char acMesg[MAXCNFLINE]; X static char acEq[] = "=", acAny[] = "*"; X register char *pcSkip, *pcBlank; X register int iLine; #if defined(INSTCK) X register int i; X auto char *pcCurGlob; #else X auto char *pcLastComp; X auto char acFPath[MAXPATHLEN+1]; #endif X X pCL->ffound = FALSE; X X if ((char *)0 == pcConfig || '\000' == pcConfig[0]) { #if defined(INSTCK) X (void)printf("%s: no configuration file\n", progname); #endif X return; X } X if (NULL == fpCnf || pcCnf != pcConfig) { X if (NULL != fpCnf) X (void)fclose(fpCnf); X pcCnf = pcConfig; X if (NULL == (fpCnf = fopen(pcCnf, "r"))) { X (void)fprintf(stderr, "%s: fopen (checklist): %s: %s\n", progname, pcCnf, strerror(errno)); X exit(EXIT_OPT); X } #if defined(F_SETFD) X /* if we can we set the close-on-exec bit to be neat */ X (void)fcntl(fileno(fpCnf), F_SETFD, 1); #endif /* have close on exec bit to twidle */ X } else { X (void)rewind(fpCnf); X } X pCL->pcspecial = pcCnf; X #if defined(INSTALL) X if ('/' != pcFile[0]) { X (void)getwd(acFPath); X (void)strcat(acFPath, "/"); X (void)strcat(acFPath, pcFile); X pcFile = CompPath(acFPath); X } else { X (void)strcpy(acFPath, pcFile); /* don't munge argument! */ X pcFile = CompPath(acFPath); X } X X if ((char *)0 == (pcLastComp = strrchr(pcFile, '/'))) { X Die("nil pointer"); X } X ++pcLastComp; #endif X X /* we set the defaults: X * not found, plain file, mode, owner, group, no strip X */ X iLine = 0; X (void)strcpy(acLine, "* = = = =\n"); X (void)strcpy(acMesg, "default message"); X pCL->pcmesg = acMesg; X pCL->pclink = (char *)0; X pCL->acmode[0] = pCL->acmode[1] = '\000'; X X do { X if ((char *)0 == (pcBlank = strchr(acLine, '\n'))) { X (void)fprintf(stderr, "%s: %s(%d) line too long\n", progname, pcCnf, iLine); X exit(EXIT_OPT); X } X do { X *pcBlank = '\000'; X } while (pcBlank != acLine && (--pcBlank, isspace(*pcBlank))); X for (pcSkip = acLine; isspace(*pcSkip); ++pcSkip) X /*empty*/; X if ('\000' == pcSkip[0] || '#' == pcSkip[0]) X continue; X X for (pcBlank = pcSkip; ! isspace(*pcBlank); ++pcBlank) X /*empty*/; X while (isspace(*pcBlank)) X *pcBlank++ = '\000'; X pCL->iline = iLine; X pCL->pcpat = pcSkip; #if defined(INSTCK) X pcCurGlob = pcSkip; #else X if (0 != iLine) { X pCL->ffound = SamePath(pcSkip, RJust(pcSkip, pcFile, pcLastComp)); X } #endif X X /* mode or link */ X pcSkip = pcBlank; X for (pcBlank = pcSkip; ! isspace(*pcBlank); ++pcBlank) X /*empty*/; X while (isspace(*pcBlank)) X *pcBlank++ = '\000'; X X /* file is a link */ X if ('@' == pcSkip[0] || ':' == pcSkip[0]) { X pCL->pclink = strcpy(malloc(strlen(pcSkip)+1),pcSkip); X goto at_comment; X } else if ((char *)0 != pCL->pclink) { X free(pCL->pclink); X pCL->pclink = (char *)0; X } X if ('~' == pcSkip[0] || '!' == pcSkip[0] || '*' == pcSkip[0]) { X (void)strcpy(pCL->acmode, pcSkip); X if ('\000' != pcSkip[1]) { X CvtMode(pcSkip+1, &pCL->mmust, &pCL->moptional); X pCL->mtype = pCL->mmust & S_IFMT; X pCL->mmust &= 07777; X } else { X pCL->mtype = 0; X pCL->mmust = 0; X pCL->moptional = 0644; X } X } else if (EQUAL(pcSkip, acEq)) { X if ((char *)0 == DEFMODE) { X pCL->acmode[0] = '*'; X } else { X CvtMode(DEFMODE, &pCL->mmust, &pCL->moptional); X pCL->acmode[0] = '\000'; X } X } else if ('\"' == pcSkip[0] && '\000' == pcSkip[1]) { X /* use previous */; X } else { X CvtMode(pcSkip, &pCL->mmust, &pCL->moptional); X pCL->moptional &= 07777; X pCL->acmode[0] = '\000'; X } X if ('\000' == pCL->acmode[0]) { X pCL->mtype = pCL->mmust & S_IFMT; X pCL->mmust &= 07777; X (void)NodeType(pCL->mtype, pCL->acmode); X } X if (0 == pCL->mtype) { X pCL->mtype = S_IFREG; X } X X /* owner */ X pcSkip = pcBlank; X for (pcBlank = pcSkip; ! isspace(*pcBlank); ++pcBlank) X /*empty*/; X while (isspace(*pcBlank)) X *pcBlank++ = '\000'; X pCL->fbangowner = 0; X if ('!' == *pcSkip) { X pCL->fbangowner = 1; X ++pcSkip; X } X if (EQUAL(pcSkip, acEq)) { X if ((struct passwd *)0 != pwdDef) { X (void)strcpy(pCL->acowner, pwdDef->pw_name); X } else if ((char *)0 == DEFOWNER) { X (void)strcpy(pCL->acowner, acAny); X } else { X (void)strcpy(pCL->acowner, DEFOWNER); X } X } else if (! EQUAL(pcSkip, "\"")) { X (void)strcpy(pCL->acowner, pcSkip); X } X X /* group */ X pcSkip = pcBlank; X for (pcBlank = pcSkip; ! isspace(*pcBlank); ++pcBlank) X /*empty*/; X while (isspace(*pcBlank)) X *pcBlank++ = '\000'; X pCL->fbanggroup = 0; X if ('!' == *pcSkip) { X pCL->fbanggroup = 1; X ++pcSkip; X } X if (EQUAL(pcSkip, acEq)) { X if ((struct group *)0 != grpDef) { X (void)strcpy(pCL->acgroup, grpDef->gr_name); X } else if ((char *)0 == DEFGROUP) { X (void)strcpy(pCL->acgroup, acAny); X } else { X (void)strcpy(pCL->acgroup, DEFGROUP); X } X } else if (! EQUAL(pcSkip, "\"")) { X (void)strcpy(pCL->acgroup, pcSkip); X } X X /* strip or not */ X pcSkip = pcBlank; X for (pcBlank = pcSkip; '\000' != *pcBlank && ! isspace(*pcBlank); ++pcBlank) X /*empty*/; X while (isspace(*pcBlank)) X *pcBlank++ = '\000'; X if (EQUAL(pcSkip, acEq)) { X pCL->chstrip = CF_NONE; X } else switch (*pcSkip) { X case 's': /* yeah we should take these too */ X case 'S': X pCL->chstrip = CF_STRIP; X break; X case 'l': X case 'L': X pCL->chstrip = CF_RANLIB; X break; X default: X (void)fprintf(stderr, "%s: unknown strip indicator `%s\'\n", progname, pcSkip); X /* fall through to recover */ X case '-': X case 'n': X case 'N': X pCL->chstrip = CF_NONE; X break; X case '*': X case '\?': X pCL->chstrip = CF_ANY; X break; X case '\"': X break; X } X X at_comment: X if (! EQUAL(pcBlank, "\"")) { X (void)strcpy(acMesg, pcBlank); X } X X if (0 == iLine) X continue; X #if defined(INSTALL) X if (!pCL->ffound) { X continue; X } #endif X X if ('*' != pCL->acowner[0] || '\000' != pCL->acowner[1]) { X struct passwd *pwd; X if ((struct passwd *)0 != pwdDef && 0 == strcmp(pCL->acowner, pwdDef->pw_name)) { X pCL->uid = pwdDef->pw_uid; X } else if ((struct passwd *)0 == (pwd = getpwnam(pCL->acowner))) { X fprintf(stderr, "%s: getpwname: %s: no such user\n", progname, pCL->acowner); X continue; X } else { X pCL->uid = pwd->pw_uid; X } X } else if (0 != (S_ISUID & pCL->mmust)) { X BadSet(iLine, 'u', "user", pCL->acmode); X } X if ('*' != pCL->acgroup[0] || '\000' != pCL->acgroup[1]) { X struct group *grp; X if ((struct group *)0 != grpDef && 0 == strcmp(pCL->acgroup, grpDef->gr_name)) { X pCL->gid = grpDef->gr_gid; X } else if ((struct group *)0 == (grp = getgrnam(pCL->acgroup))) { X fprintf(stderr, "%s: getgrname: %s: no such group\n", progname, pCL->acgroup); X continue; X } else { X pCL->gid = grp->gr_gid; X } X } else if (0 != (S_ISGID & pCL->mmust)) { X BadSet(iLine, 'g', "group", pCL->acmode); X } X X ModetoStr(pCL->acmode+1, pCL->mmust, pCL->moptional); #if defined(INSTCK) X iMatches = 0; X if ('/' == *pcCurGlob) { X FileMatch(pcCurGlob+1, "/", DoCk); X if (0 == iMatches) { X NoMatches(pcCurGlob); X } X } else { X /* never a no-matches check here, might not have X * any command line dirs to search, etc [ksb] X */ X for (i = 0; i < iCount; ++i) { X FileMatch(pcCurGlob, ppcDirs[i], DoCk); X } X } #else X if (FALSE != fVerbose || FALSE != fTrace) { X (void)printf("%s: found `%s\' in %s\n", progname, pcFile, pcConfig); X } X break; #endif X } while (++iLine, NULL != fgets(acLine, MAXCNFLINE, fpCnf)); } Purdue chmod 0444 install.d/special.i || echo 'restore of install.d/special.i failed' Wc_c="`wc -c < 'install.d/special.i'`" test 9712 -eq "$Wc_c" || echo 'install.d/special.i: original size 9712, current size' "$Wc_c" fi # ============= purge/Makefile ============== if test -f 'purge/Makefile' -a X"$1" != X"-c"; then echo 'x - skipping purge/Makefile (File already exists)' else echo 'x - extracting purge/Makefile (Text)' sed 's/^X//' << 'Purdue' > 'purge/Makefile' && # $Id: Makefile.plain,v 7.2 90/11/28 08:52:39 ksb Exp $ # # Makefile for purge # X PROG= purge BIN= ${DESTDIR}/usr/local/etc MANROOT= ${DESTDIR}/usr/man X # where is the source code for install(1L)? INSTALLD= ../install.d #INSTALLD= /usr/src/usr.bin/install.d #INSTALLD= /usr/src/local/cmd/install.d X # which type of links to build #LN=ln LN=ln -s X L=../libopt #L=/usr/include/local I=/usr/include S=/usr/include/sys P= X INCLUDE= -I$L DEBUG= -O CDEFS= CFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE} X LINKH= configure.h install.h LINKC= LINK= ${LINKH} ${LINKC} GENH= ${LINKH} GENC= ${LINKC} GEN= ${GENC} ${GENH} HDR= main.h purge.h filedup.h SRC= main.c purge.c filedup.c DEP= ${GENC} ${SRC} OBJ= main.o purge.o filedup.o MAN= purge.8l OTHER= README purge.m SOURCE= Makefile ${OTHER} ${MAN} ${HDR} ${SRC} X all: ${PROG} X ${PROG}:$P ${OBJ} # ${CC} -o $@ ${CFLAGS} ${OBJ} -lopt # ${CC} -o $@ ${CFLAGS} ${OBJ} -L /usr/local/lib -lopt X ${CC} -o $@ ${CFLAGS} ${OBJ} ../libopt/libopt.a X #main.h: main.c # #main.c: ${PROG}.m # mkcmd std_help.m ${PROG}.m # -(cmp -s prog.c main.c || (cp prog.c main.c && echo main.c updated)) # -(cmp -s prog.h main.h || (cp prog.h main.h && echo main.h updated)) # rm -f prog.[ch] X swap: ${PROG}.m X mv Makefile Makefile.plain X mv Makefile.mkcmd Makefile X mkcmd std_help.m ${PROG}.m X -cmp -s prog.c main.c || echo main.c changed X -cmp -s prog.h main.h || echo main.h changed X rm -f prog.[ch] X make depend X links: ${LINK} X ${LINK}: X ${LN} ${INSTALLD}/$@ ./$@ X clean: FRC X rm -f Makefile.bak ${PROG} ${GEN} *.o a.out core errs lint.out tags X calls: ${SRC} ${HDR} ${GEN} FRC X calls ${CDEFS} ${INCLUDE} ${DEP} X depend: ${SRC} ${HDR} ${GEN} FRC X maketd ${CDEFS} ${INCLUDE} ${DEP} X dirs: ${BIN} X install: all dirs FRC X install -cs ${PROG} ${BIN}/${PROG} X lint: ${SRC} ${HDR} ${GEN} FRC X lint -h ${CDEFS} ${INCLUDE} ${DEP} X mkcat: ${MAN} X mkcat -r${MANROOT} ${MAN} X print: source FRC X lpr -J'${PROG} source' ${SOURCE} X source: ${SOURCE} X spotless: clean X rcsclean ${SOURCE} X tags: ${HDR} ${SRC} ${GEN} X ctags -t ${HDR} ${SRC} ${GEN} X / ${BIN}: X install -dr $@ X ${SOURCE}: X co -q $@ X FRC: X # DO NOT DELETE THIS LINE - maketd DEPENDS ON IT X main.o: $L/getopt.h configure.h install.h main.c purge.h X purge.o: configure.h filedup.h install.h main.h purge.c purge.h X filedup.o: filedup.c filedup.h X # *** Do not add anything here - It will go away. *** Purdue chmod 0644 purge/Makefile || echo 'restore of purge/Makefile failed' Wc_c="`wc -c < 'purge/Makefile'`" test 2355 -eq "$Wc_c" || echo 'purge/Makefile: original size 2355, current size' "$Wc_c" fi # ============= install.d/TODO ============== if test -f 'install.d/TODO' -a X"$1" != X"-c"; then echo 'x - skipping install.d/TODO (File already exists)' else echo 'x - extracting install.d/TODO (Text)' sed 's/^X//' << 'Purdue' > 'install.d/TODO' && # $Id: TODO,v 7.0 90/09/17 09:41:37 ksb Exp $ X Last thing install should do: X in the configuration file one should be able to note that a file should be owned (grouped/moded) to the same owner (group/mode) as another file in the file system: X /bin/df -rwxr-s?-x root :/dev/rf0a s reads disk X let the colon indicate that it is a file to stat to get the group from.... X kayessbee Purdue chmod 0444 install.d/TODO || echo 'restore of install.d/TODO failed' Wc_c="`wc -c < 'install.d/TODO'`" test 379 -eq "$Wc_c" || echo 'install.d/TODO: original size 379, current size' "$Wc_c" fi # ============= install.d/myself.cf ============== if test -f 'install.d/myself.cf' -a X"$1" != X"-c"; then echo 'x - skipping install.d/myself.cf (File already exists)' else echo 'x - extracting install.d/myself.cf (Text)' sed 's/^X//' << 'Purdue' > 'install.d/myself.cf' && # this configuration file is used *just* to install install, itself (ksb) # $Id: myself.cf,v 7.0 90/10/08 11:39:55 ksb Exp $ X /usr/bin/install -rwxr-xr-x root * ? OLD drwxr-xr-x root * - Purdue chmod 0444 install.d/myself.cf || echo 'restore of install.d/myself.cf failed' Wc_c="`wc -c < 'install.d/myself.cf'`" test 232 -eq "$Wc_c" || echo 'install.d/myself.cf: original size 232, current size' "$Wc_c" fi true || echo 'restore of install.d/special.c failed' echo End of part 4, continue with part 5 exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.