From 25e43cba725c919917fea240af20d74d1a50edd1 Mon Sep 17 00:00:00 2001 From: Bruce Evans Date: Wed, 2 Jul 1997 00:25:26 +0000 Subject: [PATCH] Import Lite2's src/sbin, except for XNSrouted and routed. All relevant files in src/sbin are off the vendor branch, so this doesn't change the active versions. --- sbin/badsect/badsect.c | 209 +++++ sbin/clri/clri.c | 131 ++++ sbin/disklabel/disklabel.5.5 | 388 ++++++++++ sbin/disklabel/disklabel.c | 1315 +++++++++++++++++++++++++++++++ sbin/fastboot/fastboot.8 | 69 ++ sbin/ifconfig/ifconfig.8 | 277 +++++++ sbin/init/init.8 | 295 +++++++ sbin/init/init.c | 1297 +++++++++++++++++++++++++++++++ sbin/ping/ping.8 | 328 ++++++++ sbin/ping/ping.c | 986 +++++++++++++++++++++++ sbin/route/ccitt_addr.c | 175 +++++ sbin/route/route.8 | 325 ++++++++ sbin/route/route.c | 1415 ++++++++++++++++++++++++++++++++++ sbin/savecore/savecore.c | 649 ++++++++++++++++ sbin/scsiformat/scsiformat.c | 664 ++++++++++++++++ sbin/shutdown/shutdown.8 | 162 ++++ sbin/shutdown/shutdown.c | 493 ++++++++++++ sbin/tunefs/tunefs.8 | 166 ++++ sbin/tunefs/tunefs.c | 327 ++++++++ 19 files changed, 9671 insertions(+) create mode 100644 sbin/badsect/badsect.c create mode 100644 sbin/clri/clri.c create mode 100644 sbin/disklabel/disklabel.5.5 create mode 100644 sbin/disklabel/disklabel.c create mode 100644 sbin/fastboot/fastboot.8 create mode 100644 sbin/ifconfig/ifconfig.8 create mode 100644 sbin/init/init.8 create mode 100644 sbin/init/init.c create mode 100644 sbin/ping/ping.8 create mode 100644 sbin/ping/ping.c create mode 100644 sbin/route/ccitt_addr.c create mode 100644 sbin/route/route.8 create mode 100644 sbin/route/route.c create mode 100644 sbin/savecore/savecore.c create mode 100644 sbin/scsiformat/scsiformat.c create mode 100644 sbin/shutdown/shutdown.8 create mode 100644 sbin/shutdown/shutdown.c create mode 100644 sbin/tunefs/tunefs.8 create mode 100644 sbin/tunefs/tunefs.c diff --git a/sbin/badsect/badsect.c b/sbin/badsect/badsect.c new file mode 100644 index 000000000000..f6f01ccc5dad --- /dev/null +++ b/sbin/badsect/badsect.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 1981, 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1981, 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)badsect.c 8.2 (Berkeley) 5/4/95"; +#endif /* not lint */ + +/* + * badsect + * + * Badsect takes a list of file-system relative sector numbers + * and makes files containing the blocks of which these sectors are a part. + * It can be used to contain sectors which have problems if these sectors + * are not part of the bad file for the pack (see bad144). For instance, + * this program can be used if the driver for the file system in question + * does not support bad block forwarding. + */ +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +union { + struct fs fs; + char fsx[SBSIZE]; +} ufs; +#define sblock ufs.fs +union { + struct cg cg; + char cgx[MAXBSIZE]; +} ucg; +#define acg ucg.cg +struct fs *fs; +int fso, fsi; +int errs; +long dev_bsize = 1; + +char buf[MAXBSIZE]; + +void rdfs __P((daddr_t, int, char *)); +int chkuse __P((daddr_t, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + daddr_t number; + struct stat stbuf, devstat; + register struct direct *dp; + DIR *dirp; + char name[BUFSIZ]; + + if (argc < 3) { + fprintf(stderr, "usage: badsect bbdir blkno [ blkno ]\n"); + exit(1); + } + if (chdir(argv[1]) < 0 || stat(".", &stbuf) < 0) { + perror(argv[1]); + exit(2); + } + strcpy(name, _PATH_DEV); + if ((dirp = opendir(name)) == NULL) { + perror(name); + exit(3); + } + while ((dp = readdir(dirp)) != NULL) { + strcpy(&name[5], dp->d_name); + if (stat(name, &devstat) < 0) { + perror(name); + exit(4); + } + if (stbuf.st_dev == devstat.st_rdev && + (devstat.st_mode & IFMT) == IFBLK) + break; + } + closedir(dirp); + if (dp == NULL) { + printf("Cannot find dev 0%o corresponding to %s\n", + stbuf.st_rdev, argv[1]); + exit(5); + } + if ((fsi = open(name, 0)) < 0) { + perror(name); + exit(6); + } + fs = &sblock; + rdfs(SBOFF, SBSIZE, (char *)fs); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); + for (argc -= 2, argv += 2; argc > 0; argc--, argv++) { + number = atoi(*argv); + if (chkuse(number, 1)) + continue; + if (mknod(*argv, IFMT|0600, dbtofsb(fs, number)) < 0) { + perror(*argv); + errs++; + } + } + printf("Don't forget to run ``fsck %s''\n", name); + exit(errs); +} + +int +chkuse(blkno, cnt) + daddr_t blkno; + int cnt; +{ + int cg; + daddr_t fsbn, bn; + + fsbn = dbtofsb(fs, blkno); + if ((unsigned)(fsbn+cnt) > fs->fs_size) { + printf("block %d out of range of file system\n", blkno); + return (1); + } + cg = dtog(fs, fsbn); + if (fsbn < cgdmin(fs, cg)) { + if (cg == 0 || (fsbn+cnt) > cgsblock(fs, cg)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } else { + if ((fsbn+cnt) > cgbase(fs, cg+1)) { + printf("block %d in non-data area: cannot attach\n", + blkno); + return (1); + } + } + rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)sblock.fs_cgsize, + (char *)&acg); + if (!cg_chkmagic(&acg)) { + fprintf(stderr, "cg %d: bad magic number\n", cg); + errs++; + return (1); + } + bn = dtogd(fs, fsbn); + if (isclr(cg_blksfree(&acg), bn)) + printf("Warning: sector %d is in use\n", blkno); + return (0); +} + +/* + * read a block from the file system + */ +void +rdfs(bno, size, bf) + daddr_t bno; + int size; + char *bf; +{ + int n; + + if (lseek(fsi, (off_t)bno * dev_bsize, SEEK_SET) < 0) { + printf("seek error: %ld\n", bno); + perror("rdfs"); + exit(1); + } + n = read(fsi, bf, size); + if (n != size) { + printf("read error: %ld\n", bno); + perror("rdfs"); + exit(1); + } +} diff --git a/sbin/clri/clri.c b/sbin/clri/clri.c new file mode 100644 index 000000000000..18412018e02b --- /dev/null +++ b/sbin/clri/clri.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rich $alz of BBN Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)clri.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct fs *sbp; + register struct dinode *ip; + register int fd; + struct dinode ibuf[MAXBSIZE / sizeof (struct dinode)]; + long generation, bsize; + off_t offset; + int inonum; + char *fs, sblock[SBSIZE]; + + if (argc < 3) { + (void)fprintf(stderr, "usage: clri filesystem inode ...\n"); + exit(1); + } + + fs = *++argv; + + /* get the superblock. */ + if ((fd = open(fs, O_RDWR, 0)) < 0) + err(1, "%s", fs); + if (lseek(fd, (off_t)(SBLOCK * DEV_BSIZE), SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, sblock, sizeof(sblock)) != sizeof(sblock)) + errx(1, "%s: can't read superblock", fs); + + sbp = (struct fs *)sblock; + if (sbp->fs_magic != FS_MAGIC) + errx(1, "%s: superblock magic number 0x%x, not 0x%x", + fs, sbp->fs_magic, FS_MAGIC); + bsize = sbp->fs_bsize; + + /* remaining arguments are inode numbers. */ + while (*++argv) { + /* get the inode number. */ + if ((inonum = atoi(*argv)) <= 0) + errx(1, "%s is not a valid inode number", *argv); + (void)printf("clearing %d\n", inonum); + + /* read in the appropriate block. */ + offset = ino_to_fsba(sbp, inonum); /* inode to fs blk */ + offset = fsbtodb(sbp, offset); /* fs blk disk blk */ + offset *= DEV_BSIZE; /* disk blk to bytes */ + + /* seek and read the block */ + if (lseek(fd, offset, SEEK_SET) < 0) + err(1, "%s", fs); + if (read(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + + /* get the inode within the block. */ + ip = &ibuf[ino_to_fsbo(sbp, inonum)]; + + /* clear the inode, and bump the generation count. */ + generation = ip->di_gen + 1; + memset(ip, 0, sizeof(*ip)); + ip->di_gen = generation; + + /* backup and write the block */ + if (lseek(fd, (off_t)-bsize, SEEK_CUR) < 0) + err(1, "%s", fs); + if (write(fd, ibuf, bsize) != bsize) + err(1, "%s", fs); + (void)fsync(fd); + } + (void)close(fd); + exit(0); +} diff --git a/sbin/disklabel/disklabel.5.5 b/sbin/disklabel/disklabel.5.5 new file mode 100644 index 000000000000..7b4f70b6c0a4 --- /dev/null +++ b/sbin/disklabel/disklabel.5.5 @@ -0,0 +1,388 @@ +.\" Copyright (c) 1987, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Symmetric Computer Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)disklabel.5.5 8.2 (Berkeley) 5/6/94 +.\" +.Dd May 6, 1994 +.Dt DISKLABEL 5 +.Os +.Sh NAME +.Nm disklabel +.Nd disk pack label +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +Each disk or disk pack on a system may contain a disk label +which provides detailed information +about the geometry of the disk and the partitions into which the disk +is divided. +It should be initialized when the disk is formatted, +and may be changed later with the +.Xr disklabel 8 +program. +This information is used by the system disk driver and by the bootstrap +program to determine how to program the drive +and where to find the filesystems on the disk partitions. +Additional information is used by the filesystem in order +to use the disk most efficiently and to locate important filesystem information. +The description of each partition contains an identifier for the partition +type (standard filesystem, swap area, etc.). +The filesystem updates the in-core copy of the label if it contains +incomplete information about the filesystem. +.Pp +The label is located in sector number +.Dv LABELSECTOR +of the drive, usually sector 0 where it may be found +without any information about the disk geometry. +It is at an offset +.Dv LABELOFFSET +from the beginning of the sector, to allow room for the initial bootstrap. +The disk sector containing the label is normally made read-only +so that it is not accidentally overwritten by pack-to-pack copies +or swap operations; +the +.Dv DIOCWLABEL +.Xr ioctl 2 , +which is done as needed by the +.Xr disklabel +program. +.Pp +A copy of the in-core label for a disk can be obtained with the +.Dv DIOCGDINFO +.Xr ioctl ; +this works with a file descriptor for a block or character (``raw'') device +for any partition of the disk. +The in-core copy of the label is set by the +.Dv DIOCSDINFO +.Xr ioctl . +The offset of a partition cannot generally be changed while it is open, +nor can it be made smaller while it is open. +One exception is that any change is allowed if no label was found +on the disk, and the driver was able to construct only a skeletal label +without partition information. +Finally, the +.Dv DIOCWDINFO +.Xr ioctl +operation sets the in-core label and then updates the on-disk label; +there must be an existing label on the disk for this operation to succeed. +Thus, the initial label for a disk or disk pack must be installed +by writing to the raw disk. +All of these operations are normally done using +.Xr disklabel . +.Pp +The format of the disk label, as specified in +.Aw Pa sys/disklabel.h , +is +.Bd -literal +/* +* Disk description table, see disktab(5) +*/ +#define DISKTAB "/etc/disktab" + +/* +* Each disk has a label which includes information about the hardware +* disk geometry, filesystem partitions, and drive specific information. +* The label is in block 0 or 1, possibly offset from the beginning +* to leave room for a bootstrap, etc. +*/ + +#ifndef LABELSECTOR +#define LABELSECTOR 0 /* sector containing label */ +#endif + +#ifndef LABELOFFSET +#define LABELOFFSET 64 /* offset of label in sector */ +#endif + +#define DISKMAGIC ((u_long) 0x82564557) /* The disk magic number */ +#ifndef MAXPARTITIONS +#define MAXPARTITIONS 8 +#endif + +#ifndef LOCORE +struct disklabel { + u_long d_magic; /* the magic number */ + short d_type; /* drive type */ + short d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) + to retrieve the values from /etc/disktab. + */ +#if defined(KERNEL) || defined(STANDALONE) + char d_packname[16]; /* pack identifier */ +#else + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; + +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 +#endif /* ! KERNEL or STANDALONE */ + + /* disk geometry: */ + u_long d_secsize; /* # of bytes per sector */ + u_long d_nsectors; /* # of data sectors per track */ + u_long d_ntracks; /* # of tracks per cylinder */ + u_long d_ncylinders; /* # of data cylinders per unit */ + u_long d_secpercyl; /* # of data sectors per cylinder */ + u_long d_secperunit; /* # of data sectors per unit */ + /* + * Spares (bad sector replacements) below + * are not counted in d_nsectors or d_secpercyl. + * Spare sectors are assumed to be physical sectors + * which occupy space at the end of each track and/or cylinder. + */ + u_short d_sparespertrack; /* # of spare sectors per track */ + u_short d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternate cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + u_long d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the formatter + * or controller when formatting. When interleaving is in use, + * logically adjacent sectors are not physically contiguous, + * but instead are separated by some number of sectors. + * It is specified as the ratio of physical sectors traversed + * per logical sector. Thus an interleave of 1:1 implies contiguous + * layout, while 2:1 implies that logical sector 0 is separated + * by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N + * relative to sector 0 on track N-1 on the same cylinder. + * Finally, d_cylskew is the offset of sector 0 on cylinder N + * relative to sector 0 on cylinder N-1. + */ + u_short d_rpm; /* rotational speed */ + u_short d_interleave; /* hardware sector interleave */ + u_short d_trackskew; /* sector 0 skew, per track */ + u_short d_cylskew; /* sector 0 skew, per cylinder */ + u_long d_headswitch; /* head switch time, usec */ + u_long d_trkseek; /* track-to-track seek, usec */ + u_long d_flags; /* generic flags */ +#define NDDATA 5 + u_long d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + u_long d_spare[NSPARE]; /* reserved for future use */ + u_long d_magic2; /* the magic number (again) */ + u_short d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + u_short d_npartitions; /* number of partitions in following */ + u_long d_bbsize; /* size of boot area at sn0, bytes */ + u_long d_sbsize; /* max size of fs superblock, bytes */ + struct partition { /* the partition table */ + u_long p_size; /* number of sectors in partition */ + u_long p_offset; /* starting sector */ + u_long p_fsize; /* filesystem basic fragment size */ + u_char p_fstype; /* filesystem type, see below */ + u_char p_frag; /* filesystem fragments per block */ + union { + u_short cpg; /* UFS: FS cylinders per group */ + u_short sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + u_short p_cpg; /* filesystem cylinders per group */ + } d_partitions[MAXPARTITIONS]; /* actually may be more */ +}; + +/* d_type values: */ +#define DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */ +#define DTYPE_MSCP 2 /* MSCP */ +#define DTYPE_DEC 3 /* other DEC (rk, rl) */ +#define DTYPE_SCSI 4 /* SCSI */ +#define DTYPE_ESDI 5 /* ESDI interface */ +#define DTYPE_ST506 6 /* ST506 etc. */ +#define DTYPE_HPIB 7 /* CS/80 on HP-IB */ +#define DTYPE_HPFL 8 /* HP Fiber-link */ +#define DTYPE_FLOPPY 10 /* floppy */ + +#ifdef DKTYPENAMES +static char *dktypenames[] = { + "unknown", + "SMD", + "MSCP", + "old DEC", + "SCSI", + "ESDI", + "ST506", + "HP-IB", + "HP-FL", + "type 9", + "floppy", + 0 +}; +#define DKMAXTYPES (sizeof(dktypenames) / sizeof(dktypenames[0]) - 1) +#endif + +/* +* Filesystem type and version. +* Used to interpret other filesystem-specific +* per-partition information. +*/ +#define FS_UNUSED 0 /* unused */ +#define FS_SWAP 1 /* swap */ +#define FS_V6 2 /* Sixth Edition */ +#define FS_V7 3 /* Seventh Edition */ +#define FS_SYSV 4 /* System V */ +#define FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */ +#define FS_V8 6 /* Eighth Edition, 4K blocks */ +#define FS_BSDFFS 7 /* 4.2BSD fast file system */ +#define FS_MSDOS 8 /* MSDOS file system */ +#define FS_BSDLFS 9 /* 4.4BSD log-structured file system */ +#define FS_OTHER 10 /* in use, but unknown/unsupported */ +#define FS_HPFS 11 /* OS/2 high-performance file system */ +#define FS_ISO9660 12 /* ISO 9660, normally CD-ROM */ +#define FS_BOOT 13 /* partition contains bootstrap */ + +#ifdef DKTYPENAMES +static char *fstypenames[] = { + "unused", + "swap", + "Version 6", + "Version 7", + "System V", + "4.1BSD", + "Eighth Edition", + "4.2BSD", + "MSDOS", + "4.4LFS", + "unknown", + "HPFS", + "ISO9660", + "boot", + 0 +}; +#define FSMAXTYPES (sizeof(fstypenames) / sizeof(fstypenames[0]) - 1) +#endif + +/* +* flags shared by various drives: +*/ +#define D_REMOVABLE 0x01 /* removable media */ +#define D_ECC 0x02 /* supports ECC */ +#define D_BADSECT 0x04 /* supports bad sector forw. */ +#define D_RAMDISK 0x08 /* disk emulator */ +#define D_CHAIN 0x10 /* can do back-back transfers */ + +/* +* Drive data for SMD. +*/ + +#define d_smdflags d_drivedata[0] +#define D_SSE 0x1 /* supports skip sectoring */ +#define d_mindist d_drivedata[1] +#define d_maxdist d_drivedata[2] +#define d_sdist d_drivedata[3] + +/* +* Drive data for ST506. +*/ +#define d_precompcyl d_drivedata[0] +#define d_gap3 d_drivedata[1] /* used only when formatting */ + +/* + * Drive data for SCSI. + */ +#define d_blind d_drivedata[0] + +#ifndef LOCORE +/* +* Structure used to perform a format +* or other raw operation, returning data +* and/or register values. +* Register identification and format +* are device- and driver-dependent. +*/ +struct format_op { + char *df_buf; + int df_count; /* value-result */ + daddr_t df_startblk; + int df_reg[8]; /* result */ +}; + +/* +* Structure used internally to retrieve +* information about a partition on a disk. +*/ +struct partinfo { + struct disklabel *disklab; + struct partition *part; +}; + +/* +* Disk-specific ioctls. +*/ + /* get and set disklabel; DIOCGPART used internally */ +#define DIOCGDINFO _IOR('d', 101, struct disklabel) /* get */ +#define DIOCSDINFO _IOW('d', 102, struct disklabel) /* set */ +#define DIOCWDINFO _IOW('d', 103, struct disklabel) /* set, update disk */ +#define DIOCGPART _IOW('d', 104, struct partinfo) /* get partition */ + +/* do format operation, read or write */ +#define DIOCRFORMAT _IOWR('d', 105, struct format_op) +#define DIOCWFORMAT _IOWR('d', 106, struct format_op) + +#define DIOCSSTEP _IOW('d', 107, int) /* set step rate */ +#define DIOCSRETRIES _IOW('d', 108, int) /* set # of retries */ +#define DIOCWLABEL _IOW('d', 109, int) /* write en/disable label */ + +#define DIOCSBAD _IOW('d', 110, struct dkbad) /* set kernel dkbad */ + +#endif LOCORE +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 +.Sh HISTORY +The +.Nm disklabel +function was introduced in +.Bx 4.3 Tahoe . diff --git a/sbin/disklabel/disklabel.c b/sbin/disklabel/disklabel.c new file mode 100644 index 000000000000..7a70c30829e2 --- /dev/null +++ b/sbin/disklabel/disklabel.c @@ -0,0 +1,1315 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Symmetric Computer Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)disklabel.c 8.4 (Berkeley) 5/4/95"; +/* from static char sccsid[] = "@(#)disklabel.c 1.2 (Symmetric) 11/28/85"; */ +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#define DKTYPENAMES +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * Disklabel: read and write disklabels. + * The label is usually placed on one of the first sectors of the disk. + * Many machines also place a bootstrap in the same area, + * in which case the label is embedded in the bootstrap. + * The bootstrap source must leave space at the proper offset + * for the label on such machines. + */ + +#ifdef tahoe +#define RAWPARTITION 'a' +#else +#define RAWPARTITION 'c' +#endif + +#ifndef BBSIZE +#define BBSIZE 8192 /* size of boot area, with label */ +#endif + +#ifdef tahoe +#define NUMBOOT 0 +#else +#if defined(hp300) || defined(hp800) +#define NUMBOOT 1 +#else +#define NUMBOOT 2 +#endif +#endif + +#define DEFEDITOR _PATH_VI +#define streq(a,b) (strcmp(a,b) == 0) + +char *dkname; +char *specname; +char tmpfil[] = _PATH_TMP; + +extern int errno; +char namebuf[BBSIZE], *np = namebuf; +struct disklabel lab; +struct disklabel *readlabel(), *makebootarea(); +char bootarea[BBSIZE]; + +#if NUMBOOT > 0 +int installboot; /* non-zero if we should install a boot program */ +char *bootbuf; /* pointer to buffer with remainder of boot prog */ +int bootsize; /* size of remaining boot program */ +char *xxboot; /* primary boot */ +char *bootxx; /* secondary boot */ +char boot0[MAXPATHLEN]; +char boot1[MAXPATHLEN]; +#endif + +enum { + UNSPEC, EDIT, NOWRITE, READ, RESTORE, WRITE, WRITEABLE, WRITEBOOT +} op = UNSPEC; + +int rflag; + +#ifdef DEBUG +int debug; +#define OPTIONS "BNRWb:ders:w" +#else +#define OPTIONS "BNRWb:ers:w" +#endif + + +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + register struct disklabel *lp; + FILE *t; + int ch, f, flag, error = 0; + char *name = 0; + + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { +#if NUMBOOT > 0 + case 'B': + ++installboot; + break; + case 'b': + xxboot = optarg; + break; +#if NUMBOOT > 1 + case 's': + bootxx = optarg; + break; +#endif +#endif + case 'N': + if (op != UNSPEC) + usage(); + op = NOWRITE; + break; + case 'R': + if (op != UNSPEC) + usage(); + op = RESTORE; + break; + case 'W': + if (op != UNSPEC) + usage(); + op = WRITEABLE; + break; + case 'e': + if (op != UNSPEC) + usage(); + op = EDIT; + break; + case 'r': + ++rflag; + break; + case 'w': + if (op != UNSPEC) + usage(); + op = WRITE; + break; +#ifdef DEBUG + case 'd': + debug++; + break; +#endif + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; +#if NUMBOOT > 0 + if (installboot) { + rflag++; + if (op == UNSPEC) + op = WRITEBOOT; + } else { + if (op == UNSPEC) + op = READ; + xxboot = bootxx = 0; + } +#else + if (op == UNSPEC) + op = READ; +#endif + if (argc < 1) + usage(); + + dkname = argv[0]; + if (dkname[0] != '/') { + (void)sprintf(np, "%sr%s%c", _PATH_DEV, dkname, RAWPARTITION); + specname = np; + np += strlen(specname) + 1; + } else + specname = dkname; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + if (f < 0 && errno == ENOENT && dkname[0] != '/') { + (void)sprintf(specname, "%sr%s", _PATH_DEV, dkname); + np = namebuf + strlen(specname) + 1; + f = open(specname, op == READ ? O_RDONLY : O_RDWR); + } + if (f < 0) + Perror(specname); + + switch(op) { + + case EDIT: + if (argc != 1) + usage(); + lp = readlabel(f); + error = edit(lp, f); + break; + + case NOWRITE: + flag = 0; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + + case READ: + if (argc != 1) + usage(); + lp = readlabel(f); + display(stdout, lp); + error = checklabel(lp); + break; + + case RESTORE: +#if NUMBOOT > 0 + if (installboot && argc == 3) { + makelabel(argv[2], 0, &lab); + argc--; + } +#endif + if (argc != 2) + usage(); + lp = makebootarea(bootarea, &lab, f); + if (!(t = fopen(argv[1], "r"))) + Perror(argv[1]); + if (getasciilabel(t, lp)) + error = writelabel(f, bootarea, lp); + break; + + case WRITE: + if (argc == 3) { + name = argv[2]; + argc--; + } + if (argc != 2) + usage(); + makelabel(argv[1], name, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = lab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + + case WRITEABLE: + flag = 1; + if (ioctl(f, DIOCWLABEL, (char *)&flag) < 0) + Perror("ioctl DIOCWLABEL"); + break; + +#if NUMBOOT > 0 + case WRITEBOOT: + { + struct disklabel tlab; + + lp = readlabel(f); + tlab = *lp; + if (argc == 2) + makelabel(argv[1], 0, &lab); + lp = makebootarea(bootarea, &lab, f); + *lp = tlab; + if (checklabel(lp) == 0) + error = writelabel(f, bootarea, lp); + break; + } +#endif + } + exit(error); +} + +/* + * Construct a prototype disklabel from /etc/disktab. As a side + * effect, set the names of the primary and secondary boot files + * if specified. + */ +makelabel(type, name, lp) + char *type, *name; + register struct disklabel *lp; +{ + register struct disklabel *dp; + char *strcpy(); + + dp = getdiskbyname(type); + if (dp == NULL) { + fprintf(stderr, "%s: unknown disk type\n", type); + exit(1); + } + *lp = *dp; +#if NUMBOOT > 0 + /* + * Set bootstrap name(s). + * 1. If set from command line, use those, + * 2. otherwise, check if disktab specifies them (b0 or b1), + * 3. otherwise, makebootarea() will choose ones based on the name + * of the disk special file. E.g. /dev/ra0 -> raboot, bootra + */ + if (!xxboot && lp->d_boot0) { + if (*lp->d_boot0 != '/') + (void)sprintf(boot0, "%s/%s", + _PATH_BOOTDIR, lp->d_boot0); + else + (void)strcpy(boot0, lp->d_boot0); + xxboot = boot0; + } +#if NUMBOOT > 1 + if (!bootxx && lp->d_boot1) { + if (*lp->d_boot1 != '/') + (void)sprintf(boot1, "%s/%s", + _PATH_BOOTDIR, lp->d_boot1); + else + (void)strcpy(boot1, lp->d_boot1); + bootxx = boot1; + } +#endif +#endif + /* d_packname is union d_boot[01], so zero */ + memset(lp->d_packname, 0, sizeof(lp->d_packname)); + if (name) + (void)strncpy(lp->d_packname, name, sizeof(lp->d_packname)); +} + +writelabel(f, boot, lp) + int f; + char *boot; + register struct disklabel *lp; +{ + register int i; + int flag; + + setbootflag(lp); + lp->d_magic = DISKMAGIC; + lp->d_magic2 = DISKMAGIC; + lp->d_checksum = 0; + lp->d_checksum = dkcksum(lp); + if (rflag) { + /* + * First set the kernel disk label, + * then write a label to the raw disk. + * If the SDINFO ioctl fails because it is unimplemented, + * keep going; otherwise, the kernel consistency checks + * may prevent us from changing the current (in-core) + * label. + */ + if (ioctl(f, DIOCSDINFO, lp) < 0 && + errno != ENODEV && errno != ENOTTY) { + l_perror("ioctl DIOCSDINFO"); + return (1); + } + (void)lseek(f, (off_t)0, SEEK_SET); + /* + * write enable label sector before write (if necessary), + * disable after writing. + */ + flag = 1; + if (ioctl(f, DIOCWLABEL, &flag) < 0) + perror("ioctl DIOCWLABEL"); + if (write(f, boot, lp->d_bbsize) != lp->d_bbsize) { + perror("write"); + return (1); + } +#if NUMBOOT > 0 + /* + * Output the remainder of the disklabel + */ + if (bootbuf && write(f, bootbuf, bootsize) != bootsize) { + perror("write"); + return(1); + } +#endif + flag = 0; + (void) ioctl(f, DIOCWLABEL, &flag); + } else if (ioctl(f, DIOCWDINFO, lp) < 0) { + l_perror("ioctl DIOCWDINFO"); + return (1); + } +#ifdef vax + if (lp->d_type == DTYPE_SMD && lp->d_flags & D_BADSECT) { + daddr_t alt; + + alt = lp->d_ncylinders * lp->d_secpercyl - lp->d_nsectors; + for (i = 1; i < 11 && i < lp->d_nsectors; i += 2) { + (void)lseek(f, (off_t)((alt + i) * lp->d_secsize), + SEEK_SET); + if (write(f, boot, lp->d_secsize) < lp->d_secsize) { + int oerrno = errno; + fprintf(stderr, "alternate label %d ", i/2); + errno = oerrno; + perror("write"); + } + } + } +#endif + return (0); +} + +l_perror(s) + char *s; +{ + int saverrno = errno; + + fprintf(stderr, "disklabel: %s: ", s); + + switch (saverrno) { + + case ESRCH: + fprintf(stderr, "No disk label on disk;\n"); + fprintf(stderr, + "use \"disklabel -r\" to install initial label\n"); + break; + + case EINVAL: + fprintf(stderr, "Label magic number or checksum is wrong!\n"); + fprintf(stderr, "(disklabel or kernel is out of date?)\n"); + break; + + case EBUSY: + fprintf(stderr, "Open partition would move or shrink\n"); + break; + + case EXDEV: + fprintf(stderr, + "Labeled partition or 'a' partition must start at beginning of disk\n"); + break; + + default: + errno = saverrno; + perror((char *)NULL); + break; + } +} + +/* + * Fetch disklabel for disk. + * Use ioctl to get label unless -r flag is given. + */ +struct disklabel * +readlabel(f) + int f; +{ + register struct disklabel *lp; + + if (rflag) { + if (read(f, bootarea, BBSIZE) < BBSIZE) + Perror(specname); + for (lp = (struct disklabel *)bootarea; + lp <= (struct disklabel *)(bootarea + BBSIZE - sizeof(*lp)); + lp = (struct disklabel *)((char *)lp + 16)) + if (lp->d_magic == DISKMAGIC && + lp->d_magic2 == DISKMAGIC) + break; + if (lp > (struct disklabel *)(bootarea+BBSIZE-sizeof(*lp)) || + lp->d_magic != DISKMAGIC || lp->d_magic2 != DISKMAGIC || + dkcksum(lp) != 0) { + fprintf(stderr, + "Bad pack magic number (label is damaged, or pack is unlabeled)\n"); + /* lp = (struct disklabel *)(bootarea + LABELOFFSET); */ + exit (1); + } + } else { + lp = &lab; + if (ioctl(f, DIOCGDINFO, lp) < 0) + Perror("ioctl DIOCGDINFO"); + } + return (lp); +} + +/* + * Construct a bootarea (d_bbsize bytes) in the specified buffer ``boot'' + * Returns a pointer to the disklabel portion of the bootarea. + */ +struct disklabel * +makebootarea(boot, dp, f) + char *boot; + register struct disklabel *dp; + int f; +{ + struct disklabel *lp; + register char *p; + int b; +#if NUMBOOT > 0 + char *dkbasename; + struct stat sb; +#endif + + /* XXX */ + if (dp->d_secsize == 0) { + dp->d_secsize = DEV_BSIZE; + dp->d_bbsize = BBSIZE; + } + lp = (struct disklabel *) + (boot + (LABELSECTOR * dp->d_secsize) + LABELOFFSET); + memset(lp, 0, sizeof *lp); +#if NUMBOOT > 0 + /* + * If we are not installing a boot program but we are installing a + * label on disk then we must read the current bootarea so we don't + * clobber the existing boot. + */ + if (!installboot) { + if (rflag) { + if (read(f, boot, BBSIZE) < BBSIZE) + Perror(specname); + memset(lp, 0, sizeof *lp); + } + return (lp); + } + /* + * We are installing a boot program. Determine the name(s) and + * read them into the appropriate places in the boot area. + */ + if (!xxboot || !bootxx) { + dkbasename = np; + if ((p = strrchr(dkname, '/')) == NULL) + p = dkname; + else + p++; + while (*p && !isdigit(*p)) + *np++ = *p++; + *np++ = '\0'; + + if (!xxboot) { + (void)sprintf(np, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + xxboot = np; + (void)sprintf(xxboot, "%s/%sboot", + _PATH_BOOTDIR, dkbasename); + np += strlen(xxboot) + 1; + } +#if NUMBOOT > 1 + if (!bootxx) { + (void)sprintf(np, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + if (access(np, F_OK) < 0 && dkbasename[0] == 'r') + dkbasename++; + bootxx = np; + (void)sprintf(bootxx, "%s/boot%s", + _PATH_BOOTDIR, dkbasename); + np += strlen(bootxx) + 1; + } +#endif + } +#ifdef DEBUG + if (debug) + fprintf(stderr, "bootstraps: xxboot = %s, bootxx = %s\n", + xxboot, bootxx ? bootxx : "NONE"); +#endif + + /* + * Strange rules: + * 1. One-piece bootstrap (hp300/hp800) + * up to d_bbsize bytes of ``xxboot'' go in bootarea, the rest + * is remembered and written later following the bootarea. + * 2. Two-piece bootstraps (vax/i386?/mips?) + * up to d_secsize bytes of ``xxboot'' go in first d_secsize + * bytes of bootarea, remaining d_bbsize-d_secsize filled + * from ``bootxx''. + */ + b = open(xxboot, O_RDONLY); + if (b < 0) + Perror(xxboot); +#if NUMBOOT > 1 + if (read(b, boot, (int)dp->d_secsize) < 0) + Perror(xxboot); + (void)close(b); + b = open(bootxx, O_RDONLY); + if (b < 0) + Perror(bootxx); + if (read(b, &boot[dp->d_secsize], (int)(dp->d_bbsize-dp->d_secsize)) < 0) + Perror(bootxx); +#else + if (read(b, boot, (int)dp->d_bbsize) < 0) + Perror(xxboot); + (void)fstat(b, &sb); + bootsize = (int)sb.st_size - dp->d_bbsize; + if (bootsize > 0) { + /* XXX assume d_secsize is a power of two */ + bootsize = (bootsize + dp->d_secsize-1) & ~(dp->d_secsize-1); + bootbuf = (char *)malloc((size_t)bootsize); + if (bootbuf == 0) + Perror(xxboot); + if (read(b, bootbuf, bootsize) < 0) { + free(bootbuf); + Perror(xxboot); + } + } +#endif + (void)close(b); +#endif + /* + * Make sure no part of the bootstrap is written in the area + * reserved for the label. + */ + for (p = (char *)lp; p < (char *)lp + sizeof(struct disklabel); p++) + if (*p) { + fprintf(stderr, + "Bootstrap doesn't leave room for disk label\n"); + exit(2); + } + return (lp); +} + +display(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register int i, j; + register struct partition *pp; + + fprintf(f, "# %s:\n", specname); + if ((unsigned) lp->d_type < DKMAXTYPES) + fprintf(f, "type: %s\n", dktypenames[lp->d_type]); + else + fprintf(f, "type: %d\n", lp->d_type); + fprintf(f, "disk: %.*s\n", sizeof(lp->d_typename), lp->d_typename); + fprintf(f, "label: %.*s\n", sizeof(lp->d_packname), lp->d_packname); + fprintf(f, "flags:"); + if (lp->d_flags & D_REMOVABLE) + fprintf(f, " removeable"); + if (lp->d_flags & D_ECC) + fprintf(f, " ecc"); + if (lp->d_flags & D_BADSECT) + fprintf(f, " badsect"); + fprintf(f, "\n"); + fprintf(f, "bytes/sector: %d\n", lp->d_secsize); + fprintf(f, "sectors/track: %d\n", lp->d_nsectors); + fprintf(f, "tracks/cylinder: %d\n", lp->d_ntracks); + fprintf(f, "sectors/cylinder: %d\n", lp->d_secpercyl); + fprintf(f, "cylinders: %d\n", lp->d_ncylinders); + fprintf(f, "rpm: %d\n", lp->d_rpm); + fprintf(f, "interleave: %d\n", lp->d_interleave); + fprintf(f, "trackskew: %d\n", lp->d_trackskew); + fprintf(f, "cylinderskew: %d\n", lp->d_cylskew); + fprintf(f, "headswitch: %d\t\t# milliseconds\n", lp->d_headswitch); + fprintf(f, "track-to-track seek: %d\t# milliseconds\n", lp->d_trkseek); + fprintf(f, "drivedata: "); + for (i = NDDATA - 1; i >= 0; i--) + if (lp->d_drivedata[i]) + break; + if (i < 0) + i = 0; + for (j = 0; j <= i; j++) + fprintf(f, "%d ", lp->d_drivedata[j]); + fprintf(f, "\n\n%d partitions:\n", lp->d_npartitions); + fprintf(f, + "# size offset fstype [fsize bsize cpg]\n"); + pp = lp->d_partitions; + for (i = 0; i < lp->d_npartitions; i++, pp++) { + if (pp->p_size) { + fprintf(f, " %c: %8d %8d ", 'a' + i, + pp->p_size, pp->p_offset); + if ((unsigned) pp->p_fstype < FSMAXTYPES) + fprintf(f, "%8.8s", fstypenames[pp->p_fstype]); + else + fprintf(f, "%8d", pp->p_fstype); + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + fprintf(f, " %5d %5d %5.5s ", + pp->p_fsize, pp->p_fsize * pp->p_frag, ""); + break; + + case FS_BSDFFS: + fprintf(f, " %5d %5d %5d ", + pp->p_fsize, pp->p_fsize * pp->p_frag, + pp->p_cpg); + break; + + default: + fprintf(f, "%20.20s", ""); + break; + } + fprintf(f, "\t# (Cyl. %4d", + pp->p_offset / lp->d_secpercyl); + if (pp->p_offset % lp->d_secpercyl) + putc('*', f); + else + putc(' ', f); + fprintf(f, "- %d", + (pp->p_offset + + pp->p_size + lp->d_secpercyl - 1) / + lp->d_secpercyl - 1); + if (pp->p_size % lp->d_secpercyl) + putc('*', f); + fprintf(f, ")\n"); + } + } + fflush(f); +} + +edit(lp, f) + struct disklabel *lp; + int f; +{ + register int c; + struct disklabel label; + FILE *fd; + char *mktemp(); + + (void) mktemp(tmpfil); + fd = fopen(tmpfil, "w"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't create\n", tmpfil); + return (1); + } + (void)fchmod(fileno(fd), 0600); + display(fd, lp); + fclose(fd); + for (;;) { + if (!editit()) + break; + fd = fopen(tmpfil, "r"); + if (fd == NULL) { + fprintf(stderr, "%s: Can't reopen for reading\n", + tmpfil); + break; + } + memset(&label, 0, sizeof(label)); + if (getasciilabel(fd, &label)) { + *lp = label; + if (writelabel(f, bootarea, lp) == 0) { + (void) unlink(tmpfil); + return (0); + } + } + printf("re-edit the label? [y]: "); fflush(stdout); + c = getchar(); + if (c != EOF && c != (int)'\n') + while (getchar() != (int)'\n') + ; + if (c == (int)'n') + break; + } + (void) unlink(tmpfil); + return (1); +} + +editit() +{ + register int pid, xpid; + int stat, omask; + extern char *getenv(); + + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = fork()) < 0) { + extern int errno; + + if (errno == EPROCLIM) { + fprintf(stderr, "You have too many processes\n"); + return(0); + } + if (errno != EAGAIN) { + perror("fork"); + return(0); + } + sleep(1); + } + if (pid == 0) { + register char *ed; + + sigsetmask(omask); + setgid(getgid()); + setuid(getuid()); + if ((ed = getenv("EDITOR")) == (char *)0) + ed = DEFEDITOR; + execlp(ed, ed, tmpfil, 0); + perror(ed); + exit(1); + } + while ((xpid = wait(&stat)) >= 0) + if (xpid == pid) + break; + sigsetmask(omask); + return(!stat); +} + +char * +skip(cp) + register char *cp; +{ + + while (*cp != '\0' && isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return ((char *)NULL); + return (cp); +} + +char * +word(cp) + register char *cp; +{ + register char c; + + while (*cp != '\0' && !isspace(*cp) && *cp != '#') + cp++; + if ((c = *cp) != '\0') { + *cp++ = '\0'; + if (c != '#') + return (skip(cp)); + } + return ((char *)NULL); +} + +/* + * Read an ascii label in from fd f, + * in the same format as that put out by display(), + * and fill in lp. + */ +getasciilabel(f, lp) + FILE *f; + register struct disklabel *lp; +{ + register char **cpp, *cp; + register struct partition *pp; + char *tp, *s, line[BUFSIZ]; + int v, lineno = 0, errors = 0; + + lp->d_bbsize = BBSIZE; /* XXX */ + lp->d_sbsize = SBSIZE; /* XXX */ + while (fgets(line, sizeof(line) - 1, f)) { + lineno++; + if (cp = strchr(line,'\n')) + *cp = '\0'; + cp = skip(line); + if (cp == NULL) + continue; + tp = strchr(cp, ':'); + if (tp == NULL) { + fprintf(stderr, "line %d: syntax error\n", lineno); + errors++; + continue; + } + *tp++ = '\0', tp = skip(tp); + if (streq(cp, "type")) { + if (tp == NULL) + tp = "unknown"; + cpp = dktypenames; + for (; cpp < &dktypenames[DKMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, tp)) { + lp->d_type = cpp - dktypenames; + goto next; + } + v = atoi(tp); + if ((unsigned)v >= DKMAXTYPES) + fprintf(stderr, "line %d:%s %d\n", lineno, + "Warning, unknown disk type", v); + lp->d_type = v; + continue; + } + if (streq(cp, "flags")) { + for (v = 0; (cp = tp) && *cp != '\0';) { + tp = word(cp); + if (streq(cp, "removeable")) + v |= D_REMOVABLE; + else if (streq(cp, "ecc")) + v |= D_ECC; + else if (streq(cp, "badsect")) + v |= D_BADSECT; + else { + fprintf(stderr, + "line %d: %s: bad flag\n", + lineno, cp); + errors++; + } + } + lp->d_flags = v; + continue; + } + if (streq(cp, "drivedata")) { + register int i; + + for (i = 0; (cp = tp) && *cp != '\0' && i < NDDATA;) { + lp->d_drivedata[i++] = atoi(cp); + tp = word(cp); + } + continue; + } + if (sscanf(cp, "%d partitions", &v) == 1) { + if (v == 0 || (unsigned)v > MAXPARTITIONS) { + fprintf(stderr, + "line %d: bad # of partitions\n", lineno); + lp->d_npartitions = MAXPARTITIONS; + errors++; + } else + lp->d_npartitions = v; + continue; + } + if (tp == NULL) + tp = ""; + if (streq(cp, "disk")) { + strncpy(lp->d_typename, tp, sizeof (lp->d_typename)); + continue; + } + if (streq(cp, "label")) { + strncpy(lp->d_packname, tp, sizeof (lp->d_packname)); + continue; + } + if (streq(cp, "bytes/sector")) { + v = atoi(tp); + if (v <= 0 || (v % 512) != 0) { + fprintf(stderr, + "line %d: %s: bad sector size\n", + lineno, tp); + errors++; + } else + lp->d_secsize = v; + continue; + } + if (streq(cp, "sectors/track")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_nsectors = v; + continue; + } + if (streq(cp, "sectors/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_secpercyl = v; + continue; + } + if (streq(cp, "tracks/cylinder")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ntracks = v; + continue; + } + if (streq(cp, "cylinders")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_ncylinders = v; + continue; + } + if (streq(cp, "rpm")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_rpm = v; + continue; + } + if (streq(cp, "interleave")) { + v = atoi(tp); + if (v <= 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_interleave = v; + continue; + } + if (streq(cp, "trackskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trackskew = v; + continue; + } + if (streq(cp, "cylinderskew")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_cylskew = v; + continue; + } + if (streq(cp, "headswitch")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_headswitch = v; + continue; + } + if (streq(cp, "track-to-track seek")) { + v = atoi(tp); + if (v < 0) { + fprintf(stderr, "line %d: %s: bad %s\n", + lineno, tp, cp); + errors++; + } else + lp->d_trkseek = v; + continue; + } + if ('a' <= *cp && *cp <= 'z' && cp[1] == '\0') { + unsigned part = *cp - 'a'; + + if (part > lp->d_npartitions) { + fprintf(stderr, + "line %d: bad partition name\n", lineno); + errors++; + continue; + } + pp = &lp->d_partitions[part]; +#define NXTNUM(n) { \ + cp = tp, tp = word(cp); \ + if (tp == NULL) \ + tp = cp; \ + (n) = atoi(cp); \ + } + + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition size\n", + lineno, cp); + errors++; + } else + pp->p_size = v; + NXTNUM(v); + if (v < 0) { + fprintf(stderr, + "line %d: %s: bad partition offset\n", + lineno, cp); + errors++; + } else + pp->p_offset = v; + cp = tp, tp = word(cp); + cpp = fstypenames; + for (; cpp < &fstypenames[FSMAXTYPES]; cpp++) + if ((s = *cpp) && streq(s, cp)) { + pp->p_fstype = cpp - fstypenames; + goto gottype; + } + if (isdigit(*cp)) + v = atoi(cp); + else + v = FSMAXTYPES; + if ((unsigned)v >= FSMAXTYPES) { + fprintf(stderr, "line %d: %s %s\n", lineno, + "Warning, unknown filesystem type", cp); + v = FS_UNUSED; + } + pp->p_fstype = v; + gottype: + + switch (pp->p_fstype) { + + case FS_UNUSED: /* XXX */ + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + break; + + case FS_BSDFFS: + NXTNUM(pp->p_fsize); + if (pp->p_fsize == 0) + break; + NXTNUM(v); + pp->p_frag = v / pp->p_fsize; + NXTNUM(pp->p_cpg); + break; + + default: + break; + } + continue; + } + fprintf(stderr, "line %d: %s: Unknown disklabel field\n", + lineno, cp); + errors++; + next: + ; + } + errors += checklabel(lp); + return (errors == 0); +} + +/* + * Check disklabel for errors and fill in + * derived fields according to supplied values. + */ +checklabel(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + + if (lp->d_secsize == 0) { + fprintf(stderr, "sector size %d\n", lp->d_secsize); + return (1); + } + if (lp->d_nsectors == 0) { + fprintf(stderr, "sectors/track %d\n", lp->d_nsectors); + return (1); + } + if (lp->d_ntracks == 0) { + fprintf(stderr, "tracks/cylinder %d\n", lp->d_ntracks); + return (1); + } + if (lp->d_ncylinders == 0) { + fprintf(stderr, "cylinders/unit %d\n", lp->d_ncylinders); + errors++; + } + if (lp->d_rpm == 0) + Warning("revolutions/minute %d", lp->d_rpm); + if (lp->d_secpercyl == 0) + lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; + if (lp->d_secperunit == 0) + lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; + if (lp->d_bbsize == 0) { + fprintf(stderr, "boot block size %d\n", lp->d_bbsize); + errors++; + } else if (lp->d_bbsize % lp->d_secsize) + Warning("boot block size %% sector-size != 0"); + if (lp->d_sbsize == 0) { + fprintf(stderr, "super block size %d\n", lp->d_sbsize); + errors++; + } else if (lp->d_sbsize % lp->d_secsize) + Warning("super block size %% sector-size != 0"); + if (lp->d_npartitions > MAXPARTITIONS) + Warning("number of partitions (%d) > MAXPARTITIONS (%d)", + lp->d_npartitions, MAXPARTITIONS); + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0 && pp->p_offset != 0) + Warning("partition %c: size 0, but offset %d", + part, pp->p_offset); +#ifdef notdef + if (pp->p_size % lp->d_secpercyl) + Warning("partition %c: size %% cylinder-size != 0", + part); + if (pp->p_offset % lp->d_secpercyl) + Warning("partition %c: offset %% cylinder-size != 0", + part); +#endif + if (pp->p_offset > lp->d_secperunit) { + fprintf(stderr, + "partition %c: offset past end of unit\n", part); + errors++; + } + if (pp->p_offset + pp->p_size > lp->d_secperunit) { + fprintf(stderr, + "partition %c: partition extends past end of unit\n", + part); + errors++; + } + } + for (; i < MAXPARTITIONS; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size || pp->p_offset) + Warning("unused partition %c: size %d offset %d", + 'a' + i, pp->p_size, pp->p_offset); + } + return (errors); +} + +/* + * If we are installing a boot program that doesn't fit in d_bbsize + * we need to mark those partitions that the boot overflows into. + * This allows newfs to prevent creation of a filesystem where it might + * clobber bootstrap code. + */ +setbootflag(lp) + register struct disklabel *lp; +{ + register struct partition *pp; + int i, errors = 0; + char part; + u_long boffset; + + if (bootbuf == 0) + return; + boffset = bootsize / lp->d_secsize; + for (i = 0; i < lp->d_npartitions; i++) { + part = 'a' + i; + pp = &lp->d_partitions[i]; + if (pp->p_size == 0) + continue; + if (boffset <= pp->p_offset) { + if (pp->p_fstype == FS_BOOT) + pp->p_fstype = FS_UNUSED; + } else if (pp->p_fstype != FS_BOOT) { + if (pp->p_fstype != FS_UNUSED) { + fprintf(stderr, + "boot overlaps used partition %c\n", + part); + errors++; + } else { + pp->p_fstype = FS_BOOT; + Warning("boot overlaps partition %c, %s", + part, "marked as FS_BOOT"); + } + } + } + if (errors) { + fprintf(stderr, "Cannot install boot program\n"); + exit(4); + } +} + +/*VARARGS1*/ +Warning(fmt, a1, a2, a3, a4, a5) + char *fmt; +{ + + fprintf(stderr, "Warning, "); + fprintf(stderr, fmt, a1, a2, a3, a4, a5); + fprintf(stderr, "\n"); +} + +Perror(str) + char *str; +{ + fputs("disklabel: ", stderr); perror(str); + exit(4); +} + +usage() +{ +#if NUMBOOT > 0 + fprintf(stderr, +"%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n%s\n\t%s\n", +"usage: disklabel [-r] disk", + "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", + "(to write label with existing boot program)", +"or disklabel -e [-r] disk", + "(to edit label)", +"or disklabel -R [-r] disk protofile", + "(to restore label with existing boot program)", +#if NUMBOOT > 1 +"or disklabel -B [ -b boot1 [ -s boot2 ] ] disk [ type ]", + "(to install boot program with existing label)", +"or disklabel -w -B [ -b boot1 [ -s boot2 ] ] disk type [ packid ]", + "(to write label and boot program)", +"or disklabel -R -B [ -b boot1 [ -s boot2 ] ] disk protofile [ type ]", + "(to restore label and boot program)", +#else +"or disklabel -B [ -b bootprog ] disk [ type ]", + "(to install boot program with existing on-disk label)", +"or disklabel -w -B [ -b bootprog ] disk type [ packid ]", + "(to write label and install boot program)", +"or disklabel -R -B [ -b bootprog ] disk protofile [ type ]", + "(to restore label and install boot program)", +#endif +"or disklabel [-NW] disk", + "(to write disable/enable label)"); +#else + fprintf(stderr, "%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n%-43s%s\n", +"usage: disklabel [-r] disk", "(to read label)", +"or disklabel -w [-r] disk type [ packid ]", "(to write label)", +"or disklabel -e [-r] disk", "(to edit label)", +"or disklabel -R [-r] disk protofile", "(to restore label)", +"or disklabel [-NW] disk", "(to write disable/enable label)"); +#endif + exit(1); +} diff --git a/sbin/fastboot/fastboot.8 b/sbin/fastboot/fastboot.8 new file mode 100644 index 000000000000..2f6ac829da99 --- /dev/null +++ b/sbin/fastboot/fastboot.8 @@ -0,0 +1,69 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fastboot.8 8.1 (Berkeley) 6/5/93 +.\" +.Dd June 5, 1993 +.Dt FASTBOOT 8 +.Os BSD 4.2 +.Sh NAME +.Nm fastboot , +.Nm fasthalt +.Nd "reboot/halt the system without checking the disks" +.Sh SYNOPSIS +.Nm fastboot +.Op Ar boot-options +.Nm fasthalt +.Op Ar halt-options +.Sh DESCRIPTION +.Nm Fastboot +and +.Nm fasthalt +are shell scripts which reboot and halt the system without +checking the file systems. This is done by creating a +file +.Pa /fastboot , +then invoking the +.Xr reboot +program. The system startup script, +.Pa /etc/rc , +looks for this file and, if present, skips the normal +invocation of +.Xr fsck 8 . +.Sh SEE ALSO +.Xr halt 8 , +.Xr reboot 8 , +.Xr rc 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 new file mode 100644 index 000000000000..b7bc4dbb6b7c --- /dev/null +++ b/sbin/ifconfig/ifconfig.8 @@ -0,0 +1,277 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt IFCONFIG 8 +.Os BSD 4.2 +.Sh NAME +.Nm ifconfig +.Nd configure network interface parameters +.Sh SYNOPSIS +.Nm ifconfig +.Ar interface address_family +.Oo +.Ar address +.Op Ar dest_address +.Oc +.Op Ar parameters +.Nm ifconfig +.Ar interface +.Op Ar protocol_family +.Sh DESCRIPTION +.Nm Ifconfig +is used to assign an address +to a network interface and/or configure +network interface parameters. +.Nm Ifconfig +must be used at boot time to define the network address +of each interface present on a machine; it may also be used at +a later time to redefine an interface's address +or other operating parameters. +.Pp +Available operands for +.Nm ifconfig: +.Bl -tag -width Ds +.It Ar Address +For the +.Tn DARPA-Internet +family, +the address is either a host name present in the host name data +base, +.Xr hosts 5 , +or a +.Tn DARPA +Internet address expressed in the Internet standard +.Dq dot notation . +For the Xerox Network Systems(tm) family, +addresses are +.Ar net:a.b.c.d.e.f , +where +.Ar net +is the assigned network number (in decimal), +and each of the six bytes of the host number, +.Ar a +through +.Ar f , +are specified in hexadecimal. +The host number may be omitted on 10Mb/s Ethernet interfaces, +which use the hardware physical address, +and on interfaces other than the first. +For the +.Tn ISO +family, addresses are specified as a long hexadecimal string, +as in the Xerox family. However, two consecutive dots imply a zero +byte, and the dots are optional, if the user wishes to (carefully) +count out long strings of digits in network byte order. +.It Ar address_family +Specifies the +.Ar address family +which affects interpretation of the remaining parameters. +Since an interface can receive transmissions in differing protocols +with different naming schemes, specifying the address family is recommeded. +The address or protocol families currently +supported are +.Dq inet , +.Dq iso , +and +.Dq ns . +.It Ar Interface +The +.Ar interface +parameter is a string of the form +.Dq name unit , +for example, +.Dq en0 +.El +.Pp +The following parameters may be set with +.Nm ifconfig : +.Bl -tag -width dest_addressxx +.It Cm alias +Establish an additional network address for this interface. +This is sometimes useful when changing network numbers, and +one wishes to accept packets addressed to the old interface. +.It Cm arp +Enable the use of the Address Resolution Protocol in mapping +between network level addresses and link level addresses (default). +This is currently implemented for mapping between +.Tn DARPA +Internet +addresses and 10Mb/s Ethernet addresses. +.It Fl arp +Disable the use of the Address Resolution Protocol. +.It Cm broadcast +(Inet only) +Specify the address to use to represent broadcasts to the +network. +The default broadcast address is the address with a host part of all 1's. +.It Cm debug +Enable driver dependent debugging code; usually, this turns on +extra console error logging. +.It Fl debug +Disable driver dependent debugging code. +.ne 1i +.It Cm delete +Remove the network address specified. +This would be used if you incorrectly specified an alias, or it +was no longer needed. +If you have incorrectly set an NS address having the side effect +of specifying the host portion, removing all NS addresses will +allow you to respecify the host portion. +.It Cm dest_address +Specify the address of the correspondent on the other end +of a point to point link. +.It Cm down +Mark an interface ``down''. When an interface is +marked ``down'', the system will not attempt to +transmit messages through that interface. +If possible, the interface will be reset to disable reception as well. +This action does not automatically disable routes using the interface. +.It Cm ipdst +This is used to specify an Internet host who is willing to receive +ip packets encapsulating NS packets bound for a remote network. +An apparent point to point link is constructed, and +the address specified will be taken as the NS address and network +of the destination. +IP encapsulation of +.Tn CLNP +packets is done differently. +.It Cm metric Ar n +Set the routing metric of the interface to +.Ar n , +default 0. +The routing metric is used by the routing protocol +.Pq Xr routed 8 . +Higher metrics have the effect of making a route +less favorable; metrics are counted as addition hops +to the destination network or host. +.It Cm netmask Ar mask +(Inet and ISO) +Specify how much of the address to reserve for subdividing +networks into sub-networks. +The mask includes the network part of the local address +and the subnet part, which is taken from the host field of the address. +The mask can be specified as a single hexadecimal number +with a leading 0x, with a dot-notation Internet address, +or with a pseudo-network name listed in the network table +.Xr networks 5 . +The mask contains 1's for the bit positions in the 32-bit address +which are to be used for the network and subnet parts, +and 0's for the host part. +The mask should contain at least the standard network portion, +and the subnet field should be contiguous with the network +portion. +.\" see +.\" Xr eon 5 . +.It Cm nsellength Ar n +.Pf ( Tn ISO +only) +This specifies a trailing number of bytes for a received +.Tn NSAP +used for local identification, the remaining leading part of which is +taken to be the +.Tn NET +(Network Entity Title). +The default value is 1, which is conformant to US +.Tn GOSIP . +When an ISO address is set in an ifconfig command, +it is really the +.Tn NSAP +which is being specified. +For example, in +.Tn US GOSIP , +20 hex digits should be +specified in the +.Tn ISO NSAP +to be assigned to the interface. +There is some evidence that a number different from 1 may be useful +for +.Tn AFI +37 type addresses. +.It Cm trailers +Request the use of a ``trailer'' link level encapsulation when +sending (default). +If a network interface supports +.Cm trailers , +the system will, when possible, encapsulate outgoing +messages in a manner which minimizes the number of +memory to memory copy operations performed by the receiver. +On networks that support the Address Resolution Protocol (see +.Xr arp 4 ; +currently, only 10 Mb/s Ethernet), +this flag indicates that the system should request that other +systems use trailers when sending to this host. +Similarly, trailer encapsulations will be sent to other +hosts that have made such requests. +Currently used by Internet protocols only. +.It Fl trailers +Disable the use of a ``trailer'' link level encapsulation. +.It Cm link[0-2] +Enable special processing of the link level of the interface. +These three options are interface specific in actual effect, however, +they are in general used to select special modes of operation. An example +of this is to enable SLIP compression. Currently, only used by SLIP. +.ne 1i +.It Fl link[0-2] +Disable special processing at the link level with the specified interface. +.It Cm up +Mark an interface ``up''. +This may be used to enable an interface after an ``ifconfig down.'' +It happens automatically when setting the first address on an interface. +If the interface was reset when previously marked down, +the hardware will be re-initialized. +.El +.Pp +.Pp +.Nm Ifconfig +displays the current configuration for a network interface +when no optional parameters are supplied. +If a protocol family is specified, +Ifconfig will report only the details specific to that protocol family. +.Pp +Only the super-user may modify the configuration of a network interface. +.Sh DIAGNOSTICS +Messages indicating the specified interface does not exit, the +requested address is unknown, or the user is not privileged and +tried to alter an interface's configuration. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr netintro 4 , +.Xr rc 8 , +.Xr routed 8 , +.\" .Xr eon 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/init/init.8 b/sbin/init/init.8 new file mode 100644 index 000000000000..8db766dedb31 --- /dev/null +++ b/sbin/init/init.8 @@ -0,0 +1,295 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Donn Seeley at Berkeley Software Design, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)init.8 8.6 (Berkeley) 5/26/95 +.\" +.Dd May 26, 1995 +.Dt INIT 8 +.Os BSD 4 +.Sh NAME +.Nm init +.Nd process control initialization +.Sh SYNOPSIS +.Nm init +.Sh DESCRIPTION +The +.Nm init +program +is the last stage of the boot process. +It normally runs the automatic reboot sequence as described in +.Xr reboot 8 , +and if this succeeds, begins multi-user operation. +If the reboot scripts fail, +.Nm init +commences single user operation by giving +the super-user a shell on the console. +The +.Nm init +program may be passed parameters +from the boot program to +prevent the system from going multi-user and to instead execute +a single user shell without starting the normal daemons. +The system is then quiescent for maintenance work and may +later be made to go to multi-user by exiting the +single-user shell (with ^D). +This +causes +.Nm init +to run the +.Pa /etc/rc +start up command file in fastboot mode (skipping disk checks). +.Pp +If the +.Nm console +entry in the +.Xr ttys 5 +file is marked ``insecure'', +then +.Nm init +will require that the superuser password be +entered before the system will start a single-user shell. +The password check is skipped if the +.Nm console +is marked as ``secure''. +.Pp +The kernel runs with four different levels of security. +Any superuser process can raise the security level, but only +.Nm init +can lower it. +Security levels are defined as follows: +.Bl -tag -width flag +.It Ic -1 +Permanently insecure mode \- always run system in level 0 mode. +.It Ic 0 +Insecure mode \- immutable and append-only flags may be turned off. +All devices may be read or written subject to their permissions. +.It Ic 1 +Secure mode \- immutable and append-only flags may not be changed; +disks for mounted filesystems, +.Pa /dev/mem , +and +.Pa /dev/kmem +are read-only. +The +.Xr settimeofday 2 +system call can only advance the time. +.It Ic 2 +Highly secure mode \- same as secure mode, plus disks are always +read-only whether mounted or not. +This level precludes tampering with filesystems by unmounting them, +but also inhibits running +.Xr newfs 8 +while the system is multi-user. +.El +.Pp +Normally, the system runs in level 0 mode while single user +and in level 1 mode while multiuser. +If the level 2 mode is desired while running multiuser, +it can be set in the startup script +.Pa /etc/rc +using +.Xr sysctl 8 . +If it is desired to run the system in level 0 mode while multiuser, +the administrator must build a kernel with the variable +.Nm securelevel +defined in the file +.Pa /sys/compile/MACHINE/param.c +and initialize it to -1. +.Pp +In multi-user operation, +.Nm init +maintains +processes for the terminal ports found in the file +.Xr ttys 5 . +.Nm Init +reads this file, and executes the command found in the second field. +This command is usually +.Xr getty 8 ; +.Xr getty +opens and initializes the tty line +and +executes the +.Xr login +program. +The +.Xr login +program, when a valid user logs in, +executes a shell for that user. When this shell +dies, either because the user logged out +or an abnormal termination occurred (a signal), +the +.Nm init +program wakes up, deletes the user +from the +.Xr utmp 5 +file of current users and records the logout in the +.Xr wtmp +file. +The cycle is +then restarted by +.Nm init +executing a new +.Xr getty +for the line. +.pl +1 +.Pp +Line status (on, off, secure, getty, or window information) +may be changed in the +.Xr ttys +file without a reboot by sending the signal +.Dv SIGHUP +to +.Nm init +with the command +.Dq Li "kill \-s HUP 1" . +On receipt of this signal, +.Nm init +re-reads the +.Xr ttys +file. +When a line is turned off in +.Xr ttys , +.Nm init +will send a SIGHUP signal to the controlling process +for the session associated with the line. +For any lines that were previously turned off in the +.Xr ttys +file and are now on, +.Nm init +executes a new +.Xr getty +to enable a new login. +If the getty or window field for a line is changed, +the change takes effect at the end of the current +login session (e.g., the next time +.Nm init +starts a process on the line). +If a line is commented out or deleted from +.Xr ttys , +.Nm init +will not do anything at all to that line. +However, it will complain that the relationship between lines +in the +.Xr ttys +file and records in the +.Xr utmp +file is out of sync, +so this practice is not recommended. +.Pp +.Nm Init +will terminate multi-user operations and resume single-user mode +if sent a terminate +.Pq Dv TERM +signal, for example, +.Dq Li "kill \-s TERM 1" . +If there are processes outstanding that are deadlocked (because of +hardware or software failure), +.Xr init +will not wait for them all to die (which might take forever), but +will time out after 30 seconds and print a warning message. +.Pp +.Nm Init +will cease creating new +.Xr getty Ns 's +and allow the system to slowly die away, if it is sent a terminal stop +.Pq Dv TSTP +signal, i.e. +.Dq Li "kill \-s TSTP 1" . +A later hangup will resume full +multi-user operations, or a terminate will start a single user shell. +This hook is used by +.Xr reboot 8 +and +.Xr halt 8 . +.Pp +The role of +.Nm init +is so critical that if it dies, the system will reboot itself +automatically. +If, at bootstrap time, the +.Xr init +process cannot be located, the system will panic with the message +``panic: "init died (signal %d, exit %d)''. +.Sh DIAGNOSTICS +.Bl -diag +.It "getty repeating too quickly on port %s, sleeping" +A process being started to service a line is exiting quickly +each time it is started. +This is often caused by a ringing or noisy terminal line. +.Em "Init will sleep for 10 seconds" , +.Em "then continue trying to start the process" . +.Pp +.It "some processes would not die; ps axl advised." +A process +is hung and could not be killed when the system was shutting down. +This condition is usually caused by a process +that is stuck in a device driver because of +a persistent device error condition. +.El +.Sh FILES +.Bl -tag -width /var/log/wtmp -compact +.It Pa /dev/console +System console device. +.It Pa /dev/tty* +Terminal ports found in +.Xr ttys . +.It Pa /var/run/utmp +Record of Current users on the system. +.It Pa /var/log/wtmp +Record of all logins and logouts. +.It Pa /etc/ttys +The terminal initialization information file. +.It Pa /etc/rc +System startup commands. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr kill 1 , +.Xr sh 1 , +.Xr ttys 5 , +.Xr crash 8 , +.Xr getty 8 , +.Xr rc 8 , +.Xr reboot 8 , +.Xr halt 8 , +.Xr shutdown 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +.Sh BUGS +Systems without +.Xr sysctl +behave as though they have security level \-1. diff --git a/sbin/init/init.c b/sbin/init/init.c new file mode 100644 index 000000000000..316aa939283a --- /dev/null +++ b/sbin/init/init.c @@ -0,0 +1,1297 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Donn Seeley at Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)init.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __STDC__ +#include +#else +#include +#endif + +#ifdef SECURE +#include +#endif + +#include "pathnames.h" + +/* + * Until the mythical util.h arrives... + */ +extern int login_tty __P((int)); +extern int logout __P((const char *)); +extern void logwtmp __P((const char *, const char *, const char *)); + +/* + * Sleep times; used to prevent thrashing. + */ +#define GETTY_SPACING 5 /* N secs minimum getty spacing */ +#define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ +#define WINDOW_WAIT 3 /* wait N secs after starting window */ +#define STALL_TIMEOUT 30 /* wait N secs after warning */ +#define DEATH_WATCH 10 /* wait N secs for procs to die */ + +void handle __P((sig_t, ...)); +void delset __P((sigset_t *, ...)); + +void stall __P((char *, ...)); +void warning __P((char *, ...)); +void emergency __P((char *, ...)); +void disaster __P((int)); +void badsys __P((int)); + +/* + * We really need a recursive typedef... + * The following at least guarantees that the return type of (*state_t)() + * is sufficiently wide to hold a function pointer. + */ +typedef long (*state_func_t) __P((void)); +typedef state_func_t (*state_t) __P((void)); + +state_func_t single_user __P((void)); +state_func_t runcom __P((void)); +state_func_t read_ttys __P((void)); +state_func_t multi_user __P((void)); +state_func_t clean_ttys __P((void)); +state_func_t catatonia __P((void)); +state_func_t death __P((void)); + +enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; + +void transition __P((state_t)); +state_t requested_transition = runcom; + +void setctty __P((char *)); + +typedef struct init_session { + int se_index; /* index of entry in ttys file */ + pid_t se_process; /* controlling process */ + time_t se_started; /* used to avoid thrashing */ + int se_flags; /* status of session */ +#define SE_SHUTDOWN 0x1 /* session won't be restarted */ + char *se_device; /* filename of port */ + char *se_getty; /* what to run on that port */ + char **se_getty_argv; /* pre-parsed argument array */ + char *se_window; /* window system (started only once) */ + char **se_window_argv; /* pre-parsed argument array */ + struct init_session *se_prev; + struct init_session *se_next; +} session_t; + +void free_session __P((session_t *)); +session_t *new_session __P((session_t *, int, struct ttyent *)); +session_t *sessions; + +char **construct_argv __P((char *)); +void start_window_system __P((session_t *)); +void collect_child __P((pid_t)); +pid_t start_getty __P((session_t *)); +void transition_handler __P((int)); +void alrm_handler __P((int)); +void setsecuritylevel __P((int)); +int getsecuritylevel __P((void)); +int setupargv __P((session_t *, struct ttyent *)); +int clang; + +void clear_session_logs __P((session_t *)); + +int start_session_db __P((void)); +void add_session __P((session_t *)); +void del_session __P((session_t *)); +session_t *find_session __P((pid_t)); +DB *session_db; + +/* + * The mother of all processes. + */ +int +main(argc, argv) + int argc; + char **argv; +{ + int c; + struct sigaction sa; + sigset_t mask; + + + /* Dispose of random users. */ + if (getuid() != 0) { + (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); + exit (1); + } + + /* System V users like to reexec init. */ + if (getpid() != 1) { + (void)fprintf(stderr, "init: already running\n"); + exit (1); + } + + /* + * Note that this does NOT open a file... + * Does 'init' deserve its own facility number? + */ + openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); + + /* + * Create an initial session. + */ + if (setsid() < 0) + warning("initial setsid() failed: %m"); + + /* + * Establish an initial user so that programs running + * single user do not freak out and die (like passwd). + */ + if (setlogin("root") < 0) + warning("setlogin() failed: %m"); + + /* + * This code assumes that we always get arguments through flags, + * never through bits set in some random machine register. + */ + while ((c = getopt(argc, argv, "sf")) != -1) + switch (c) { + case 's': + requested_transition = single_user; + break; + case 'f': + runcom_mode = FASTBOOT; + break; + default: + warning("unrecognized flag '-%c'", c); + break; + } + + if (optind != argc) + warning("ignoring excess arguments"); + + /* + * We catch or block signals rather than ignore them, + * so that they get reset on exec. + */ + handle(badsys, SIGSYS, 0); + handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, + SIGBUS, SIGXCPU, SIGXFSZ, 0); + handle(transition_handler, SIGHUP, SIGTERM, SIGTSTP, 0); + handle(alrm_handler, SIGALRM, 0); + sigfillset(&mask); + delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, + SIGXCPU, SIGXFSZ, SIGHUP, SIGTERM, SIGTSTP, SIGALRM, 0); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTTIN, &sa, (struct sigaction *)0); + (void) sigaction(SIGTTOU, &sa, (struct sigaction *)0); + + /* + * Paranoia. + */ + close(0); + close(1); + close(2); + + /* + * Start the state machine. + */ + transition(requested_transition); + + /* + * Should never reach here. + */ + return 1; +} + +/* + * Associate a function with a signal handler. + */ +void +#ifdef __STDC__ +handle(sig_t handler, ...) +#else +handle(va_alist) + va_dcl +#endif +{ + int sig; + struct sigaction sa; + int mask_everything; + va_list ap; +#ifndef __STDC__ + sig_t handler; + + va_start(ap); + handler = va_arg(ap, sig_t); +#else + va_start(ap, handler); +#endif + + sa.sa_handler = handler; + sigfillset(&mask_everything); + + while (sig = va_arg(ap, int)) { + sa.sa_mask = mask_everything; + /* XXX SA_RESTART? */ + sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; + sigaction(sig, &sa, (struct sigaction *) 0); + } + va_end(ap); +} + +/* + * Delete a set of signals from a mask. + */ +void +#ifdef __STDC__ +delset(sigset_t *maskp, ...) +#else +delset(va_alist) + va_dcl +#endif +{ + int sig; + va_list ap; +#ifndef __STDC__ + sigset_t *maskp; + + va_start(ap); + maskp = va_arg(ap, sigset_t *); +#else + va_start(ap, maskp); +#endif + + while (sig = va_arg(ap, int)) + sigdelset(maskp, sig); + va_end(ap); +} + +/* + * Log a message and sleep for a while (to give someone an opportunity + * to read it and to save log or hardcopy output if the problem is chronic). + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +stall(char *message, ...) +#else +stall(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); + sleep(STALL_TIMEOUT); +} + +/* + * Like stall(), but doesn't sleep. + * If cpp had variadic macros, the two functions could be #defines for another. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +warning(char *message, ...) +#else +warning(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_ALERT, message, ap); + va_end(ap); +} + +/* + * Log an emergency message. + * NB: should send a message to the session logger to avoid blocking. + */ +void +#ifdef __STDC__ +emergency(char *message, ...) +#else +emergency(va_alist) + va_dcl +#endif +{ + va_list ap; +#ifndef __STDC__ + char *message; + + va_start(ap); + message = va_arg(ap, char *); +#else + va_start(ap, message); +#endif + + vsyslog(LOG_EMERG, message, ap); + va_end(ap); +} + +/* + * Catch a SIGSYS signal. + * + * These may arise if a system does not support sysctl. + * We tolerate up to 25 of these, then throw in the towel. + */ +void +badsys(sig) + int sig; +{ + static int badcount = 0; + + if (badcount++ < 25) + return; + disaster(sig); +} + +/* + * Catch an unexpected signal. + */ +void +disaster(sig) + int sig; +{ + emergency("fatal signal: %s", + sig < (unsigned) NSIG ? sys_siglist[sig] : "unknown signal"); + + sleep(STALL_TIMEOUT); + _exit(sig); /* reboot */ +} + +/* + * Get the security level of the kernel. + */ +int +getsecuritylevel() +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + size_t len; + extern int errno; + + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + len = sizeof curlevel; + if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { + emergency("cannot get kernel security level: %s", + strerror(errno)); + return (-1); + } + return (curlevel); +#else + return (-1); +#endif +} + +/* + * Set the security level of the kernel. + */ +void +setsecuritylevel(newlevel) + int newlevel; +{ +#ifdef KERN_SECURELVL + int name[2], curlevel; + extern int errno; + + curlevel = getsecuritylevel(); + if (newlevel == curlevel) + return; + name[0] = CTL_KERN; + name[1] = KERN_SECURELVL; + if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { + emergency( + "cannot change kernel security level from %d to %d: %s", + curlevel, newlevel, strerror(errno)); + return; + } +#ifdef SECURE + warning("kernel security level changed from %d to %d", + curlevel, newlevel); +#endif +#endif +} + +/* + * Change states in the finite state machine. + * The initial state is passed as an argument. + */ +void +transition(s) + state_t s; +{ + for (;;) + s = (state_t) (*s)(); +} + +/* + * Close out the accounting files for a login session. + * NB: should send a message to the session logger to avoid blocking. + */ +void +clear_session_logs(sp) + session_t *sp; +{ + char *line = sp->se_device + sizeof(_PATH_DEV) - 1; + + if (logout(line)) + logwtmp(line, "", ""); +} + +/* + * Start a session and allocate a controlling terminal. + * Only called by children of init after forking. + */ +void +setctty(name) + char *name; +{ + int fd; + + (void) revoke(name); + sleep (2); /* leave DTR low */ + if ((fd = open(name, O_RDWR)) == -1) { + stall("can't open %s: %m", name); + _exit(1); + } + if (login_tty(fd) == -1) { + stall("can't get %s for controlling terminal: %m", name); + _exit(1); + } +} + +/* + * Bring the system up single user. + */ +state_func_t +single_user() +{ + pid_t pid, wpid; + int status; + sigset_t mask; + char *shell = _PATH_BSHELL; + char *argv[2]; +#ifdef SECURE + struct ttyent *typ; + struct passwd *pp; + static const char banner[] = + "Enter root password, or ^D to go multi-user\n"; + char *clear, *password; +#endif + + /* + * If the kernel is in secure mode, downgrade it to insecure mode. + */ + if (getsecuritylevel() > 0) + setsecuritylevel(0); + + if ((pid = fork()) == 0) { + /* + * Start the single user session. + */ + setctty(_PATH_CONSOLE); + +#ifdef SECURE + /* + * Check the root password. + * We don't care if the console is 'on' by default; + * it's the only tty that can be 'off' and 'secure'. + */ + typ = getttynam("console"); + pp = getpwnam("root"); + if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp) { + write(2, banner, sizeof banner - 1); + for (;;) { + clear = getpass("Password:"); + if (clear == 0 || *clear == '\0') + _exit(0); + password = crypt(clear, pp->pw_passwd); + memset(clear, 0, _PASSWORD_LEN); + if (strcmp(password, pp->pw_passwd) == 0) + break; + warning("single-user login failed\n"); + } + } + endttyent(); + endpwent(); +#endif /* SECURE */ + +#ifdef DEBUGSHELL + { + char altshell[128], *cp = altshell; + int num; + +#define SHREQUEST \ + "Enter pathname of shell or RETURN for sh: " + (void)write(STDERR_FILENO, + SHREQUEST, sizeof(SHREQUEST) - 1); + while ((num = read(STDIN_FILENO, cp, 1)) != -1 && + num != 0 && *cp != '\n' && cp < &altshell[127]) + cp++; + *cp = '\0'; + if (altshell[0] != '\0') + shell = altshell; + } +#endif /* DEBUGSHELL */ + + /* + * Unblock signals. + * We catch all the interesting ones, + * and those are reset to SIG_DFL on exec. + */ + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + /* + * Fire off a shell. + * If the default one doesn't work, try the Bourne shell. + */ + argv[0] = "-sh"; + argv[1] = 0; + execv(shell, argv); + emergency("can't exec %s for single user: %m", shell); + execv(_PATH_BSHELL, argv); + emergency("can't exec %s for single user: %m", _PATH_BSHELL); + sleep(STALL_TIMEOUT); + _exit(1); + } + + if (pid == -1) { + /* + * We are seriously hosed. Do our best. + */ + emergency("can't fork single-user shell, trying again"); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + return (state_func_t) single_user; + } + + requested_transition = 0; + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for single-user shell failed: %m; restarting"); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: shell stopped, restarting\n"); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid && !requested_transition); + + if (requested_transition) + return (state_func_t) requested_transition; + + if (!WIFEXITED(status)) { + if (WTERMSIG(status) == SIGKILL) { + /* + * reboot(8) killed shell? + */ + warning("single user shell terminated."); + sleep(STALL_TIMEOUT); + _exit(0); + } else { + warning("single user shell terminated, restarting"); + return (state_func_t) single_user; + } + } + + runcom_mode = FASTBOOT; + return (state_func_t) runcom; +} + +/* + * Run the system startup script. + */ +state_func_t +runcom() +{ + pid_t pid, wpid; + int status; + char *argv[4]; + struct sigaction sa; + + if ((pid = fork()) == 0) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGTSTP, &sa, (struct sigaction *)0); + (void) sigaction(SIGHUP, &sa, (struct sigaction *)0); + + setctty(_PATH_CONSOLE); + + argv[0] = "sh"; + argv[1] = _PATH_RUNCOM; + argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : 0; + argv[3] = 0; + + sigprocmask(SIG_SETMASK, &sa.sa_mask, (sigset_t *) 0); + + execv(_PATH_BSHELL, argv); + stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); + _exit(1); /* force single user mode */ + } + + if (pid == -1) { + emergency("can't fork for %s on %s: %m", + _PATH_BSHELL, _PATH_RUNCOM); + while (waitpid(-1, (int *) 0, WNOHANG) > 0) + continue; + sleep(STALL_TIMEOUT); + return (state_func_t) single_user; + } + + /* + * Copied from single_user(). This is a bit paranoid. + */ + do { + if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) + collect_child(wpid); + if (wpid == -1) { + if (errno == EINTR) + continue; + warning("wait for %s on %s failed: %m; going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + if (wpid == pid && WIFSTOPPED(status)) { + warning("init: %s on %s stopped, restarting\n", + _PATH_BSHELL, _PATH_RUNCOM); + kill(pid, SIGCONT); + wpid = -1; + } + } while (wpid != pid); + + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && + requested_transition == catatonia) { + /* /etc/rc executed /sbin/reboot; wait for the end quietly */ + sigset_t s; + + sigfillset(&s); + for (;;) + sigsuspend(&s); + } + + if (!WIFEXITED(status)) { + warning("%s on %s terminated abnormally, going to single user mode", + _PATH_BSHELL, _PATH_RUNCOM); + return (state_func_t) single_user; + } + + if (WEXITSTATUS(status)) + return (state_func_t) single_user; + + runcom_mode = AUTOBOOT; /* the default */ + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "reboot", ""); + return (state_func_t) read_ttys; +} + +/* + * Open the session database. + * + * NB: We could pass in the size here; is it necessary? + */ +int +start_session_db() +{ + if (session_db && (*session_db->close)(session_db)) + emergency("session database close: %s", strerror(errno)); + if ((session_db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == 0) { + emergency("session database open: %s", strerror(errno)); + return (1); + } + return (0); + +} + +/* + * Add a new login session. + */ +void +add_session(sp) + session_t *sp; +{ + DBT key; + DBT data; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + data.data = &sp; + data.size = sizeof sp; + + if ((*session_db->put)(session_db, &key, &data, 0)) + emergency("insert %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Delete an old login session. + */ +void +del_session(sp) + session_t *sp; +{ + DBT key; + + key.data = &sp->se_process; + key.size = sizeof sp->se_process; + + if ((*session_db->del)(session_db, &key, 0)) + emergency("delete %d: %s", sp->se_process, strerror(errno)); +} + +/* + * Look up a login session by pid. + */ +session_t * +#ifdef __STDC__ +find_session(pid_t pid) +#else +find_session(pid) + pid_t pid; +#endif +{ + DBT key; + DBT data; + session_t *ret; + + key.data = &pid; + key.size = sizeof pid; + if ((*session_db->get)(session_db, &key, &data, 0) != 0) + return 0; + memmove(&ret, data.data, sizeof(ret)); + return ret; +} + +/* + * Construct an argument vector from a command line. + */ +char ** +construct_argv(command) + char *command; +{ + register int argc = 0; + register char **argv = (char **) malloc(((strlen(command) + 1) / 2 + 1) + * sizeof (char *)); + static const char separators[] = " \t"; + + if ((argv[argc++] = strtok(command, separators)) == 0) + return 0; + while (argv[argc++] = strtok((char *) 0, separators)) + continue; + return argv; +} + +/* + * Deallocate a session descriptor. + */ +void +free_session(sp) + register session_t *sp; +{ + free(sp->se_device); + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + if (sp->se_window) { + free(sp->se_window); + free(sp->se_window_argv); + } + free(sp); +} + +/* + * Allocate a new session descriptor. + */ +session_t * +new_session(sprev, session_index, typ) + session_t *sprev; + int session_index; + register struct ttyent *typ; +{ + register session_t *sp; + + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_name == 0 || + typ->ty_getty == 0) + return 0; + + sp = (session_t *) malloc(sizeof (session_t)); + memset(sp, 0, sizeof *sp); + + sp->se_index = session_index; + + sp->se_device = malloc(sizeof(_PATH_DEV) + strlen(typ->ty_name)); + (void) sprintf(sp->se_device, "%s%s", _PATH_DEV, typ->ty_name); + + if (setupargv(sp, typ) == 0) { + free_session(sp); + return (0); + } + + sp->se_next = 0; + if (sprev == 0) { + sessions = sp; + sp->se_prev = 0; + } else { + sprev->se_next = sp; + sp->se_prev = sprev; + } + + return sp; +} + +/* + * Calculate getty and if useful window argv vectors. + */ +int +setupargv(sp, typ) + session_t *sp; + struct ttyent *typ; +{ + + if (sp->se_getty) { + free(sp->se_getty); + free(sp->se_getty_argv); + } + sp->se_getty = malloc(strlen(typ->ty_getty) + strlen(typ->ty_name) + 2); + (void) sprintf(sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name); + sp->se_getty_argv = construct_argv(sp->se_getty); + if (sp->se_getty_argv == 0) { + warning("can't parse getty for port %s", sp->se_device); + free(sp->se_getty); + sp->se_getty = 0; + return (0); + } + if (typ->ty_window) { + if (sp->se_window) + free(sp->se_window); + sp->se_window = strdup(typ->ty_window); + sp->se_window_argv = construct_argv(sp->se_window); + if (sp->se_window_argv == 0) { + warning("can't parse window for port %s", + sp->se_device); + free(sp->se_window); + sp->se_window = 0; + return (0); + } + } + return (1); +} + +/* + * Walk the list of ttys and create sessions for each active line. + */ +state_func_t +read_ttys() +{ + int session_index = 0; + register session_t *sp, *snext; + register struct ttyent *typ; + + /* + * Destroy any previous session state. + * There shouldn't be any, but just in case... + */ + for (sp = sessions; sp; sp = snext) { + if (sp->se_process) + clear_session_logs(sp); + snext = sp->se_next; + free_session(sp); + } + sessions = 0; + if (start_session_db()) + return (state_func_t) single_user; + + /* + * Allocate a session entry for each active port. + * Note that sp starts at 0. + */ + while (typ = getttyent()) + if (snext = new_session(sp, ++session_index, typ)) + sp = snext; + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Start a window system running. + */ +void +start_window_system(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + + if ((pid = fork()) == -1) { + emergency("can't fork for window system on port %s: %m", + sp->se_device); + /* hope that getty fails and we can try again */ + return; + } + + if (pid) + return; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + if (setsid() < 0) + emergency("setsid failed (window) %m"); + + execv(sp->se_window_argv[0], sp->se_window_argv); + stall("can't exec window system '%s' for port %s: %m", + sp->se_window_argv[0], sp->se_device); + _exit(1); +} + +/* + * Start a login session running. + */ +pid_t +start_getty(sp) + session_t *sp; +{ + pid_t pid; + sigset_t mask; + time_t current_time = time((time_t *) 0); + + /* + * fork(), not vfork() -- we can't afford to block. + */ + if ((pid = fork()) == -1) { + emergency("can't fork for getty on port %s: %m", sp->se_device); + return -1; + } + + if (pid) + return pid; + + if (current_time > sp->se_started && + current_time - sp->se_started < GETTY_SPACING) { + warning("getty repeating too quickly on port %s, sleeping", + sp->se_device); + sleep((unsigned) GETTY_SLEEP); + } + + if (sp->se_window) { + start_window_system(sp); + sleep(WINDOW_WAIT); + } + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, (sigset_t *) 0); + + execv(sp->se_getty_argv[0], sp->se_getty_argv); + stall("can't exec getty '%s' for port %s: %m", + sp->se_getty_argv[0], sp->se_device); + _exit(1); +} + +/* + * Collect exit status for a child. + * If an exiting login, start a new login running. + */ +void +#ifdef __STDC__ +collect_child(pid_t pid) +#else +collect_child(pid) + pid_t pid; +#endif +{ + register session_t *sp, *sprev, *snext; + + if (! sessions) + return; + + if (! (sp = find_session(pid))) + return; + + clear_session_logs(sp); + del_session(sp); + sp->se_process = 0; + + if (sp->se_flags & SE_SHUTDOWN) { + if (sprev = sp->se_prev) + sprev->se_next = sp->se_next; + else + sessions = sp->se_next; + if (snext = sp->se_next) + snext->se_prev = sp->se_prev; + free_session(sp); + return; + } + + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + return; + } + + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); +} + +/* + * Catch a signal and request a state transition. + */ +void +transition_handler(sig) + int sig; +{ + + switch (sig) { + case SIGHUP: + requested_transition = clean_ttys; + break; + case SIGTERM: + requested_transition = death; + break; + case SIGTSTP: + requested_transition = catatonia; + break; + default: + requested_transition = 0; + break; + } +} + +/* + * Take the system multiuser. + */ +state_func_t +multi_user() +{ + pid_t pid; + register session_t *sp; + + requested_transition = 0; + + /* + * If the administrator has not set the security level to -1 + * to indicate that the kernel should not run multiuser in secure + * mode, and the run script has not set a higher level of security + * than level 1, then put the kernel into secure mode. + */ + if (getsecuritylevel() == 0) + setsecuritylevel(1); + + for (sp = sessions; sp; sp = sp->se_next) { + if (sp->se_process) + continue; + if ((pid = start_getty(sp)) == -1) { + /* serious trouble */ + requested_transition = clean_ttys; + break; + } + sp->se_process = pid; + sp->se_started = time((time_t *) 0); + add_session(sp); + } + + while (!requested_transition) + if ((pid = waitpid(-1, (int *) 0, 0)) != -1) + collect_child(pid); + + return (state_func_t) requested_transition; +} + +/* + * This is an n-squared algorithm. We hope it isn't run often... + */ +state_func_t +clean_ttys() +{ + register session_t *sp, *sprev; + register struct ttyent *typ; + register int session_index = 0; + register int devlen; + + if (! sessions) + return (state_func_t) multi_user; + + devlen = sizeof(_PATH_DEV) - 1; + while (typ = getttyent()) { + ++session_index; + + for (sprev = 0, sp = sessions; sp; sprev = sp, sp = sp->se_next) + if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) + break; + + if (sp) { + if (sp->se_index != session_index) { + warning("port %s changed utmp index from %d to %d", + sp->se_device, sp->se_index, + session_index); + sp->se_index = session_index; + } + if ((typ->ty_status & TTY_ON) == 0 || + typ->ty_getty == 0) { + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + continue; + } + sp->se_flags &= ~SE_SHUTDOWN; + if (setupargv(sp, typ) == 0) { + warning("can't parse getty for port %s", + sp->se_device); + sp->se_flags |= SE_SHUTDOWN; + kill(sp->se_process, SIGHUP); + } + continue; + } + + new_session(sprev, session_index, typ); + } + + endttyent(); + + return (state_func_t) multi_user; +} + +/* + * Block further logins. + */ +state_func_t +catatonia() +{ + register session_t *sp; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + return (state_func_t) multi_user; +} + +/* + * Note SIGALRM. + */ +void +alrm_handler(sig) + int sig; +{ + clang = 1; +} + +/* + * Bring the system down to single user. + */ +state_func_t +death() +{ + register session_t *sp; + register int i; + pid_t pid; + static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; + + for (sp = sessions; sp; sp = sp->se_next) + sp->se_flags |= SE_SHUTDOWN; + + /* NB: should send a message to the session logger to avoid blocking. */ + logwtmp("~", "shutdown", ""); + + for (i = 0; i < 3; ++i) { + if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) + return (state_func_t) single_user; + + clang = 0; + alarm(DEATH_WATCH); + do + if ((pid = waitpid(-1, (int *)0, 0)) != -1) + collect_child(pid); + while (clang == 0 && errno != ECHILD); + + if (errno == ECHILD) + return (state_func_t) single_user; + } + + warning("some processes would not die; ps axl advised"); + + return (state_func_t) single_user; +} diff --git a/sbin/ping/ping.8 b/sbin/ping/ping.8 new file mode 100644 index 000000000000..2382365b266f --- /dev/null +++ b/sbin/ping/ping.8 @@ -0,0 +1,328 @@ +.\" Copyright (c) 1985, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ping.8 8.3 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt PING 8 +.Os BSD 4.3 +.Sh NAME +.Nm ping +.Nd send +.Tn ICMP ECHO_REQUEST +packets to network hosts +.Sh SYNOPSIS +.Nm ping +.Op Fl Rdfnqrv +.Op Fl c Ar count +.Op Fl i Ar wait +.Op Fl l Ar preload +.Op Fl p Ar pattern +.Op Fl s Ar packetsize +.Ar host +.Sh DESCRIPTION +.Nm Ping +uses the +.Tn ICMP +protocol's mandatory +.Tn ECHO_REQUEST +datagram to elicit an +.Tn ICMP ECHO_RESPONSE +from a host or gateway. +.Tn ECHO_REQUEST +datagrams (``pings'') have an IP and +.Tn ICMP +header, +followed by a +.Dq struct timeval +and then an arbitrary number of ``pad'' bytes used to fill out the +packet. +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar count +Stop after sending (and receiving) +.Ar count +.Tn ECHO_RESPONSE +packets. +.It Fl d +Set the +.Dv SO_DEBUG +option on the socket being used. +.It Fl f +Flood ping. +Outputs packets as fast as they come back or one hundred times per second, +whichever is more. +For every +.Tn ECHO_REQUEST +sent a period ``.'' is printed, while for every +.Tn ECHO_REPLY +received a backspace is printed. +This provides a rapid display of how many packets are being dropped. +Only the super-user may use this option. +.Bf -emphasis +This can be very hard on a network and should be used with caution. +.Ef +.It Fl i Ar wait +Wait +.Ar wait +seconds +.Em between sending each packet . +The default is to wait for one second between each packet. +This option is incompatible with the +.Fl f +option. +.It Fl l Ar preload +If +.Ar preload +is specified, +.Nm ping +sends that many packets as fast as possible before falling into its normal +mode of behavior. +.It Fl n +Numeric output only. +No attempt will be made to lookup symbolic names for host addresses. +.It Fl p Ar pattern +You may specify up to 16 ``pad'' bytes to fill out the packet you send. +This is useful for diagnosing data-dependent problems in a network. +For example, +.Dq Li \-p ff +will cause the sent packet to be filled with all +ones. +.It Fl q +Quiet output. +Nothing is displayed except the summary lines at startup time and +when finished. +.It Fl R +Record route. +Includes the +.Tn RECORD_ROUTE +option in the +.Tn ECHO_REQUEST +packet and displays +the route buffer on returned packets. +Note that the IP header is only large enough for nine such routes. +Many hosts ignore or discard this option. +.It Fl r +Bypass the normal routing tables and send directly to a host on an attached +network. +If the host is not on a directly-attached network, an error is returned. +This option can be used to ping a local host through an interface +that has no route through it (e.g., after the interface was dropped by +.Xr routed 8 ) . +.It Fl s Ar packetsize +Specifies the number of data bytes to be sent. +The default is 56, which translates into 64 +.Tn ICMP +data bytes when combined +with the 8 bytes of +.Tn ICMP +header data. +.It Fl v +Verbose output. +.Tn ICMP +packets other than +.Tn ECHO_RESPONSE +that are received are listed. +.El +.Pp +When using +.Nm ping +for fault isolation, it should first be run on the local host, to verify +that the local network interface is up and running. +Then, hosts and gateways further and further away should be ``pinged''. +Round-trip times and packet loss statistics are computed. +If duplicate packets are received, they are not included in the packet +loss calculation, although the round trip time of these packets is used +in calculating the minimum/average/maximum round-trip time numbers. +When the specified number of packets have been sent (and received) or +if the program is terminated with a +.Dv SIGINT , +a brief summary is displayed. +.Pp +This program is intended for use in network testing, measurement and +management. +Because of the load it can impose on the network, it is unwise to use +.Nm ping +during normal operations or from automated scripts. +.Sh ICMP PACKET DETAILS +An IP header without options is 20 bytes. +An +.Tn ICMP +.Tn ECHO_REQUEST +packet contains an additional 8 bytes worth +of +.Tn ICMP +header followed by an arbitrary amount of data. +When a +.Ar packetsize +is given, this indicated the size of this extra piece of data (the +default is 56). +Thus the amount of data received inside of an IP packet of type +.Tn ICMP +.Tn ECHO_REPLY +will always be 8 bytes more than the requested data space +(the +.Tn ICMP +header). +.Pp +If the data space is at least eight bytes large, +.Nm ping +uses the first eight bytes of this space to include a timestamp which +it uses in the computation of round trip times. +If less than eight bytes of pad are specified, no round trip times are +given. +.Sh DUPLICATE AND DAMAGED PACKETS +.Nm Ping +will report duplicate and damaged packets. +Duplicate packets should never occur, and seem to be caused by +inappropriate link-level retransmissions. +Duplicates may occur in many situations and are rarely (if ever) a +good sign, although the presence of low levels of duplicates may not +always be cause for alarm. +.Pp +Damaged packets are obviously serious cause for alarm and often +indicate broken hardware somewhere in the +.Nm ping +packet's path (in the network or in the hosts). +.Sh TRYING DIFFERENT DATA PATTERNS +The (inter)network layer should never treat packets differently depending +on the data contained in the data portion. +Unfortunately, data-dependent problems have been known to sneak into +networks and remain undetected for long periods of time. +In many cases the particular pattern that will have problems is something +that doesn't have sufficient ``transitions'', such as all ones or all +zeros, or a pattern right at the edge, such as almost all zeros. +It isn't necessarily enough to specify a data pattern of all zeros (for +example) on the command line because the pattern that is of interest is +at the data link level, and the relationship between what you type and +what the controllers transmit can be complicated. +.Pp +This means that if you have a data-dependent problem you will probably +have to do a lot of testing to find it. +If you are lucky, you may manage to find a file that either can't be sent +across your network or that takes much longer to transfer than other +similar length files. +You can then examine this file for repeated patterns that you can test +using the +.Fl p +option of +.Nm ping . +.Sh TTL DETAILS +The +.Tn TTL +value of an IP packet represents the maximum number of IP routers +that the packet can go through before being thrown away. +In current practice you can expect each router in the Internet to decrement +the +.Tn TTL +field by exactly one. +.Pp +The +.Tn TCP/IP +specification states that the +.Tn TTL +field for +.Tn TCP +packets should +be set to 60, but many systems use smaller values (4.3 +.Tn BSD +uses 30, 4.2 used +15). +.Pp +The maximum possible value of this field is 255, and most Unix systems set +the +.Tn TTL +field of +.Tn ICMP ECHO_REQUEST +packets to 255. +This is why you will find you can ``ping'' some hosts, but not reach them +with +.Xr telnet 1 +or +.Xr ftp 1 . +.Pp +In normal operation ping prints the ttl value from the packet it receives. +When a remote system receives a ping packet, it can do one of three things +with the +.Tn TTL +field in its response: +.Bl -bullet +.It +Not change it; this is what Berkeley Unix systems did before the +.Bx 4.3 tahoe +release. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the round-trip path. +.It +Set it to 255; this is what current Berkeley Unix systems do. +In this case the +.Tn TTL +value in the received packet will be 255 minus the +number of routers in the path +.Xr from +the remote system +.Em to +the +.Nm ping Ns Em ing +host. +.It +Set it to some other value. +Some machines use the same value for +.Tn ICMP +packets that they use for +.Tn TCP +packets, for example either 30 or 60. +Others may use completely wild values. +.El +.Sh BUGS +Many Hosts and Gateways ignore the +.Tn RECORD_ROUTE +option. +.Pp +The maximum IP header length is too small for options like +.Tn RECORD_ROUTE +to +be completely useful. +There's not much that that can be done about this, however. +.Pp +Flood pinging is not recommended in general, and flood pinging the +broadcast address should only be done under very controlled conditions. +.Sh SEE ALSO +.Xr netstat 1 , +.Xr ifconfig 8 , +.Xr routed 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/sbin/ping/ping.c b/sbin/ping/ping.c new file mode 100644 index 000000000000..7ccec1e02e9a --- /dev/null +++ b/sbin/ping/ping.c @@ -0,0 +1,986 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Mike Muuss. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ping.c 8.3 (Berkeley) 4/28/95"; +#endif /* not lint */ + +/* + * P I N G . C + * + * Using the InterNet Control Message Protocol (ICMP) "ECHO" facility, + * measure round-trip-delays and packet loss across network paths. + * + * Author - + * Mike Muuss + * U. S. Army Ballistic Research Laboratory + * December, 1983 + * + * Status - + * Public Domain. Distribution Unlimited. + * Bugs - + * More statistics could always be gathered. + * This program has to run SUID to ROOT to access the ICMP socket. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFDATALEN (64 - 8) /* default data length */ +#define MAXIPLEN 60 +#define MAXICMPLEN 76 +#define MAXPACKET (65536 - 60 - 8)/* max packet size */ +#define MAXWAIT 10 /* max seconds to wait for response */ +#define NROUTES 9 /* number of record route slots */ + +#define A(bit) rcvd_tbl[(bit)>>3] /* identify byte in array */ +#define B(bit) (1 << ((bit) & 0x07)) /* identify bit in byte */ +#define SET(bit) (A(bit) |= B(bit)) +#define CLR(bit) (A(bit) &= (~B(bit))) +#define TST(bit) (A(bit) & B(bit)) + +/* various options */ +int options; +#define F_FLOOD 0x001 +#define F_INTERVAL 0x002 +#define F_NUMERIC 0x004 +#define F_PINGFILLED 0x008 +#define F_QUIET 0x010 +#define F_RROUTE 0x020 +#define F_SO_DEBUG 0x040 +#define F_SO_DONTROUTE 0x080 +#define F_VERBOSE 0x100 + +/* + * MAX_DUP_CHK is the number of bits in received table, i.e. the maximum + * number of received sequence numbers we can keep track of. Change 128 + * to 8192 for complete accuracy... + */ +#define MAX_DUP_CHK (8 * 128) +int mx_dup_ck = MAX_DUP_CHK; +char rcvd_tbl[MAX_DUP_CHK / 8]; + +struct sockaddr whereto; /* who to ping */ +int datalen = DEFDATALEN; +int s; /* socket file descriptor */ +u_char outpack[MAXPACKET]; +char BSPACE = '\b'; /* characters written for flood */ +char DOT = '.'; +char *hostname; +int ident; /* process id to identify our packets */ + +/* counters */ +long npackets; /* max packets to transmit */ +long nreceived; /* # of packets we got back */ +long nrepeats; /* number of duplicates */ +long ntransmitted; /* sequence # for outbound packets = #sent */ +int interval = 1; /* interval between packets */ + +/* timing */ +int timing; /* flag to do timing */ +double tmin = 999999999.0; /* minimum round trip time */ +double tmax = 0.0; /* maximum round trip time */ +double tsum = 0.0; /* sum of all times, for doing average */ + +char *pr_addr(); +void catcher(), finish(); + +main(argc, argv) + int argc; + char **argv; +{ + extern int errno, optind; + extern char *optarg; + struct timeval timeout; + struct hostent *hp; + struct sockaddr_in *to; + struct protoent *proto; + register int i; + int ch, fdmask, hold, packlen, preload; + u_char *datap, *packet; + char *target, hnamebuf[MAXHOSTNAMELEN], *malloc(); +#ifdef IP_OPTIONS + char rspace[3 + 4 * NROUTES + 1]; /* record route space */ +#endif + + preload = 0; + datap = &outpack[8 + sizeof(struct timeval)]; + while ((ch = getopt(argc, argv, "Rc:dfh:i:l:np:qrs:v")) != EOF) + switch(ch) { + case 'c': + npackets = atoi(optarg); + if (npackets <= 0) { + (void)fprintf(stderr, + "ping: bad number of packets to transmit.\n"); + exit(1); + } + break; + case 'd': + options |= F_SO_DEBUG; + break; + case 'f': + if (getuid()) { + (void)fprintf(stderr, + "ping: %s\n", strerror(EPERM)); + exit(1); + } + options |= F_FLOOD; + setbuf(stdout, (char *)NULL); + break; + case 'i': /* wait between sending packets */ + interval = atoi(optarg); + if (interval <= 0) { + (void)fprintf(stderr, + "ping: bad timing interval.\n"); + exit(1); + } + options |= F_INTERVAL; + break; + case 'l': + preload = atoi(optarg); + if (preload < 0) { + (void)fprintf(stderr, + "ping: bad preload value.\n"); + exit(1); + } + break; + case 'n': + options |= F_NUMERIC; + break; + case 'p': /* fill buffer with user pattern */ + options |= F_PINGFILLED; + fill((char *)datap, optarg); + break; + case 'q': + options |= F_QUIET; + break; + case 'R': + options |= F_RROUTE; + break; + case 'r': + options |= F_SO_DONTROUTE; + break; + case 's': /* size of packet to send */ + datalen = atoi(optarg); + if (datalen > MAXPACKET) { + (void)fprintf(stderr, + "ping: packet size too large.\n"); + exit(1); + } + if (datalen <= 0) { + (void)fprintf(stderr, + "ping: illegal packet size.\n"); + exit(1); + } + break; + case 'v': + options |= F_VERBOSE; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + target = *argv; + + memset(&whereto, 0, sizeof(struct sockaddr)); + to = (struct sockaddr_in *)&whereto; + to->sin_family = AF_INET; + to->sin_addr.s_addr = inet_addr(target); + if (to->sin_addr.s_addr != (u_int)-1) + hostname = target; + else { + hp = gethostbyname(target); + if (!hp) { + (void)fprintf(stderr, + "ping: unknown host %s\n", target); + exit(1); + } + to->sin_family = hp->h_addrtype; + memmove(&to->sin_addr, hp->h_addr, hp->h_length); + (void)strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1); + hostname = hnamebuf; + } + + if (options & F_FLOOD && options & F_INTERVAL) { + (void)fprintf(stderr, + "ping: -f and -i incompatible options.\n"); + exit(1); + } + + if (datalen >= sizeof(struct timeval)) /* can we time transfer */ + timing = 1; + packlen = datalen + MAXIPLEN + MAXICMPLEN; + if (!(packet = (u_char *)malloc((u_int)packlen))) { + (void)fprintf(stderr, "ping: out of memory.\n"); + exit(1); + } + if (!(options & F_PINGFILLED)) + for (i = 8; i < datalen; ++i) + *datap++ = i; + + ident = getpid() & 0xFFFF; + + if (!(proto = getprotobyname("icmp"))) { + (void)fprintf(stderr, "ping: unknown protocol icmp.\n"); + exit(1); + } + if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { + perror("ping: socket"); + exit(1); + } + hold = 1; + if (options & F_SO_DEBUG) + (void)setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&hold, + sizeof(hold)); + if (options & F_SO_DONTROUTE) + (void)setsockopt(s, SOL_SOCKET, SO_DONTROUTE, (char *)&hold, + sizeof(hold)); + + /* record route option */ + if (options & F_RROUTE) { +#ifdef IP_OPTIONS + rspace[IPOPT_OPTVAL] = IPOPT_RR; + rspace[IPOPT_OLEN] = sizeof(rspace)-1; + rspace[IPOPT_OFFSET] = IPOPT_MINOFF; + if (setsockopt(s, IPPROTO_IP, IP_OPTIONS, rspace, + sizeof(rspace)) < 0) { + perror("ping: record route"); + exit(1); + } +#else + (void)fprintf(stderr, + "ping: record route not available in this implementation.\n"); + exit(1); +#endif /* IP_OPTIONS */ + } + + /* + * When pinging the broadcast address, you can get a lot of answers. + * Doing something so evil is useful if you are trying to stress the + * ethernet, or just want to fill the arp cache to get some stuff for + * /etc/ethers. + */ + hold = 48 * 1024; + (void)setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&hold, + sizeof(hold)); + + if (to->sin_family == AF_INET) + (void)printf("PING %s (%s): %d data bytes\n", hostname, + inet_ntoa(*(struct in_addr *)&to->sin_addr.s_addr), + datalen); + else + (void)printf("PING %s: %d data bytes\n", hostname, datalen); + + (void)signal(SIGINT, finish); + (void)signal(SIGALRM, catcher); + + while (preload--) /* fire off them quickies */ + pinger(); + + if ((options & F_FLOOD) == 0) + catcher(); /* start things going */ + + for (;;) { + struct sockaddr_in from; + register int cc; + int fromlen; + + if (options & F_FLOOD) { + pinger(); + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + fdmask = 1 << s; + if (select(s + 1, (fd_set *)&fdmask, (fd_set *)NULL, + (fd_set *)NULL, &timeout) < 1) + continue; + } + fromlen = sizeof(from); + if ((cc = recvfrom(s, (char *)packet, packlen, 0, + (struct sockaddr *)&from, &fromlen)) < 0) { + if (errno == EINTR) + continue; + perror("ping: recvfrom"); + continue; + } + pr_pack((char *)packet, cc, &from); + if (npackets && nreceived >= npackets) + break; + } + finish(); + /* NOTREACHED */ +} + +/* + * catcher -- + * This routine causes another PING to be transmitted, and then + * schedules another SIGALRM for 1 second from now. + * + * bug -- + * Our sense of time will slowly skew (i.e., packets will not be + * launched exactly at 1-second intervals). This does not affect the + * quality of the delay and loss statistics. + */ +void +catcher() +{ + int waittime; + + pinger(); + (void)signal(SIGALRM, catcher); + if (!npackets || ntransmitted < npackets) + alarm((u_int)interval); + else { + if (nreceived) { + waittime = 2 * tmax / 1000; + if (!waittime) + waittime = 1; + } else + waittime = MAXWAIT; + (void)signal(SIGALRM, finish); + (void)alarm((u_int)waittime); + } +} + +/* + * pinger -- + * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet + * will be added on by the kernel. The ID field is our UNIX process ID, + * and the sequence number is an ascending integer. The first 8 bytes + * of the data portion are used to hold a UNIX "timeval" struct in VAX + * byte-order, to compute the round-trip time. + */ +pinger() +{ + register struct icmp *icp; + register int cc; + int i; + + icp = (struct icmp *)outpack; + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_seq = ntransmitted++; + icp->icmp_id = ident; /* ID */ + + CLR(icp->icmp_seq % mx_dup_ck); + + if (timing) + (void)gettimeofday((struct timeval *)&outpack[8], + (struct timezone *)NULL); + + cc = datalen + 8; /* skips ICMP portion */ + + /* compute ICMP checksum here */ + icp->icmp_cksum = in_cksum((u_short *)icp, cc); + + i = sendto(s, (char *)outpack, cc, 0, &whereto, + sizeof(struct sockaddr)); + + if (i < 0 || i != cc) { + if (i < 0) + perror("ping: sendto"); + (void)printf("ping: wrote %s %d chars, ret=%d\n", + hostname, cc, i); + } + if (!(options & F_QUIET) && options & F_FLOOD) + (void)write(STDOUT_FILENO, &DOT, 1); +} + +/* + * pr_pack -- + * Print out the packet, if it came from us. This logic is necessary + * because ALL readers of the ICMP socket get a copy of ALL ICMP packets + * which arrive ('tis only fair). This permits multiple copies of this + * program to be run without having intermingled output (or statistics!). + */ +pr_pack(buf, cc, from) + char *buf; + int cc; + struct sockaddr_in *from; +{ + register struct icmp *icp; + register u_long l; + register int i, j; + register u_char *cp,*dp; + static int old_rrlen; + static char old_rr[MAX_IPOPTLEN]; + struct ip *ip; + struct timeval tv, *tp; + double triptime; + int hlen, dupflag; + + (void)gettimeofday(&tv, (struct timezone *)NULL); + + /* Check the IP header */ + ip = (struct ip *)buf; + hlen = ip->ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) { + if (options & F_VERBOSE) + (void)fprintf(stderr, + "ping: packet too short (%d bytes) from %s\n", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr)); + return; + } + + /* Now the ICMP part */ + cc -= hlen; + icp = (struct icmp *)(buf + hlen); + if (icp->icmp_type == ICMP_ECHOREPLY) { + if (icp->icmp_id != ident) + return; /* 'Twas not our ECHO */ + ++nreceived; + if (timing) { +#ifndef icmp_data + tp = (struct timeval *)&icp->icmp_ip; +#else + tp = (struct timeval *)icp->icmp_data; +#endif + tvsub(&tv, tp); + triptime = ((double)tv.tv_sec) * 1000.0 + + ((double)tv.tv_usec) / 1000.0; + tsum += triptime; + if (triptime < tmin) + tmin = triptime; + if (triptime > tmax) + tmax = triptime; + } + + if (TST(icp->icmp_seq % mx_dup_ck)) { + ++nrepeats; + --nreceived; + dupflag = 1; + } else { + SET(icp->icmp_seq % mx_dup_ck); + dupflag = 0; + } + + if (options & F_QUIET) + return; + + if (options & F_FLOOD) + (void)write(STDOUT_FILENO, &BSPACE, 1); + else { + (void)printf("%d bytes from %s: icmp_seq=%u", cc, + inet_ntoa(*(struct in_addr *)&from->sin_addr.s_addr), + icp->icmp_seq); + (void)printf(" ttl=%d", ip->ip_ttl); + if (timing) + (void)printf(" time=%g ms", triptime); + if (dupflag) + (void)printf(" (DUP!)"); + /* check the data */ + cp = (u_char*)&icp->icmp_data[8]; + dp = &outpack[8 + sizeof(struct timeval)]; + for (i = 8; i < datalen; ++i, ++cp, ++dp) { + if (*cp != *dp) { + (void)printf("\nwrong data byte #%d should be 0x%x but was 0x%x", + i, *dp, *cp); + cp = (u_char*)&icp->icmp_data[0]; + for (i = 8; i < datalen; ++i, ++cp) { + if ((i % 32) == 8) + (void)printf("\n\t"); + (void)printf("%x ", *cp); + } + break; + } + } + } + } else { + /* We've got something other than an ECHOREPLY */ + if (!(options & F_VERBOSE)) + return; + (void)printf("%d bytes from %s: ", cc, + pr_addr(from->sin_addr.s_addr)); + pr_icmph(icp); + } + + /* Display any IP options */ + cp = (u_char *)buf + sizeof(struct ip); + + for (; hlen > (int)sizeof(struct ip); --hlen, ++cp) + switch (*cp) { + case IPOPT_EOL: + hlen = 0; + break; + case IPOPT_LSRR: + (void)printf("\nLSRR: "); + hlen -= 2; + j = *++cp; + ++cp; + if (j > IPOPT_MINOFF) + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + j -= 4; + if (j <= IPOPT_MINOFF) + break; + (void)putchar('\n'); + } + break; + case IPOPT_RR: + j = *++cp; /* get length */ + i = *++cp; /* and pointer */ + hlen -= 2; + if (i > j) + i = j; + i -= IPOPT_MINOFF; + if (i <= 0) + continue; + if (i == old_rrlen + && cp == (u_char *)buf + sizeof(struct ip) + 2 + && !memcmp(cp, old_rr, i) + && !(options & F_FLOOD)) { + (void)printf("\t(same route)"); + i = ((i + 3) / 4) * 4; + hlen -= i; + cp += i; + break; + } + old_rrlen = i; + memmove(old_rr, cp, i); + (void)printf("\nRR: "); + for (;;) { + l = *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + l = (l<<8) + *++cp; + if (l == 0) + (void)printf("\t0.0.0.0"); + else + (void)printf("\t%s", pr_addr(ntohl(l))); + hlen -= 4; + i -= 4; + if (i <= 0) + break; + (void)putchar('\n'); + } + break; + case IPOPT_NOP: + (void)printf("\nNOP"); + break; + default: + (void)printf("\nunknown option %x", *cp); + break; + } + if (!(options & F_FLOOD)) { + (void)putchar('\n'); + (void)fflush(stdout); + } +} + +/* + * in_cksum -- + * Checksum routine for Internet Protocol family headers (C Version) + */ +in_cksum(addr, len) + u_short *addr; + int len; +{ + register int nleft = len; + register u_short *w = addr; + register int sum = 0; + u_short answer = 0; + + /* + * Our algorithm is simple, using a 32 bit accumulator (sum), we add + * sequential 16 bit words to it, and at the end, fold back all the + * carry bits from the top 16 bits into the lower 16 bits. + */ + while (nleft > 1) { + sum += *w++; + nleft -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nleft == 1) { + *(u_char *)(&answer) = *(u_char *)w ; + sum += answer; + } + + /* add back carry outs from top 16 bits to low 16 bits */ + sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ + sum += (sum >> 16); /* add carry */ + answer = ~sum; /* truncate to 16 bits */ + return(answer); +} + +/* + * tvsub -- + * Subtract 2 timeval structs: out = out - in. Out is assumed to + * be >= in. + */ +tvsub(out, in) + register struct timeval *out, *in; +{ + if ((out->tv_usec -= in->tv_usec) < 0) { + --out->tv_sec; + out->tv_usec += 1000000; + } + out->tv_sec -= in->tv_sec; +} + +/* + * finish -- + * Print out statistics, and give up. + */ +void +finish() +{ + register int i; + + (void)signal(SIGINT, SIG_IGN); + (void)putchar('\n'); + (void)fflush(stdout); + (void)printf("--- %s ping statistics ---\n", hostname); + (void)printf("%ld packets transmitted, ", ntransmitted); + (void)printf("%ld packets received, ", nreceived); + if (nrepeats) + (void)printf("+%ld duplicates, ", nrepeats); + if (ntransmitted) + if (nreceived > ntransmitted) + (void)printf("-- somebody's printing up packets!"); + else + (void)printf("%d%% packet loss", + (int) (((ntransmitted - nreceived) * 100) / + ntransmitted)); + (void)putchar('\n'); + if (nreceived && timing) { + /* Only display average to microseconds */ + i = 1000.0 * tsum / (nreceived + nrepeats); + (void)printf("round-trip min/avg/max = %g/%g/%g ms\n", + tmin, ((double)i) / 1000.0, tmax); + } + exit(0); +} + +#ifdef notdef +static char *ttab[] = { + "Echo Reply", /* ip + seq + udata */ + "Dest Unreachable", /* net, host, proto, port, frag, sr + IP */ + "Source Quench", /* IP */ + "Redirect", /* redirect type, gateway, + IP */ + "Echo", + "Time Exceeded", /* transit, frag reassem + IP */ + "Parameter Problem", /* pointer + IP */ + "Timestamp", /* id + seq + three timestamps */ + "Timestamp Reply", /* " */ + "Info Request", /* id + sq */ + "Info Reply" /* " */ +}; +#endif + +/* + * pr_icmph -- + * Print a descriptive string about an ICMP header. + */ +pr_icmph(icp) + struct icmp *icp; +{ + switch(icp->icmp_type) { + case ICMP_ECHOREPLY: + (void)printf("Echo Reply\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_UNREACH: + switch(icp->icmp_code) { + case ICMP_UNREACH_NET: + (void)printf("Destination Net Unreachable\n"); + break; + case ICMP_UNREACH_HOST: + (void)printf("Destination Host Unreachable\n"); + break; + case ICMP_UNREACH_PROTOCOL: + (void)printf("Destination Protocol Unreachable\n"); + break; + case ICMP_UNREACH_PORT: + (void)printf("Destination Port Unreachable\n"); + break; + case ICMP_UNREACH_NEEDFRAG: + (void)printf("frag needed and DF set\n"); + break; + case ICMP_UNREACH_SRCFAIL: + (void)printf("Source Route Failed\n"); + break; + default: + (void)printf("Dest Unreachable, Bad Code: %d\n", + icp->icmp_code); + break; + } + /* Print returned IP header information */ +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_SOURCEQUENCH: + (void)printf("Source Quench\n"); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_REDIRECT: + switch(icp->icmp_code) { + case ICMP_REDIRECT_NET: + (void)printf("Redirect Network"); + break; + case ICMP_REDIRECT_HOST: + (void)printf("Redirect Host"); + break; + case ICMP_REDIRECT_TOSNET: + (void)printf("Redirect Type of Service and Network"); + break; + case ICMP_REDIRECT_TOSHOST: + (void)printf("Redirect Type of Service and Host"); + break; + default: + (void)printf("Redirect, Bad Code: %d", icp->icmp_code); + break; + } + (void)printf("(New addr: 0x%08lx)\n", icp->icmp_gwaddr.s_addr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_ECHO: + (void)printf("Echo Request\n"); + /* XXX ID + Seq + Data */ + break; + case ICMP_TIMXCEED: + switch(icp->icmp_code) { + case ICMP_TIMXCEED_INTRANS: + (void)printf("Time to live exceeded\n"); + break; + case ICMP_TIMXCEED_REASS: + (void)printf("Frag reassembly time exceeded\n"); + break; + default: + (void)printf("Time exceeded, Bad Code: %d\n", + icp->icmp_code); + break; + } +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_PARAMPROB: + (void)printf("Parameter problem: pointer = 0x%02x\n", + icp->icmp_hun.ih_pptr); +#ifndef icmp_data + pr_retip(&icp->icmp_ip); +#else + pr_retip((struct ip *)icp->icmp_data); +#endif + break; + case ICMP_TSTAMP: + (void)printf("Timestamp\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_TSTAMPREPLY: + (void)printf("Timestamp Reply\n"); + /* XXX ID + Seq + 3 timestamps */ + break; + case ICMP_IREQ: + (void)printf("Information Request\n"); + /* XXX ID + Seq */ + break; + case ICMP_IREQREPLY: + (void)printf("Information Reply\n"); + /* XXX ID + Seq */ + break; +#ifdef ICMP_MASKREQ + case ICMP_MASKREQ: + (void)printf("Address Mask Request\n"); + break; +#endif +#ifdef ICMP_MASKREPLY + case ICMP_MASKREPLY: + (void)printf("Address Mask Reply\n"); + break; +#endif + default: + (void)printf("Bad ICMP type: %d\n", icp->icmp_type); + } +} + +/* + * pr_iph -- + * Print an IP header with options. + */ +pr_iph(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + 20; /* point to options */ + + (void)printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); + (void)printf(" %1x %1x %02x %04x %04x", + ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); + (void)printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, + (ip->ip_off) & 0x1fff); + (void)printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr)); + (void)printf(" %s ", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr)); + /* dump and option bytes */ + while (hlen-- > 20) { + (void)printf("%02x", *cp++); + } + (void)putchar('\n'); +} + +/* + * pr_addr -- + * Return an ascii host address as a dotted quad and optionally with + * a hostname. + */ +char * +pr_addr(l) + u_long l; +{ + struct hostent *hp; + static char buf[80]; + + if ((options & F_NUMERIC) || + !(hp = gethostbyaddr((char *)&l, 4, AF_INET))) + (void)sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&l)); + else + (void)sprintf(buf, "%s (%s)", hp->h_name, + inet_ntoa(*(struct in_addr *)&l)); + return(buf); +} + +/* + * pr_retip -- + * Dump some info on a returned (via ICMP) IP packet. + */ +pr_retip(ip) + struct ip *ip; +{ + int hlen; + u_char *cp; + + pr_iph(ip); + hlen = ip->ip_hl << 2; + cp = (u_char *)ip + hlen; + + if (ip->ip_p == 6) + (void)printf("TCP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); + else if (ip->ip_p == 17) + (void)printf("UDP: from port %u, to port %u (decimal)\n", + (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); +} + +fill(bp, patp) + char *bp, *patp; +{ + register int ii, jj, kk; + int pat[16]; + char *cp; + + for (cp = patp; *cp; cp++) + if (!isxdigit(*cp)) { + (void)fprintf(stderr, + "ping: patterns must be specified as hex digits.\n"); + exit(1); + } + ii = sscanf(patp, + "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x", + &pat[0], &pat[1], &pat[2], &pat[3], &pat[4], &pat[5], &pat[6], + &pat[7], &pat[8], &pat[9], &pat[10], &pat[11], &pat[12], + &pat[13], &pat[14], &pat[15]); + + if (ii > 0) + for (kk = 0; + kk <= MAXPACKET - (8 + sizeof(struct timeval) + ii); + kk += ii) + for (jj = 0; jj < ii; ++jj) + bp[jj + kk] = pat[jj]; + if (!(options & F_QUIET)) { + (void)printf("PATTERN: 0x"); + for (jj = 0; jj < ii; ++jj) + (void)printf("%02x", bp[jj] & 0xFF); + (void)printf("\n"); + } +} + +usage() +{ + (void)fprintf(stderr, + "usage: ping [-Rdfnqrv] [-c count] [-i wait] [-l preload]\n\t[-p pattern] [-s packetsize] host\n"); + exit(1); +} diff --git a/sbin/route/ccitt_addr.c b/sbin/route/ccitt_addr.c new file mode 100644 index 000000000000..54fb9b265b3d --- /dev/null +++ b/sbin/route/ccitt_addr.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ccitt_addr.c 8.2 (Berkeley) 4/28/95 + */ +/* + * parse CCITT addresses + * + * Addresses must have the format: [hpr],x121address[,userdata][,protocol] + * items enclosed with square brackets are optional + * 'h' or 'p' means hi priority (packet size = 128; specific to Datapac + * and necessary only for X.25(76) and non-negotiating X.25(80) DTE's) + * 'r' means reverse charge (remote DTE pays for call). + * The x121address consists of an optional netid and dot, followed + * by a dte address. + * + * Frank Pronk + * The University of British Columbia + * Laboratory for Computational Vision + * Copyright (c) 1984 + */ + +#include +#include +#include + +static char *copychar (); + +ccitt_addr (addr, xp) +char *addr; +register struct sockaddr_x25 *xp; +{ + register char *p, *ap, *limit; + int havenet = 0; + + memset(xp, 0, sizeof (*xp)); + xp->x25_family = AF_CCITT; + xp->x25_len = sizeof(*xp); + p = addr; + + /* + * process optional priority and reverse charging flags + */ + + if (*p == 'p' || *p == 'r' || *p == 'h') { + while (*p == 'p' || *p == 'r' || *p == 'h') { + if (*p == 'p' || *p == 'h') + xp->x25_opts.op_psize = X25_PS128; + else if (*p == 'r') + xp->x25_opts.op_flags |= X25_REVERSE_CHARGE; + p++; + } + if (*p != ',') + return (0); + p++; + } + if (*p == '\0') + return (0); + + /* + * [network id:]X.121 address + */ + + ap = xp->x25_addr; + limit = ap + sizeof (xp->x25_addr) - 1; + while (*p) { + if (*p == ',') + break; + if (*p == '.' || *p == ':') { + if (havenet) + return (0); + havenet++; + xp->x25_net = atoi (xp->x25_addr); + p++; + ap = xp->x25_addr; + *ap = '\0'; + } + if (*p < '0' || *p > '9') + return (0); + if (ap >= limit) + return (0); + *ap++ = *p++; + } + if (*p == '\0') + return (1); + + /* + * optional user data, bytes 4 to 16 + */ + + p++; + ap = xp->x25_udata + 4; /* first four bytes are protocol id */ + limit = ap + sizeof (xp->x25_udata) - 4; + xp->x25_udlen = 4; + while (*p) { + if (*p == ',') + break; + if (ap >= limit) + return (0); + p = copychar (p, ap++); + xp->x25_udlen++; + } + if (xp->x25_udlen == 4) + xp->x25_udlen = 0; + if (*p == '\0') + return (1); + + p++; + ap = xp->x25_udata; /* protocol id */ + limit = ap + (xp->x25_udlen ? 4 : sizeof(xp->x25_udata)); + while (*p) { + if (*p == ',') + return (0); + if (ap >= limit) + return (0); + p = copychar (p, ap++); + } + if (xp->x25_udlen == 0) + xp->x25_udlen = ap - xp->x25_udata; + return (1); +} + +static char * +copychar (from, to) +register char *from, *to; +{ + register int n; + + if (*from != '\\' || from[1] < '0' || from[1] > '7') { + *to = *from++; + return (from); + } + n = *++from - '0'; + from++; + if (*from >= '0' && *from <= '7') { + register int n1; + + n = n*8 + *from++ - '0'; + if (*from >= '0' && *from <= '7' && (n1 = n*8 + *from-'0') < 256) { + n = n1; + from++; + } + } + *to = n; + return (from); +} diff --git a/sbin/route/route.8 b/sbin/route/route.8 new file mode 100644 index 000000000000..62b9cfe44708 --- /dev/null +++ b/sbin/route/route.8 @@ -0,0 +1,325 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)route.8 8.4 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt ROUTE 8 +.Os BSD 4.4 +.Sh NAME +.Nm route +.Nd manually manipulate the routing tables. +.Sh SYNOPSIS +.Nm route +.Op Fl nqv +.Ar command +.Oo +.Op Ar modifiers +.Ar args +.Oc +.Sh DESCRIPTION +.Nm Route +is a utility used to manually manipulate the network +routing tables. It normally is not needed, as a +system routing table management daemon such as +.Xr routed 8 , +should tend to this task. +.Pp +The +.Nm route : +utility supports a limited number of general options, +but a rich command language, enabling the user to specify +any arbitrary request that could be delivered via the +programmatic interface discussed in +.Xr route 4 . +.Pp +.Bl -tag -width Ds +.It Fl n +Bypasses attempts to print host and network names symbolically +when reporting actions. (The process of translating between symbolic +names and numerical equivalents can be quite time consuming, and +may require correct operation of the network; thus it may be expedient +to forgo this, especially when attempting to repair networking operations), +.It Fl v +(verbose) Print additional details. +.It Fl q +Suppress all output. +.El +.Pp +The +.Nm route : +utility provides six commands: +.Pp +.Bl -tag -width Fl -compact +.It Cm add +Add a route. +.It Cm flush +Remove all routes. +.It Cm delete +Delete a specific route. +.It Cm change +Change aspects of a route (such as its gateway). +.It Cm get +Lookup and display the route for a destination. +.It Cm monitor +Continuously report any changes to the routing information base, +routing lookup misses, or suspected network partitionings. +.El +.Pp +The monitor command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Cm monitor +.Ed +.Pp +The flush command has the syntax +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Cm flush +.Op Ar family +.Ed +.Pp +If the +.Cm flush +command is specified, +.Nm route +will ``flush'' the routing tables of all gateway entries. +When the address family may is specified by any of the +.Fl osi , +.Fl xns , +or +.Fl inet +modifiers, only routes having destinations with addresses in the +delineated family will be deleted. +.Pp +The other commands have the following syntax: +.Pp +.Bd -filled -offset indent -compact +.Nm route Op Fl n +.Ar command +.Op Fl net No \&| Fl host +.Ar destination gateway +.Ed +.Pp +where +.Ar destination +is the destination host or network, +.Ar gateway +is the next-hop intermediary via which packets should be routed. +Routes to a particular host may be distinguished from those to +a network by interpreting the Internet address specified as the +.Ar destination argument. +The optional modifiers +.Fl net +and +.Fl host +force the destination to be interpreted as a network or a host, respectively. +Otherwise, if the +.Ar destination +has a ``local address part'' of +INADDR_ANY , +or if the +.Ar destination +is the symbolic name of a network, then the route is +assumed to be to a network; otherwise, it is presumed to be a +route to a host. +.Pp +For example, +.Li 128.32 +is interpreted as +.Fl host Li 128.0.0.32 ; +.Li 128.32.130 +is interpreted as +.Fl host Li 128.32.0.130 ; +.Fl net Li 128.32 +is interpreted as +.Li 128.32.0.0; +and +.Fl net Li 128.32.130 +is interpreted as +.Li 128.32.130.0 . +.Pp +If the destination is directly reachable +via an interface requiring +no intermediary system to act as a gateway, the +.Fl interface +modifier should be specified; +the gateway given is the address of this host on the common network, +indicating the interface to be used for transmission. +.Pp +The optional modifiers +.Fl xns , +.Fl osi , +and +.Fl link +specify that all subsequent addresses are in the +.Tn XNS +.Tn OSI +address families, +or are specified as link-level addresses, +and the names must be numeric specifications rather than +symbolic names. +.Pp +The optional +.Fl netmask +qualifier is intended +to achieve the effect of an +.Tn OSI +.Tn ESIS +redirect with the netmask option, +or to manually add subnet routes with +netmasks different from that of the implied network interface +(as would otherwise be communicated using the OSPF or ISIS routing protocols). +One specifies an additional ensuing address parameter +(to be interpreted as a network mask). +The implicit network mask generated in the AF_INET case +can be overridden by making sure this option follows the destination parameter. +.Pp +Routes have associated flags which influence operation of the protocols +when sending to destinations matched by the routes. +These flags may be set (or sometimes cleared) +by indicating the following corresponding modifiers: +.Bd -literal +-cloning RTF_CLONING - generates a new route on use +-xresolve RTF_XRESOLVE - emit mesg on use (for external lookup) +-iface ~RTF_GATEWAY - destination is directly reachable +-static RTF_STATIC - manually added route +-nostatic ~RTF_STATIC - pretend route added by kernel or daemon +-reject RTF_REJECT - emit an ICMP unreachable when matched +-blackhole RTF_BLACKHOLE - silently discard pkts (during updates) +-proto1 RTF_PROTO1 - set protocol specific routing flag #1 +-proto2 RTF_PROTO2 - set protocol specific routing flag #2 +-llinfo RTF_LLINFO - validly translates proto addr to link addr +.Ed +.Pp +The optional modifiers +.Fl rtt , +.Fl rttvar , +.Fl sendpipe , +.Fl recvpipe , +.Fl mtu , +.Fl hopcount , +.Fl expire , +and +.Fl ssthresh +provide initial values to quantities maintained in the routing entry +by transport level protocols, such as TCP or TP4. +These may be individually locked by preceding each such modifier to +be locked by +the +.Fl lock +meta-modifier, or one can +specify that all ensuing metrics may be locked by the +.Fl lockrest +meta-modifier. +.Pp +In a +.Cm change +or +.Cm add +command where the destination and gateway are not sufficient to specify +the route (as in the +.Tn ISO +case where several interfaces may have the +same address), the +.Fl ifp +or +.Fl ifa +modifiers may be used to determine the interface or interface address. +.Pp +All symbolic names specified for a +.Ar destination +or +.Ar gateway +are looked up first as a host name using +.Xr gethostbyname 3 . +If this lookup fails, +.Xr getnetbyname 3 +is then used to interpret the name as that of a network. +.Pp +.Nm Route +uses a routing socket and the new message types +RTM_ADD, +RTM_DELETE, +RTM_GET, +and +RTM_CHANGE. +As such, only the super-user may modify +the routing tables. +.ne 1i +.Sh DIAGNOSTICS +.Bl -tag -width Ds +.It Sy "add [host \&| network ] %s: gateway %s flags %x" +The specified route is being added to the tables. The +values printed are from the routing table entry supplied +in the +.Xr ioctl 2 +call. +If the gateway address used was not the primary address of the gateway +(the first one returned by +.Xr gethostbyname 3 ) , +the gateway address is printed numerically as well as symbolically. +.It Sy "delete [ host &| network ] %s: gateway %s flags %x" +As above, but when deleting an entry. +.It Sy "%s %s done" +When the +.Cm flush +command is specified, each routing table entry deleted +is indicated with a message of this form. +.It Sy "Network is unreachable" +An attempt to add a route failed because the gateway listed was not +on a directly-connected network. +The next-hop gateway must be given. +.It Sy "not in table" +A delete operation was attempted for an entry which +wasn't present in the tables. +.It Sy "routing table overflow" +An add operation was attempted, but the system was +low on resources and was unable to allocate memory +to create the new entry. +.El +.Sh SEE ALSO +.Xr netintro 4 , +.Xr route 4 , +.Xr esis 4 , +.Xr routed 8 , +.Xr XNSrouted 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Sh BUGS +The first paragraph may have slightly exaggerated +.Xr routed Ns 's +abilities. diff --git a/sbin/route/route.c b/sbin/route/route.c new file mode 100644 index 000000000000..2e48e71a2cca --- /dev/null +++ b/sbin/route/route.c @@ -0,0 +1,1415 @@ +/* + * + * Copyright (c) 1983, 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)route.c 8.6 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct keytab { + char *kt_cp; + int kt_i; +} keywords[] = { +#include "keywords.h" + {0, 0} +}; + +struct ortentry route; +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_ns sns; + struct sockaddr_iso siso; + struct sockaddr_dl sdl; + struct sockaddr_x25 sx25; +} so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp; + +typedef union sockunion *sup; +int pid, rtm_addrs, uid; +int s; +int forcehost, forcenet, doflush, nflag, af, qflag, tflag, keyword(); +int iflag, verbose, aflen = sizeof (struct sockaddr_in); +int locking, lockrest, debugonly; +struct rt_metrics rt_metrics; +u_long rtm_inits; +struct in_addr inet_makeaddr(); +char *routename(), *netname(); +void flushroutes(), newroute(), monitor(), sockaddr(), sodump(), bprintf(); +void print_getmsg(), print_rtmsg(), pmsg_common(), pmsg_addrs(), mask_addr(); +int getaddr(), rtmsg(), x25_makemask(); +extern char *inet_ntoa(), *iso_ntoa(), *link_ntoa(); + +__dead void +usage(cp) + char *cp; +{ + if (cp) + (void) fprintf(stderr, "route: botched keyword: %s\n", cp); + (void) fprintf(stderr, + "usage: route [ -nqv ] cmd [[ - ] args ]\n"); + exit(1); + /* NOTREACHED */ +} + +void +quit(s) + char *s; +{ + int sverrno = errno; + + (void) fprintf(stderr, "route: "); + if (s) + (void) fprintf(stderr, "%s: ", s); + (void) fprintf(stderr, "%s\n", strerror(sverrno)); + exit(1); + /* NOTREACHED */ +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) + +int +main(argc, argv) + int argc; + char **argv; +{ + extern int optind; + int ch; + + if (argc < 2) + usage((char *)NULL); + + while ((ch = getopt(argc, argv, "nqdtv")) != EOF) + switch(ch) { + case 'n': + nflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'v': + verbose = 1; + break; + case 't': + tflag = 1; + break; + case 'd': + debugonly = 1; + break; + case '?': + default: + usage((char *)NULL); + } + argc -= optind; + argv += optind; + + pid = getpid(); + uid = getuid(); + if (tflag) + s = open("/dev/null", O_WRONLY, 0); + else + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) + quit("socket"); + if (*argv) + switch (keyword(*argv)) { + case K_GET: + uid = 0; + /* FALLTHROUGH */ + + case K_CHANGE: + case K_ADD: + case K_DELETE: + newroute(argc, argv); + exit(0); + /* NOTREACHED */ + + case K_MONITOR: + monitor(); + /* NOTREACHED */ + + case K_FLUSH: + flushroutes(argc, argv); + exit(0); + /* NOTREACHED */ + } + usage(*argv); + /* NOTREACHED */ +} + +/* + * Purge all entries in the routing tables not + * associated with network interfaces. + */ +void +flushroutes(argc, argv) + int argc; + char *argv[]; +{ + size_t needed; + int mib[6], rlen, seqno; + char *buf, *next, *lim; + register struct rt_msghdr *rtm; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + shutdown(s, 0); /* Don't want to read back our messages */ + if (argc > 1) { + argv++; + if (argc == 2 && **argv == '-') + switch (keyword(*argv + 1)) { + case K_INET: + af = AF_INET; + break; + case K_XNS: + af = AF_NS; + break; + case K_LINK: + af = AF_LINK; + break; + case K_ISO: + case K_OSI: + af = AF_ISO; + break; + case K_X25: + af = AF_CCITT; + default: + goto bad; + } else +bad: usage(*argv); + } + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of routing table"); + lim = buf + needed; + if (verbose) + (void) printf("Examining routing table from sysctl\n"); + seqno = 0; /* ??? */ + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + if (verbose) + print_rtmsg(rtm, rtm->rtm_msglen); + if ((rtm->rtm_flags & RTF_GATEWAY) == 0) + continue; + if (af) { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + + if (sa->sa_family != af) + continue; + } + if (debugonly) + continue; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_seq = seqno; + rlen = write(s, next, rtm->rtm_msglen); + if (rlen < (int)rtm->rtm_msglen) { + (void) fprintf(stderr, + "route: write to routing socket: %s\n", + strerror(errno)); + (void) printf("got only %d for rlen\n", rlen); + break; + } + seqno++; + if (qflag) + continue; + if (verbose) + print_rtmsg(rtm, rlen); + else { + struct sockaddr *sa = (struct sockaddr *)(rtm + 1); + (void) printf("%-20.20s ", rtm->rtm_flags & RTF_HOST ? + routename(sa) : netname(sa)); + sa = (struct sockaddr *)(sa->sa_len + (char *)sa); + (void) printf("%-20.20s ", routename(sa)); + (void) printf("done\n"); + } + } +} + +char * +routename(sa) + struct sockaddr *sa; +{ + register char *cp; + static char line[50]; + struct hostent *hp; + static char domain[MAXHOSTNAMELEN + 1]; + static int first = 1; + char *ns_print(); + + if (first) { + first = 0; + if (gethostname(domain, MAXHOSTNAMELEN) == 0 && + (cp = strchr(domain, '.'))) + (void) strcpy(domain, cp + 1); + else + domain[0] = 0; + } + + if (sa->sa_len == 0) + strcpy(line, "default"); + else switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + cp = 0; + if (in.s_addr == INADDR_ANY || sa->sa_len < 4) + cp = "default"; + if (cp == 0 && !nflag) { + hp = gethostbyaddr((char *)&in, sizeof (struct in_addr), + AF_INET); + if (hp) { + if ((cp = strchr(hp->h_name, '.')) && + !strcmp(cp + 1, domain)) + *cp = 0; + cp = hp->h_name; + } + } + if (cp) + strcpy(line, cp); + else { +#define C(x) ((x) & 0xff) + in.s_addr = ntohl(in.s_addr); + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); + } + break; + } + + case AF_NS: + return (ns_print((struct sockaddr_ns *)sa)); + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + case AF_ISO: + (void) sprintf(line, "iso %s", + iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); + break; + + default: + { u_short *s = (u_short *)sa; + u_short *slim = s + ((sa->sa_len + 1) >> 1); + char *cp = line + sprintf(line, "(%d)", sa->sa_family); + + while (++s < slim) /* start with sa->sa_data */ + cp += sprintf(cp, " %x", *s); + break; + } + } + return (line); +} + +/* + * Return the name of the network whose address is given. + * The address is assumed to be that of a net or subnet, not a host. + */ +char * +netname(sa) + struct sockaddr *sa; +{ + char *cp = 0; + static char line[50]; + struct netent *np = 0; + u_long net, mask; + register u_long i; + int subnetshift; + char *ns_print(); + + switch (sa->sa_family) { + + case AF_INET: + { struct in_addr in; + in = ((struct sockaddr_in *)sa)->sin_addr; + + i = in.s_addr = ntohl(in.s_addr); + if (in.s_addr == 0) + cp = "default"; + else if (!nflag) { + if (IN_CLASSA(i)) { + mask = IN_CLASSA_NET; + subnetshift = 8; + } else if (IN_CLASSB(i)) { + mask = IN_CLASSB_NET; + subnetshift = 8; + } else { + mask = IN_CLASSC_NET; + subnetshift = 4; + } + /* + * If there are more bits than the standard mask + * would suggest, subnets must be in use. + * Guess at the subnet mask, assuming reasonable + * width subnet fields. + */ + while (in.s_addr &~ mask) + mask = (long)mask >> subnetshift; + net = in.s_addr & mask; + while ((mask & 1) == 0) + mask >>= 1, net >>= 1; + np = getnetbyaddr(net, AF_INET); + if (np) + cp = np->n_name; + } + if (cp) + strcpy(line, cp); + else if ((in.s_addr & 0xffffff) == 0) + (void) sprintf(line, "%u", C(in.s_addr >> 24)); + else if ((in.s_addr & 0xffff) == 0) + (void) sprintf(line, "%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16)); + else if ((in.s_addr & 0xff) == 0) + (void) sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8)); + else + (void) sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), + C(in.s_addr >> 16), C(in.s_addr >> 8), + C(in.s_addr)); + break; + } + + case AF_NS: + return (ns_print((struct sockaddr_ns *)sa)); + break; + + case AF_LINK: + return (link_ntoa((struct sockaddr_dl *)sa)); + + case AF_ISO: + (void) sprintf(line, "iso %s", + iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); + break; + + default: + { u_short *s = (u_short *)sa->sa_data; + u_short *slim = s + ((sa->sa_len + 1)>>1); + char *cp = line + sprintf(line, "af %d:", sa->sa_family); + + while (s < slim) + cp += sprintf(cp, " %x", *s++); + break; + } + } + return (line); +} + +void +set_metric(value, key) + char *value; + int key; +{ + int flag = 0; + u_long noval, *valp = &noval; + + switch (key) { +#define caseof(x, y, z) case x: valp = &rt_metrics.z; flag = y; break + caseof(K_MTU, RTV_MTU, rmx_mtu); + caseof(K_HOPCOUNT, RTV_HOPCOUNT, rmx_hopcount); + caseof(K_EXPIRE, RTV_EXPIRE, rmx_expire); + caseof(K_RECVPIPE, RTV_RPIPE, rmx_recvpipe); + caseof(K_SENDPIPE, RTV_SPIPE, rmx_sendpipe); + caseof(K_SSTHRESH, RTV_SSTHRESH, rmx_ssthresh); + caseof(K_RTT, RTV_RTT, rmx_rtt); + caseof(K_RTTVAR, RTV_RTTVAR, rmx_rttvar); + } + rtm_inits |= flag; + if (lockrest || locking) + rt_metrics.rmx_locks |= flag; + if (locking) + locking = 0; + *valp = atoi(value); +} + +void +newroute(argc, argv) + int argc; + register char **argv; +{ + char *cmd, *dest = "", *gateway = "", *err; + int ishost = 0, ret, attempts, oerrno, flags = RTF_STATIC; + int key; + struct hostent *hp = 0; + + if (uid) { + errno = EACCES; + quit("must be root to alter routing table"); + } + cmd = argv[0]; + if (*cmd != 'g') + shutdown(s, 0); /* Don't want to read back our messages */ + while (--argc > 0) { + if (**(++argv)== '-') { + switch (key = keyword(1 + *argv)) { + case K_LINK: + af = AF_LINK; + aflen = sizeof(struct sockaddr_dl); + break; + case K_OSI: + case K_ISO: + af = AF_ISO; + aflen = sizeof(struct sockaddr_iso); + break; + case K_INET: + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + break; + case K_X25: + af = AF_CCITT; + aflen = sizeof(struct sockaddr_x25); + break; + case K_SA: + af = PF_ROUTE; + aflen = sizeof(union sockunion); + break; + case K_XNS: + af = AF_NS; + aflen = sizeof(struct sockaddr_ns); + break; + case K_IFACE: + case K_INTERFACE: + iflag++; + case K_NOSTATIC: + flags &= ~RTF_STATIC; + break; + case K_LOCK: + locking = 1; + break; + case K_LOCKREST: + lockrest = 1; + break; + case K_HOST: + forcehost++; + break; + case K_REJECT: + flags |= RTF_REJECT; + break; + case K_BLACKHOLE: + flags |= RTF_BLACKHOLE; + break; + case K_PROTO1: + flags |= RTF_PROTO1; + break; + case K_PROTO2: + flags |= RTF_PROTO2; + break; + case K_CLONING: + flags |= RTF_CLONING; + break; + case K_XRESOLVE: + flags |= RTF_XRESOLVE; + break; + case K_STATIC: + flags |= RTF_STATIC; + break; + case K_IFA: + argc--; + (void) getaddr(RTA_IFA, *++argv, 0); + break; + case K_IFP: + argc--; + (void) getaddr(RTA_IFP, *++argv, 0); + break; + case K_GENMASK: + argc--; + (void) getaddr(RTA_GENMASK, *++argv, 0); + break; + case K_GATEWAY: + argc--; + (void) getaddr(RTA_GATEWAY, *++argv, 0); + break; + case K_DST: + argc--; + ishost = getaddr(RTA_DST, *++argv, &hp); + dest = *argv; + break; + case K_NETMASK: + argc--; + (void) getaddr(RTA_NETMASK, *++argv, 0); + /* FALLTHROUGH */ + case K_NET: + forcenet++; + break; + case K_MTU: + case K_HOPCOUNT: + case K_EXPIRE: + case K_RECVPIPE: + case K_SENDPIPE: + case K_SSTHRESH: + case K_RTT: + case K_RTTVAR: + argc--; + set_metric(*++argv, key); + break; + default: + usage(1+*argv); + } + } else { + if ((rtm_addrs & RTA_DST) == 0) { + dest = *argv; + ishost = getaddr(RTA_DST, *argv, &hp); + } else if ((rtm_addrs & RTA_GATEWAY) == 0) { + gateway = *argv; + (void) getaddr(RTA_GATEWAY, *argv, &hp); + } else { + int ret = atoi(*argv); + + if (ret == 0) { + if (strcmp(*argv, "0") == 0) + printf("%s,%s", + "old usage of trailing 0", + "assuming route to if\n"); + else + usage((char *)NULL); + iflag = 1; + continue; + } else if (ret > 0 && ret < 10) { + printf("old usage of trailing digit, "); + printf("assuming route via gateway\n"); + iflag = 0; + continue; + } + (void) getaddr(RTA_NETMASK, *argv, 0); + } + } + } + if (forcehost) + ishost = 1; + if (forcenet) + ishost = 0; + flags |= RTF_UP; + if (ishost) + flags |= RTF_HOST; + if (iflag == 0) + flags |= RTF_GATEWAY; + for (attempts = 1; ; attempts++) { + errno = 0; + if ((ret = rtmsg(*cmd, flags)) == 0) + break; + if (errno != ENETUNREACH && errno != ESRCH) + break; + if (af == AF_INET && *gateway && hp && hp->h_addr_list[1]) { + hp->h_addr_list++; + memmove(&so_gate.sin.sin_addr, hp->h_addr_list[0], + hp->h_length); + } else + break; + } + if (*cmd == 'g') + exit(0); + oerrno = errno; + (void) printf("%s %s %s", cmd, ishost? "host" : "net", dest); + if (*gateway) { + (void) printf(": gateway %s", gateway); + if (attempts > 1 && ret == 0 && af == AF_INET) + (void) printf(" (%s)", + inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr)); + } + if (ret == 0) + (void) printf("\n"); + else { + switch (oerrno) { + case ESRCH: + err = "not in table"; + break; + case EBUSY: + err = "entry in use"; + break; + case ENOBUFS: + err = "routing table overflow"; + break; + default: + err = strerror(oerrno); + break; + } + (void) printf(": %s\n", err); + } +} + +void +inet_makenetandmask(net, sin) + u_long net; + register struct sockaddr_in *sin; +{ + u_long addr, mask = 0; + register char *cp; + + rtm_addrs |= RTA_NETMASK; + if (net == 0) + mask = addr = 0; + else if (net < 128) { + addr = net << IN_CLASSA_NSHIFT; + mask = IN_CLASSA_NET; + } else if (net < 65536) { + addr = net << IN_CLASSB_NSHIFT; + mask = IN_CLASSB_NET; + } else if (net < 16777216L) { + addr = net << IN_CLASSC_NSHIFT; + mask = IN_CLASSC_NET; + } else { + addr = net; + if ((addr & IN_CLASSA_HOST) == 0) + mask = IN_CLASSA_NET; + else if ((addr & IN_CLASSB_HOST) == 0) + mask = IN_CLASSB_NET; + else if ((addr & IN_CLASSC_HOST) == 0) + mask = IN_CLASSC_NET; + else + mask = -1; + } + sin->sin_addr.s_addr = htonl(addr); + sin = &so_mask.sin; + sin->sin_addr.s_addr = htonl(mask); + sin->sin_len = 0; + sin->sin_family = 0; + cp = (char *)(&sin->sin_addr + 1); + while (*--cp == 0 && cp > (char *)sin) + ; + sin->sin_len = 1 + cp - (char *)sin; +} + +/* + * Interpret an argument as a network address of some kind, + * returning 1 if a host address, 0 if a network address. + */ +int +getaddr(which, s, hpp) + int which; + char *s; + struct hostent **hpp; +{ + register sup su; + struct ns_addr ns_addr(); + struct iso_addr *iso_addr(); + struct hostent *hp; + struct netent *np; + u_long val; + + if (af == 0) { + af = AF_INET; + aflen = sizeof(struct sockaddr_in); + } + rtm_addrs |= which; + switch (which) { + case RTA_DST: + su = &so_dst; + su->sa.sa_family = af; + break; + case RTA_GATEWAY: + su = &so_gate; + su->sa.sa_family = af; + break; + case RTA_NETMASK: + su = &so_mask; + break; + case RTA_GENMASK: + su = &so_genmask; + break; + case RTA_IFP: + su = &so_ifp; + su->sa.sa_family = af; + break; + case RTA_IFA: + su = &so_ifa; + su->sa.sa_family = af; + break; + default: + usage("Internal Error"); + /*NOTREACHED*/ + } + su->sa.sa_len = aflen; + if (strcmp(s, "default") == 0) { + switch (which) { + case RTA_DST: + forcenet++; + (void) getaddr(RTA_NETMASK, s, 0); + break; + case RTA_NETMASK: + case RTA_GENMASK: + su->sa.sa_len = 0; + } + return (0); + } + switch (af) { + case AF_NS: + if (which == RTA_DST) { + extern short ns_bh[3]; + struct sockaddr_ns *sms = &(so_mask.sns); + memset(sms, 0, sizeof(*sms)); + sms->sns_family = 0; + sms->sns_len = 6; + sms->sns_addr.x_net = *(union ns_net *)ns_bh; + rtm_addrs |= RTA_NETMASK; + } + su->sns.sns_addr = ns_addr(s); + return (!ns_nullhost(su->sns.sns_addr)); + + case AF_OSI: + su->siso.siso_addr = *iso_addr(s); + if (which == RTA_NETMASK || which == RTA_GENMASK) { + register char *cp = (char *)TSEL(&su->siso); + su->siso.siso_nlen = 0; + do {--cp ;} while ((cp > (char *)su) && (*cp == 0)); + su->siso.siso_len = 1 + cp - (char *)su; + } + return (1); + + case AF_LINK: + link_addr(s, &su->sdl); + return (1); + + case AF_CCITT: + ccitt_addr(s, &su->sx25); + return (which == RTA_DST ? x25_makemask() : 1); + + case PF_ROUTE: + su->sa.sa_len = sizeof(*su); + sockaddr(s, &su->sa); + return (1); + + case AF_INET: + default: + break; + } + + if (hpp == NULL) + hpp = &hp; + *hpp = NULL; + if (((val = inet_addr(s)) != -1) && + (which != RTA_DST || forcenet == 0)) { + su->sin.sin_addr.s_addr = val; + if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) + return (1); + else { + val = ntohl(val); + goto netdone; + } + } + if ((val = inet_network(s)) != -1 || + ((np = getnetbyname(s)) != NULL && (val = np->n_net) != 0)) { +netdone: + if (which == RTA_DST) + inet_makenetandmask(val, &su->sin); + return (0); + } + hp = gethostbyname(s); + if (hp) { + *hpp = hp; + su->sin.sin_family = hp->h_addrtype; + memmove(&su->sin.sin_addr, hp->h_addr, hp->h_length); + return (1); + } + (void) fprintf(stderr, "%s: bad value\n", s); + exit(1); +} + +int +x25_makemask() +{ + register char *cp; + + if ((rtm_addrs & RTA_NETMASK) == 0) { + rtm_addrs |= RTA_NETMASK; + for (cp = (char *)&so_mask.sx25.x25_net; + cp < &so_mask.sx25.x25_opts.op_flags; cp++) + *cp = -1; + so_mask.sx25.x25_len = (u_char)&(((sup)0)->sx25.x25_opts); + } + return 0; +} + +short ns_nullh[] = {0,0,0}; +short ns_bh[] = {-1,-1,-1}; + +char * +ns_print(sns) + struct sockaddr_ns *sns; +{ + struct ns_addr work; + union { union ns_net net_e; u_long long_e; } net; + u_short port; + static char mybuf[50], cport[10], chost[25]; + char *host = ""; + register char *p; + register u_char *q; + + work = sns->sns_addr; + port = ntohs(work.x_port); + work.x_port = 0; + net.net_e = work.x_net; + if (ns_nullhost(work) && net.long_e == 0) { + if (!port) + return ("*.*"); + (void) sprintf(mybuf, "*.%XH", port); + return (mybuf); + } + + if (memcmp(ns_bh, work.x_host.c_host, 6) == 0) + host = "any"; + else if (memcmp(ns_nullh, work.x_host.c_host, 6) == 0) + host = "*"; + else { + q = work.x_host.c_host; + (void) sprintf(chost, "%02X%02X%02X%02X%02X%02XH", + q[0], q[1], q[2], q[3], q[4], q[5]); + for (p = chost; *p == '0' && p < chost + 12; p++) + /* void */; + host = p; + } + if (port) + (void) sprintf(cport, ".%XH", htons(port)); + else + *cport = 0; + + (void) sprintf(mybuf,"%XH.%s%s", ntohl(net.long_e), host, cport); + return (mybuf); +} + +void +interfaces() +{ + size_t needed; + int mib[6]; + char *buf, *lim, *next; + register struct rt_msghdr *rtm; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_IFLIST; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) + quit("route-sysctl-estimate"); + if ((buf = malloc(needed)) == NULL) + quit("malloc"); + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) + quit("actual retrieval of interface table"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + print_rtmsg(rtm, rtm->rtm_msglen); + } +} + +void +monitor() +{ + int n; + char msg[2048]; + + verbose = 1; + if (debugonly) { + interfaces(); + exit(0); + } + for(;;) { + n = read(s, msg, 2048); + (void) printf("got message of size %d\n", n); + print_rtmsg((struct rt_msghdr *)msg, n); + } +} + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int +rtmsg(cmd, flags) + int cmd, flags; +{ + static int seq; + int rlen; + register char *cp = m_rtmsg.m_space; + register int l; + +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = ROUNDUP(u.sa.sa_len); memmove(cp, &(u), l); cp += l;\ + if (verbose) sodump(&(u),"u");\ + } + + errno = 0; + memset(&m_rtmsg, 0, sizeof(m_rtmsg)); + if (cmd == 'a') + cmd = RTM_ADD; + else if (cmd == 'c') + cmd = RTM_CHANGE; + else if (cmd == 'g') { + cmd = RTM_GET; + if (so_ifp.sa.sa_family == 0) { + so_ifp.sa.sa_family = AF_LINK; + so_ifp.sa.sa_len = sizeof(struct sockaddr_dl); + rtm_addrs |= RTA_IFP; + } + } else + cmd = RTM_DELETE; +#define rtm m_rtmsg.m_rtm + rtm.rtm_type = cmd; + rtm.rtm_flags = flags; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + rtm.rtm_rmx = rt_metrics; + rtm.rtm_inits = rtm_inits; + + if (rtm_addrs & RTA_NETMASK) + mask_addr(); + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_GATEWAY, so_gate); + NEXTADDR(RTA_NETMASK, so_mask); + NEXTADDR(RTA_GENMASK, so_genmask); + NEXTADDR(RTA_IFP, so_ifp); + NEXTADDR(RTA_IFA, so_ifa); + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + if (verbose) + print_rtmsg(&rtm, l); + if (debugonly) + return (0); + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + perror("writing to routing socket"); + return (-1); + } + if (cmd == RTM_GET) { + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + if (l < 0) + (void) fprintf(stderr, + "route: read from routing socket: %s\n", + strerror(errno)); + else + print_getmsg(&rtm, l); + } +#undef rtm + return (0); +} + +void +mask_addr() +{ + int olen = so_mask.sa.sa_len; + register char *cp1 = olen + (char *)&so_mask, *cp2; + + for (so_mask.sa.sa_len = 0; cp1 > (char *)&so_mask; ) + if (*--cp1 != 0) { + so_mask.sa.sa_len = 1 + cp1 - (char *)&so_mask; + break; + } + if ((rtm_addrs & RTA_DST) == 0) + return; + switch (so_dst.sa.sa_family) { + case AF_NS: + case AF_INET: + case AF_CCITT: + case 0: + return; + case AF_ISO: + olen = MIN(so_dst.siso.siso_nlen, + MAX(so_mask.sa.sa_len - 6, 0)); + break; + } + cp1 = so_mask.sa.sa_len + 1 + (char *)&so_dst; + cp2 = so_dst.sa.sa_len + 1 + (char *)&so_dst; + while (cp2 > cp1) + *--cp2 = 0; + cp2 = so_mask.sa.sa_len + 1 + (char *)&so_mask; + while (cp1 > so_dst.sa.sa_data) + *--cp1 &= *--cp2; + switch (so_dst.sa.sa_family) { + case AF_ISO: + so_dst.siso.siso_nlen = olen; + break; + } +} + +char *msgtypes[] = { + "", + "RTM_ADD: Add Route", + "RTM_DELETE: Delete Route", + "RTM_CHANGE: Change Metrics or flags", + "RTM_GET: Report Metrics", + "RTM_LOSING: Kernel Suspects Partitioning", + "RTM_REDIRECT: Told to use different route", + "RTM_MISS: Lookup failed on this address", + "RTM_LOCK: fix specified metrics", + "RTM_OLDADD: caused by SIOCADDRT", + "RTM_OLDDEL: caused by SIOCDELRT", + "RTM_RESOLVE: Route created by cloning", + "RTM_NEWADDR: address being added to iface", + "RTM_DELADDR: address being removed from iface", + "RTM_IFINFO: iface status change", + 0, +}; + +char metricnames[] = +"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount\1mtu"; +char routeflags[] = +"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT\011CLONING\012XRESOLVE\013LLINFO\014STATIC\017PROTO2\020PROTO1"; +char ifnetflags[] = +"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1\017LINK2\020MULTICAST"; +char addrnames[] = +"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD"; + +void +print_rtmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct if_msghdr *ifm; + struct ifa_msghdr *ifam; + + if (verbose == 0) + return; + if (rtm->rtm_version != RTM_VERSION) { + (void) printf("routing message version %d not understood\n", + rtm->rtm_version); + return; + } + (void)printf("%s: len %d, ", msgtypes[rtm->rtm_type], rtm->rtm_msglen); + switch (rtm->rtm_type) { + case RTM_IFINFO: + ifm = (struct if_msghdr *)rtm; + (void) printf("if# %d, flags:", ifm->ifm_index); + bprintf(stdout, ifm->ifm_flags, ifnetflags); + pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs); + break; + case RTM_NEWADDR: + case RTM_DELADDR: + ifam = (struct ifa_msghdr *)rtm; + (void) printf("metric %d, flags:", ifam->ifam_metric); + bprintf(stdout, ifam->ifam_flags, routeflags); + pmsg_addrs((char *)(ifam + 1), ifam->ifam_addrs); + break; + default: + (void) printf("pid: %d, seq %d, errno %d, flags:", + rtm->rtm_pid, rtm->rtm_seq, rtm->rtm_errno); + bprintf(stdout, rtm->rtm_flags, routeflags); + pmsg_common(rtm); + } +} + +void +print_getmsg(rtm, msglen) + register struct rt_msghdr *rtm; + int msglen; +{ + struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL; + struct sockaddr_dl *ifp = NULL; + register struct sockaddr *sa; + register char *cp; + register int i; + + (void) printf(" route to: %s\n", routename(&so_dst)); + if (rtm->rtm_version != RTM_VERSION) { + (void)fprintf(stderr, + "routing message version %d not understood\n", + rtm->rtm_version); + return; + } + if (rtm->rtm_msglen > msglen) { + (void)fprintf(stderr, + "message length mismatch, in packet %d, returned %d\n", + rtm->rtm_msglen, msglen); + } + if (rtm->rtm_errno) { + (void) fprintf(stderr, "RTM_GET: %s (errno %d)\n", + strerror(rtm->rtm_errno), rtm->rtm_errno); + return; + } + cp = ((char *)(rtm + 1)); + if (rtm->rtm_addrs) + for (i = 1; i; i <<= 1) + if (i & rtm->rtm_addrs) { + sa = (struct sockaddr *)cp; + switch (i) { + case RTA_DST: + dst = sa; + break; + case RTA_GATEWAY: + gate = sa; + break; + case RTA_NETMASK: + mask = sa; + break; + case RTA_IFP: + if (sa->sa_family == AF_LINK && + ((struct sockaddr_dl *)sa)->sdl_nlen) + ifp = (struct sockaddr_dl *)sa; + break; + } + ADVANCE(cp, sa); + } + if (dst && mask) + mask->sa_family = dst->sa_family; /* XXX */ + if (dst) + (void)printf("destination: %s\n", routename(dst)); + if (mask) { + int savenflag = nflag; + + nflag = 1; + (void)printf(" mask: %s\n", routename(mask)); + nflag = savenflag; + } + if (gate && rtm->rtm_flags & RTF_GATEWAY) + (void)printf(" gateway: %s\n", routename(gate)); + if (ifp) + (void)printf(" interface: %.*s\n", + ifp->sdl_nlen, ifp->sdl_data); + (void)printf(" flags: "); + bprintf(stdout, rtm->rtm_flags, routeflags); + +#define lock(f) ((rtm->rtm_rmx.rmx_locks & __CONCAT(RTV_,f)) ? 'L' : ' ') +#define msec(u) (((u) + 500) / 1000) /* usec to msec */ + + (void) printf("\n%s\n", "\ + recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire"); + printf("%8d%c ", rtm->rtm_rmx.rmx_recvpipe, lock(RPIPE)); + printf("%8d%c ", rtm->rtm_rmx.rmx_sendpipe, lock(SPIPE)); + printf("%8d%c ", rtm->rtm_rmx.rmx_ssthresh, lock(SSTHRESH)); + printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rtt), lock(RTT)); + printf("%8d%c ", msec(rtm->rtm_rmx.rmx_rttvar), lock(RTTVAR)); + printf("%8d%c ", rtm->rtm_rmx.rmx_hopcount, lock(HOPCOUNT)); + printf("%8d%c ", rtm->rtm_rmx.rmx_mtu, lock(MTU)); + if (rtm->rtm_rmx.rmx_expire) + rtm->rtm_rmx.rmx_expire -= time(0); + printf("%8d%c\n", rtm->rtm_rmx.rmx_expire, lock(EXPIRE)); +#undef lock +#undef msec +#define RTA_IGN (RTA_DST|RTA_GATEWAY|RTA_NETMASK|RTA_IFP|RTA_IFA|RTA_BRD) + if (verbose) + pmsg_common(rtm); + else if (rtm->rtm_addrs &~ RTA_IGN) { + (void) printf("sockaddrs: "); + bprintf(stdout, rtm->rtm_addrs, addrnames); + putchar('\n'); + } +#undef RTA_IGN +} + +void +pmsg_common(rtm) + register struct rt_msghdr *rtm; +{ + (void) printf("\nlocks: "); + bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); + (void) printf(" inits: "); + bprintf(stdout, rtm->rtm_inits, metricnames); + pmsg_addrs(((char *)(rtm + 1)), rtm->rtm_addrs); +} + +void +pmsg_addrs(cp, addrs) + char *cp; + int addrs; +{ + register struct sockaddr *sa; + int i; + + if (addrs == 0) + return; + (void) printf("\nsockaddrs: "); + bprintf(stdout, addrs, addrnames); + (void) putchar('\n'); + for (i = 1; i; i <<= 1) + if (i & addrs) { + sa = (struct sockaddr *)cp; + (void) printf(" %s", routename(sa)); + ADVANCE(cp, sa); + } + (void) putchar('\n'); + (void) fflush(stdout); +} + +void +bprintf(fp, b, s) + register FILE *fp; + register int b; + register u_char *s; +{ + register int i; + int gotsome = 0; + + if (b == 0) + return; + while (i = *s++) { + if (b & (1 << (i-1))) { + if (gotsome == 0) + i = '<'; + else + i = ','; + (void) putc(i, fp); + gotsome = 1; + for (; (i = *s) > 32; s++) + (void) putc(i, fp); + } else + while (*s > 32) + s++; + } + if (gotsome) + (void) putc('>', fp); +} + +int +keyword(cp) + char *cp; +{ + register struct keytab *kt = keywords; + + while (kt->kt_cp && strcmp(kt->kt_cp, cp)) + kt++; + return kt->kt_i; +} + +void +sodump(su, which) + register sup su; + char *which; +{ + switch (su->sa.sa_family) { + case AF_LINK: + (void) printf("%s: link %s; ", + which, link_ntoa(&su->sdl)); + break; + case AF_ISO: + (void) printf("%s: iso %s; ", + which, iso_ntoa(&su->siso.siso_addr)); + break; + case AF_INET: + (void) printf("%s: inet %s; ", + which, inet_ntoa(su->sin.sin_addr)); + break; + case AF_NS: + (void) printf("%s: xns %s; ", + which, ns_ntoa(su->sns.sns_addr)); + break; + } + (void) fflush(stdout); +} + +/* States*/ +#define VIRGIN 0 +#define GOTONE 1 +#define GOTTWO 2 +/* Inputs */ +#define DIGIT (4*0) +#define END (4*1) +#define DELIM (4*2) + +void +sockaddr(addr, sa) + register char *addr; + register struct sockaddr *sa; +{ + register char *cp = (char *)sa; + int size = sa->sa_len; + char *cplim = cp + size; + register int byte = 0, state = VIRGIN, new; + + memset(cp, 0, size); + cp++; + do { + if ((*addr >= '0') && (*addr <= '9')) { + new = *addr - '0'; + } else if ((*addr >= 'a') && (*addr <= 'f')) { + new = *addr - 'a' + 10; + } else if ((*addr >= 'A') && (*addr <= 'F')) { + new = *addr - 'A' + 10; + } else if (*addr == 0) + state |= END; + else + state |= DELIM; + addr++; + switch (state /* | INPUT */) { + case GOTTWO | DIGIT: + *cp++ = byte; /*FALLTHROUGH*/ + case VIRGIN | DIGIT: + state = GOTONE; byte = new; continue; + case GOTONE | DIGIT: + state = GOTTWO; byte = new + (byte << 4); continue; + default: /* | DELIM */ + state = VIRGIN; *cp++ = byte; byte = 0; continue; + case GOTONE | END: + case GOTTWO | END: + *cp++ = byte; /* FALLTHROUGH */ + case VIRGIN | END: + break; + } + break; + } while (cp < cplim); + sa->sa_len = cp - (char *)sa; +} diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c new file mode 100644 index 000000000000..a377fdc5a2a6 --- /dev/null +++ b/sbin/savecore/savecore.c @@ -0,0 +1,649 @@ +/*- + * Copyright (c) 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1986, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)savecore.c 8.5 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ok(number) ((number) - KERNBASE) + +struct nlist current_nl[] = { /* Namelist for currently running system. */ +#define X_DUMPDEV 0 + { "_dumpdev" }, +#define X_DUMPLO 1 + { "_dumplo" }, +#define X_TIME 2 + { "_time" }, +#define X_DUMPSIZE 3 + { "_dumpsize" }, +#define X_VERSION 4 + { "_version" }, +#define X_PANICSTR 5 + { "_panicstr" }, +#define X_DUMPMAG 6 + { "_dumpmag" }, + { "" }, +}; +int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; +int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; + +struct nlist dump_nl[] = { /* Name list for dumped system. */ + { "_dumpdev" }, /* Entries MUST be the same as */ + { "_dumplo" }, /* those in current_nl[]. */ + { "_time" }, + { "_dumpsize" }, + { "_version" }, + { "_panicstr" }, + { "_dumpmag" }, + { "" }, +}; + +/* Types match kernel declarations. */ +long dumplo; /* where dump starts on dumpdev */ +int dumpmag; /* magic number in dump */ +int dumpsize; /* amount of memory dumped */ + +char *vmunix; +char *dirname; /* directory to save dumps in */ +char *ddname; /* name of dump device */ +dev_t dumpdev; /* dump device */ +int dumpfd; /* read/write descriptor on block dev */ +time_t now; /* current date */ +char panic_mesg[1024]; +int panicstr; +char vers[1024]; + +int clear, compress, force, verbose; /* flags */ + +void check_kmem __P((void)); +int check_space __P((void)); +void clear_dump __P((void)); +int Create __P((char *, int)); +int dump_exists __P((void)); +char *find_dev __P((dev_t, int)); +int get_crashtime __P((void)); +void kmem_setup __P((void)); +void log __P((int, char *, ...)); +void Lseek __P((int, off_t, int)); +int Open __P((char *, int rw)); +int Read __P((int, void *, int)); +char *rawname __P((char *s)); +void save_core __P((void)); +void usage __P((void)); +void Write __P((int, void *, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + + openlog("savecore", LOG_PERROR, LOG_DAEMON); + + while ((ch = getopt(argc, argv, "cdfN:vz")) != EOF) + switch(ch) { + case 'c': + clear = 1; + break; + case 'd': /* Not documented. */ + case 'v': + verbose = 1; + break; + case 'f': + force = 1; + break; + case 'N': + vmunix = optarg; + break; + case 'z': + compress = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!clear) { + if (argc != 1 && argc != 2) + usage(); + dirname = argv[0]; + } + if (argc == 2) + vmunix = argv[1]; + + (void)time(&now); + kmem_setup(); + + if (clear) { + clear_dump(); + exit(0); + } + + if (!dump_exists() && !force) + exit(1); + + check_kmem(); + + if (panicstr) + syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg); + else + syslog(LOG_ALERT, "reboot"); + + if ((!get_crashtime() || !check_space()) && !force) + exit(1); + + save_core(); + + clear_dump(); + exit(0); +} + +void +kmem_setup() +{ + FILE *fp; + int kmem, i; + char *dump_sys; + + /* + * Some names we need for the currently running system, others for + * the system that was running when the dump was made. The values + * obtained from the current system are used to look for things in + * /dev/kmem that cannot be found in the dump_sys namelist, but are + * presumed to be the same (since the disk partitions are probably + * the same!) + */ + if ((nlist(_PATH_UNIX, current_nl)) == -1) + syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno)); + for (i = 0; cursyms[i] != -1; i++) + if (current_nl[cursyms[i]].n_value == 0) { + syslog(LOG_ERR, "%s: %s not in namelist", + _PATH_UNIX, current_nl[cursyms[i]].n_name); + exit(1); + } + + dump_sys = vmunix ? vmunix : _PATH_UNIX; + if ((nlist(dump_sys, dump_nl)) == -1) + syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno)); + for (i = 0; dumpsyms[i] != -1; i++) + if (dump_nl[dumpsyms[i]].n_value == 0) { + syslog(LOG_ERR, "%s: %s not in namelist", + dump_sys, dump_nl[dumpsyms[i]].n_name); + exit(1); + } + + kmem = Open(_PATH_KMEM, O_RDONLY); + Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET); + (void)Read(kmem, &dumpdev, sizeof(dumpdev)); + if (dumpdev == NODEV) { + syslog(LOG_WARNING, "no core dump (no dumpdev)"); + exit(1); + } + Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET); + (void)Read(kmem, &dumplo, sizeof(dumplo)); + if (verbose) + (void)printf("dumplo = %d (%d * %d)\n", + dumplo, dumplo/DEV_BSIZE, DEV_BSIZE); + Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET); + (void)Read(kmem, &dumpmag, sizeof(dumpmag)); + dumplo *= DEV_BSIZE; + ddname = find_dev(dumpdev, S_IFBLK); + dumpfd = Open(ddname, O_RDWR); + fp = fdopen(kmem, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM); + exit(1); + } + if (vmunix) + return; + (void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET); + (void)fgets(vers, sizeof(vers), fp); + + /* Don't fclose(fp), we use dumpfd later. */ +} + +void +check_kmem() +{ + register char *cp; + FILE *fp; + char core_vers[1024]; + + fp = fdopen(dumpfd, "r"); + if (fp == NULL) { + syslog(LOG_ERR, "%s: fdopen: %m", ddname); + exit(1); + } + fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET); + fgets(core_vers, sizeof(core_vers), fp); + if (strcmp(vers, core_vers) && vmunix == 0) + syslog(LOG_WARNING, + "warning: %s version mismatch:\n\t%s\nand\t%s\n", + _PATH_UNIX, vers, core_vers); + (void)fseek(fp, + (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); + (void)fread(&panicstr, sizeof(panicstr), 1, fp); + if (panicstr) { + (void)fseek(fp, dumplo + ok(panicstr), L_SET); + cp = panic_mesg; + do + *cp = getc(fp); + while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]); + } + /* Don't fclose(fp), we use dumpfd later. */ +} + +void +clear_dump() +{ + long newdumplo; + + newdumplo = 0; + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + Write(dumpfd, &newdumplo, sizeof(newdumplo)); +} + +int +dump_exists() +{ + int newdumpmag; + + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + (void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag)); + if (newdumpmag != dumpmag) { + if (verbose) + syslog(LOG_WARNING, "magic number mismatch (%x != %x)", + newdumpmag, dumpmag); + syslog(LOG_WARNING, "no core dump"); + return (0); + } + return (1); +} + +char buf[1024 * 1024]; + +void +save_core() +{ + register FILE *fp; + register int bounds, ifd, nr, nw, ofd; + char *rawp, path[MAXPATHLEN]; + + /* + * Get the current number and update the bounds file. Do the update + * now, because may fail later and don't want to overwrite anything. + */ + (void)snprintf(path, sizeof(path), "%s/bounds", dirname); + if ((fp = fopen(path, "r")) == NULL) + goto err1; + if (fgets(buf, sizeof(buf), fp) == NULL) { + if (ferror(fp)) +err1: syslog(LOG_WARNING, "%s: %s", path, strerror(errno)); + bounds = 0; + } else + bounds = atoi(buf); + if (fp != NULL) + (void)fclose(fp); + if ((fp = fopen(path, "w")) == NULL) + syslog(LOG_ERR, "%s: %m", path); + else { + (void)fprintf(fp, "%d\n", bounds + 1); + (void)fclose(fp); + } + (void)fclose(fp); + + /* Create the core file. */ + (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s", + dirname, bounds, compress ? ".Z" : ""); + if (compress) { + if ((fp = zopen(path, "w", 0)) == NULL) { + syslog(LOG_ERR, "%s: %s", path, strerror(errno)); + exit(1); + } + } else + ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + /* Open the raw device. */ + rawp = rawname(ddname); + if ((ifd = open(rawp, O_RDONLY)) == -1) { + syslog(LOG_WARNING, "%s: %m; using block device", rawp); + ifd = dumpfd; + } + + /* Read the dump size. */ + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); + (void)Read(dumpfd, &dumpsize, sizeof(dumpsize)); + + /* Seek to the start of the core. */ + Lseek(ifd, (off_t)dumplo, L_SET); + + /* Copy the core file. */ + dumpsize *= NBPG; + syslog(LOG_NOTICE, "writing %score to %s", + compress ? "compressed " : "", path); + for (; dumpsize > 0; dumpsize -= nr) { + (void)printf("%6dK\r", dumpsize / 1024); + (void)fflush(stdout); + nr = read(ifd, buf, MIN(dumpsize, sizeof(buf))); + if (nr <= 0) { + if (nr == 0) + syslog(LOG_WARNING, + "WARNING: EOF on dump device"); + else + syslog(LOG_ERR, "%s: %m", rawp); + goto err2; + } + if (compress) + nw = fwrite(buf, 1, nr, fp); + else + nw = write(ofd, buf, nr); + if (nw != nr) { + syslog(LOG_ERR, "%s: %s", + path, strerror(nw == 0 ? EIO : errno)); +err2: syslog(LOG_WARNING, + "WARNING: vmcore may be incomplete"); + (void)printf("\n"); + exit(1); + } + } + (void)printf("\n"); + (void)close(ifd); + if (compress) + (void)fclose(fp); + else + (void)close(ofd); + + /* Copy the kernel. */ + ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY); + (void)snprintf(path, sizeof(path), "%s/vmunix.%d%s", + dirname, bounds, compress ? ".Z" : ""); + if (compress) { + if ((fp = zopen(path, "w", 0)) == NULL) { + syslog(LOG_ERR, "%s: %s", path, strerror(errno)); + exit(1); + } + } else + ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + syslog(LOG_NOTICE, "writing %skernel to %s", + compress ? "compressed " : "", path); + while ((nr = read(ifd, buf, sizeof(buf))) > 0) { + if (compress) + nw = fwrite(buf, 1, nr, fp); + else + nw = write(ofd, buf, nr); + if (nw != nr) { + syslog(LOG_ERR, "%s: %s", + path, strerror(nw == 0 ? EIO : errno)); + syslog(LOG_WARNING, + "WARNING: vmunix may be incomplete"); + exit(1); + } + } + if (nr < 0) { + syslog(LOG_ERR, "%s: %s", + vmunix ? vmunix : _PATH_UNIX, strerror(errno)); + syslog(LOG_WARNING, + "WARNING: vmunix may be incomplete"); + exit(1); + } + if (compress) + (void)fclose(fp); + else + (void)close(ofd); +} + +char * +find_dev(dev, type) + register dev_t dev; + register int type; +{ + register DIR *dfd; + struct dirent *dir; + struct stat sb; + char *dp, devname[MAXPATHLEN + 1]; + + if ((dfd = opendir(_PATH_DEV)) == NULL) { + syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno)); + exit(1); + } + (void)strcpy(devname, _PATH_DEV); + while ((dir = readdir(dfd))) { + (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); + if (lstat(devname, &sb)) { + syslog(LOG_ERR, "%s: %s", devname, strerror(errno)); + continue; + } + if ((sb.st_mode & S_IFMT) != type) + continue; + if (dev == sb.st_rdev) { + closedir(dfd); + if ((dp = strdup(devname)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + return (dp); + } + } + closedir(dfd); + syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev)); + exit(1); +} + +char * +rawname(s) + char *s; +{ + char *sl, name[MAXPATHLEN]; + + if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') { + syslog(LOG_ERR, + "can't make raw dump device name from %s", s); + return (s); + } + (void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1); + if ((sl = strdup(name)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + return (sl); +} + +int +get_crashtime() +{ + time_t dumptime; /* Time the dump was taken. */ + + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); + (void)Read(dumpfd, &dumptime, sizeof(dumptime)); + if (dumptime == 0) { + if (verbose) + syslog(LOG_ERR, "dump time is zero"); + return (0); + } + (void)printf("savecore: system went down at %s", ctime(&dumptime)); +#define LEEWAY (7 * SECSPERDAY) + if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { + (void)printf("dump time is unreasonable\n"); + return (0); + } + return (1); +} + +int +check_space() +{ + register FILE *fp; + char *tvmunix; + off_t minfree, spacefree, vmunixsize, needed; + struct stat st; + struct statfs fsbuf; + char buf[100], path[MAXPATHLEN]; + + tvmunix = vmunix ? vmunix : _PATH_UNIX; + if (stat(tvmunix, &st) < 0) { + syslog(LOG_ERR, "%s: %m", tvmunix); + exit(1); + } + vmunixsize = st.st_blocks * S_BLKSIZE; + if (statfs(dirname, &fsbuf) < 0) { + syslog(LOG_ERR, "%s: %m", dirname); + exit(1); + } + spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024; + + (void)snprintf(path, sizeof(path), "%s/minfree", dirname); + if ((fp = fopen(path, "r")) == NULL) + minfree = 0; + else { + if (fgets(buf, sizeof(buf), fp) == NULL) + minfree = 0; + else + minfree = atoi(buf); + (void)fclose(fp); + } + + needed = (dumpsize + vmunixsize) / 1024; + if (minfree > 0 && spacefree - needed < minfree) { + syslog(LOG_WARNING, + "no dump, not enough free space on device"); + return (0); + } + if (spacefree - needed < minfree) + syslog(LOG_WARNING, + "dump performed, but free space threshold crossed"); + return (1); +} + +int +Open(name, rw) + char *name; + int rw; +{ + int fd; + + if ((fd = open(name, rw, 0)) < 0) { + syslog(LOG_ERR, "%s: %m", name); + exit(1); + } + return (fd); +} + +int +Read(fd, bp, size) + int fd, size; + void *bp; +{ + int nr; + + nr = read(fd, bp, size); + if (nr != size) { + syslog(LOG_ERR, "read: %m"); + exit(1); + } + return (nr); +} + +void +Lseek(fd, off, flag) + int fd, flag; + off_t off; +{ + off_t ret; + + ret = lseek(fd, off, flag); + if (ret == -1) { + syslog(LOG_ERR, "lseek: %m"); + exit(1); + } +} + +int +Create(file, mode) + char *file; + int mode; +{ + register int fd; + + fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode); + if (fd < 0) { + syslog(LOG_ERR, "%s: %m", file); + exit(1); + } + return (fd); +} + +void +Write(fd, bp, size) + int fd, size; + void *bp; +{ + int n; + + if ((n = write(fd, bp, size)) < size) { + syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO)); + exit(1); + } +} + +void +usage() +{ + (void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory"); + exit(1); +} diff --git a/sbin/scsiformat/scsiformat.c b/sbin/scsiformat/scsiformat.c new file mode 100644 index 000000000000..a185a8ee154c --- /dev/null +++ b/sbin/scsiformat/scsiformat.c @@ -0,0 +1,664 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsiformat.c 5.5 (Berkeley) 4/2/94 + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)scsiformat.c 5.5 (Berkeley) 4/2/94"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include + +#define COMPAT_HPSCSI + +#include +#include +#include +#include +#include +#include + +int fd; +char *device; + +void scsi_str __P((char *, char *, int)); +void do_command __P((int, struct scsi_cdb *, void *, int)); +void do_format __P((void)); +void print_capacity __P((void)); +void print_inquiry __P((void)); +void prflags __P((int, const char *)); +u_char *print_mode_page __P((u_char *)); +void print_mode_sense __P((void)); +void usage __P((void)); + +#define N2(c, d) (((c) << 8) | (d)) +#define N3(b, c, d) (((b) << 16) | N2(c, d)) +#define N4(a, b, c, d) (((a) << 24) | N3(b, c, d)) + +int sense_pctl; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + int ch, readonly; + + readonly = 0; + sense_pctl = SCSI_MSENSE_PCTL_CUR; + while ((ch = getopt(argc, argv, "rp:")) != EOF) { + switch(ch) { + case 'r': + readonly = 1; + break; + case 'p': /* mode sense page control */ + switch (*optarg) { + case 'c': + sense_pctl = SCSI_MSENSE_PCTL_CUR; + break; + case 'd': + sense_pctl = SCSI_MSENSE_PCTL_DFLT; + break; + case 's': + sense_pctl = SCSI_MSENSE_PCTL_SAVED; + break; + case 'v': + (void)printf( + "*** note: for variable parameters, 1-bit means ``can write here''\n"); + sense_pctl = SCSI_MSENSE_PCTL_VAR; + break; + } + /* FALLTHROUGH */ + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + device = *argv; + fd = open(device, readonly ? O_RDONLY : O_RDWR, 0); + if (fd < 0) { + (void)fprintf(stderr, + "scsiformat: %s: %s\n", device, strerror(errno)); + exit(1); + } + print_inquiry(); + print_capacity(); + print_mode_sense(); + + if (!readonly) + do_format(); + exit(0); +} + +/* + * Copy a counted string, trimming trailing blanks, and turning the + * result into a C-style string. + */ +void +scsi_str(src, dst, len) + register char *src, *dst; + register int len; +{ + + while (src[len - 1] == ' ') { + if (--len == 0) { + *dst = 0; + return; + } + } + bcopy(src, dst, len); + dst[len] = 0; +} + +void +print_inquiry() +{ + register struct scsi_inq_ansi *si; + int ver; + struct scsi_inquiry inqbuf; + char vendor[10], product[17], rev[5]; + static struct scsi_cdb inq = { + CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 + }; + + do_command(fd, &inq, &inqbuf, sizeof(inqbuf)); + (void)printf("%s: ", device); + + ver = (inqbuf.si_version >> VER_ANSI_SHIFT) & VER_ANSI_MASK; + if (ver != 1 && ver != 2) { + (void)printf("type 0x%x, qual 0x%x, ver 0x%x (ansi %d)\n", + inqbuf.si_type, inqbuf.si_qual, inqbuf.si_version, ver); + return; + } + si = (struct scsi_inq_ansi *)&inqbuf; + switch (si->si_type & TYPE_TYPE_MASK) { + + case TYPE_DAD: + (void)printf("(disk)"); + break; + + case TYPE_WORM: + (void)printf("(WORM)"); + break; + + case TYPE_ROM: + (void)printf("(CD-ROM)"); + break; + + case TYPE_MO: + (void)printf("(MO-DISK)"); + break; + + case TYPE_JUKEBOX: + (void)printf("(jukebox)"); + break; + + default: + (void)printf("(??)"); + break; + } + scsi_str(si->si_vendor, vendor, sizeof(si->si_vendor)); + scsi_str(si->si_product, product, sizeof(si->si_product)); + scsi_str(si->si_rev, rev, sizeof(si->si_rev)); + (void)printf(" %s %s rev %s:", vendor, product, rev); +} + +void +print_capacity() +{ + struct scsi_rc rc; /* for READ CAPACITY */ + static struct scsi_cdb cap = { CMD_READ_CAPACITY }; + + do_command(fd, &cap, &rc, sizeof(rc)); + (void)printf(" %d blocks of %d bytes each\n", + N4(rc.rc_lbah, rc.rc_lbahm, rc.rc_lbalm, rc.rc_lbal) + 1, + N4(rc.rc_blh, rc.rc_blhm, rc.rc_bllm, rc.rc_bll)); +} + +void +print_mode_sense() +{ + register u_char *cp, *ep; + register struct scsi_ms_bd *bd; + register int n, i, l, len, bdlen; +#ifdef TEN_BYTE_SENSE + struct { + struct scsi_ms10 ms; + u_char p[1023 - sizeof(struct scsi_ms10)]; + } msbuf; + static struct scsi_cdb modesense = { + CMD_MODE_SENSE10, SCSI_MSENSE_DBD, 0, 0, 0, 0, 0, + sizeof(msbuf) >> 8, sizeof (msbuf), 0 + }; + + CDB10(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL; + do_command(fd, &modesense, &msbuf, sizeof(msbuf)); + len = N2(msbuf.ms.ms_lenh, msbuf.ms.ms_lenl); + bdlen = N2(msbuf.ms.ms_bdlh, msbuf.ms.ms_bdll); +#else + struct { + struct scsi_ms6 ms; + u_char p[255 - sizeof(struct scsi_ms6)]; + } msbuf; + static struct scsi_cdb modesense = { + CMD_MODE_SENSE6, 0, 0, 0, sizeof(msbuf), 0 + }; + + CDB6(&modesense)->cdb_lbam = sense_pctl | SCSI_MS_PC_ALL; + do_command(fd, &modesense, &msbuf, sizeof(msbuf)); + len = msbuf.ms.ms_len; + bdlen = msbuf.ms.ms_bdl; +#endif + (void)printf("\n%d bytes of mode sense data. ", len); + (void)printf("medium type 0x%x, %swrite protected\n", + msbuf.ms.ms_mt, msbuf.ms.ms_dsp & SCSI_MS_DSP_WP ? "" : "not "); + if ((n = bdlen) != 0) { + bd = (struct scsi_ms_bd *)msbuf.p; + for (n /= sizeof(*bd); --n >= 0; bd++) { + (void)printf("\tdensity code 0x%x, ", bd->bd_dc); + i = N3(bd->bd_nbh, bd->bd_nbm, bd->bd_nbl); + l = N3(bd->bd_blh, bd->bd_blm, bd->bd_bll); + if (i) + (void)printf("%d blocks of length %d\n", i, l); + else + (void)printf("all blocks of length %d\n", l); + } + } + /* + * Sense header lengths includes the sense header, while mode page + * lengths do not ... let's hear it for consistency! + */ + cp = msbuf.p + bdlen; + ep = msbuf.p + len - sizeof(msbuf.ms); + while (cp < ep) + cp = print_mode_page(cp); +} + +void +prflags(v, cp) + int v; + register const char *cp; +{ + register const char *np; + char f, sep; + + for (sep = '<'; (f = *cp++) != 0; cp = np) { + for (np = cp; *np >= ' ';) + np++; + if ((v & (1 << (f - 1))) == 0) + continue; + printf("%c%.*s", sep, np - cp, cp); + sep = ','; + } + if (sep != '<') + putchar('>'); +} + +static char * +cache_policy(x) + int x; +{ + static char rsvd[30]; + + switch (x) { + + case SCSI_CACHE_DEFAULT: + return ("default"); + + case SCSI_CACHE_KEEPPF: + return ("toss cmd data, save prefetch"); + + case SCSI_CACHE_KEEPCMD: + return ("toss prefetch data, save cmd"); + + default: + (void)sprintf(rsvd, "reserved %d", x); + return (rsvd); + } + /* NOTREACHED */ +} + +u_char * +print_mode_page(cp) + u_char *cp; +{ + register struct scsi_ms_page_hdr *mp; + int len, code, i; + u_char *tp; + const char *s; + + mp = (struct scsi_ms_page_hdr *)cp; + code = mp->mp_psc & SCSI_MS_PC_MASK; + len = mp->mp_len; + (void)printf("\npage type %d%s (%d bytes): ", + code, mp->mp_psc & SCSI_MS_MP_SAVEABLE ? " (saveable)" : "", len); + switch (code) { + + case SCSI_MS_PC_RWERRREC: +#define rw ((struct scsi_page_rwerrrec *)(mp + 1)) + (void)printf("Read/Write Error Recovery parameters.\n"); + (void)printf("\tflags = 0x%x", rw->rw_flags); + prflags(rw->rw_flags, + "\10AWRE\7ARRE\6TB\5RC\4EER\3PER\2DTE\1DCR"); + (void)printf(",\n\t%d read retries, %d correction span bits,\n", + rw->rw_read_retry, rw->rw_corr_span); + (void)printf("\t%d head offsets, %d data strobe offsets%s\n", + rw->rw_hd_off, rw->rw_ds_off, len > 6 ? "," : "."); + if (len <= 6) + break; + (void)printf("\t%d write retries, ", rw->rw_write_retry); + i = N2(rw->rw_rtlh, rw->rw_rtll); + if (i != 0xffff) + (void)printf("%d", i); + else + (void)printf("no"); + (void)printf(" recovery time limit.\n"); + break; +#undef rw + + case SCSI_MS_PC_DR: +#define dr ((struct scsi_page_dr *)(mp + 1)) + (void)printf("Disconnect/Reconnect control.\n"); + (void)printf("\tbuffer full ratio %d, buffer empty ratio %d,\n", + dr->dr_full, dr->dr_empty); + (void)printf("\ttime limits: %d bus inactivity, ", + N2(dr->dr_inacth, dr->dr_inactl)); + (void)printf("%d disconnect, %d connect.\n", + N2(dr->dr_disconh, dr->dr_disconl), + N2(dr->dr_conh, dr->dr_conl)); + (void)printf("\tmaximum burst size %d,\n", + N2(dr->dr_bursth, dr->dr_burstl)); + switch (dr->dr_dtdc & SCSI_DR_DTDC_MASK) { + case SCSI_DR_DTDC_NONE: + s = "never"; + break; + case SCSI_DR_DTDC_NOTDATA: + s = "during data transfer"; + break; + case SCSI_DR_DTDC_RSVD: + s = "???"; + break; + case SCSI_DR_DTDC_NOTD2: + s = "during and after data transfer"; + break; + } + (void)printf("\tsuppress disconnect %s.\n", s); + break; +#undef dr + + case SCSI_MS_PC_FMT: +#define fmt ((struct scsi_page_fmt *)(mp + 1)) + (void)printf("Format parameters.\n"); + (void)printf("\t%d tracks/zone, %d alt.sect./zone, ", + N2(fmt->fmt_tpzh, fmt->fmt_tpzl), + N2(fmt->fmt_aspzh, fmt->fmt_aspzl)); + (void)printf("%d alt.tracks/zone,\n\t%d alt.tracks/vol., ", + N2(fmt->fmt_atpzh, fmt->fmt_atpzl), + N2(fmt->fmt_atpvh, fmt->fmt_atpvl)); + (void)printf("%d sectors/track, %d bytes/phys.sector,\n", + N2(fmt->fmt_spth, fmt->fmt_sptl), + N2(fmt->fmt_dbppsh, fmt->fmt_dbppsl)); + (void)printf("\tinterleave %d, track skew %d, cyl.skew %d,\n", + N2(fmt->fmt_ilh, fmt->fmt_ill), + N2(fmt->fmt_tsfh, fmt->fmt_tsfl), + N2(fmt->fmt_csfh, fmt->fmt_csfl)); + (void)printf("\tdrive flags 0x%x", fmt->fmt_flags); + prflags(fmt->fmt_flags, "\10SSEC\7HSEC\6RMB\5SURF"); + (void)printf(".\n"); + break; +#undef fmt + + case SCSI_MS_PC_RDGEOM: +#define rd ((struct scsi_page_rdgeom *)(mp + 1)) + (void)printf("Disk Geometry parameters.\n"); + (void)printf("\t%d cylinders, %d heads,\n", + N3(rd->rd_ncylh, rd->rd_ncylm, rd->rd_ncyll), + rd->rd_nheads); + (void)printf("\tstart write precompensation at cyl %d,\n", + N3(rd->rd_wpcylh, rd->rd_wpcylm, rd->rd_wpcyll)); + (void)printf("\tstart reduced write current at cyl %d,\n", + N3(rd->rd_rwcylh, rd->rd_rwcylm, rd->rd_rwcyll)); + (void)printf("\tseek step rate %f us, landing zone cyl %d,\n", + N2(rd->rd_steph, rd->rd_stepl) * 0.1, + N3(rd->rd_lcylh, rd->rd_lcylm, rd->rd_lcyll)); + switch (rd->rd_rpl & SCSI_RD_RPL_MASK) { + case SCSI_RD_RPL_NONE: + s = "disabled or unsupported"; + break; + case SCSI_RD_RPL_SLAVE: + s = "slave"; + break; + case SCSI_RD_RPL_MASTER: + s = "master"; + break; + case SCSI_RD_RPL_MCONTROL: + s = "master control"; + break; + } + (void)printf("\trotational synch %s, offset %d/256%s\n", + s, rd->rd_roff, len > 18 ? "," : "."); + if (len > 18) + (void)printf("\trotation %d rpm.\n", + N2(rd->rd_rpmh, rd->rd_rpml)); + break; +#undef rd + + case SCSI_MS_PC_VERRREC: +#define v ((struct scsi_page_verrrec *)(mp + 1)) + (void)printf("Verify Error Recovery parameters.\n"); + (void)printf("\tflags = 0x%x", v->v_flags); + prflags(v->v_flags, "\4EER\3PER\2DTE\1DCR"); + (void)printf(",\n\t%d verify retries, %d %s span bits,\n\t", + v->v_verify_retry, v->v_corr_span, "correction"); + (void)printf("%d recovery time limit.\n", + N2(v->v_rtlh, v->v_rtll)); + break; +#undef v + + case SCSI_MS_PC_CACHE: +#define cache ((struct scsi_page_cache *)(mp + 1)) + (void)printf("Caching Page.\n"); + (void)printf("\tflags = 0x%x", cache->cache_flags); + prflags(cache->cache_flags, "\3WCE\2MF\1RCD"); + (void)printf( + ",\n\tread retention = %s, write retention = %s,\n", + cache_policy(SCSI_CACHE_RDPOLICY(cache->cache_reten)), + cache_policy(SCSI_CACHE_WRPOLICY(cache->cache_reten))); + (void)printf("\tdisable prefetch transfer length = %d,\n", + N2(cache->cache_dptlh, cache->cache_dptll)); + (void)printf("\tmin prefetch = %d, max prefetch = %d, ", + N2(cache->cache_minpfh, cache->cache_minpfl), + N2(cache->cache_maxpfh, cache->cache_maxpfl)); + (void)printf("max prefetch ceiling = %d.\n", + N2(cache->cache_mpch, cache->cache_mpcl)); + break; +#undef cache + + case SCSI_MS_PC_CTLMODE: +#define cm ((struct scsi_page_ctlmode *)(mp + 1)) + (void)printf("Control Mode Page.\n"); + (void)printf("\t%s report log-activity error conditions,\n", + cm->cm_rlec & SCSI_CM_RLEC ? "do" : "do not"); + (void)printf("\tqueue algorithm modifier = %d, flags = 0x%x", + SCSI_CM_QMOD(cm->cm_qctl), + cm->cm_qctl & (SCSI_CM_QERR|SCSI_CM_DQUE)); + prflags(cm->cm_qctl, "\2QERR\1DQUE"); + (void)printf(",\n\tECA/AEN flags = 0x%x", cm->cm_ecaaen); + prflags(cm->cm_ecaaen, "\10ECA\3RAENP\2UUAENP\1EAENP"); + (void)printf(", AEN holdoff period = %d ms.\n", + N2(cm->cm_aenholdh, cm->cm_aenholdl)); + break; +#undef cm + + /* + * Vendor Unique, but what the heck. + */ + case SCSI_MS_PC_CDCCACHECTL: +#define ccm ((struct scsi_page_CDCcachectlmode *)(mp + 1)) + (void)printf("CDC-specific Cache Control Mode Page.\n"); + (void)printf("\tflags = 0x%x", ccm->ccm_flags); + prflags(ccm->ccm_flags, "\7WIE\5ENABLE"); + (void)printf(", table size = %d, prefetch threshold = %d\n", + SCSI_CDC_CCM_TBLSZ(ccm->ccm_flags), + ccm->ccm_pfthresh); + (void)printf("\tmaximum %s = %d, maximum %s = %d,\n", + "threshold", ccm->ccm_maxthresh, + "prefetch multiplier", ccm->ccm_maxpfmult); + (void)printf("\tminimum %s = %d, minimum %s = %d.\n", + "threshold", ccm->ccm_minthresh, + "prefetch multiplier", ccm->ccm_minpfmult); + break; +#undef ccm + + default: + (void)printf("Unknown page type."); + for (tp = cp + sizeof(*mp), i = 0; i < len; ++i) { + if ((i & 7) == 0) + (void)printf("\n\t%2d: ", i); + (void)printf(" %02x", *tp++); + } + (void)printf(".\n"); + break; + } + return (cp + sizeof(*mp) + len); +} + +void +pr_sense(fd) + int fd; +{ + static struct scsi_fmt_sense s; + register struct scsi_sense *sn; + + if (ioctl(fd, SDIOCSENSE, &s) < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSENSE: %s\n", strerror(errno)); + + (void)printf("scsi status 0x%x", s.status); + if (s.status & STS_CHECKCOND) { + sn = (struct scsi_sense *)s.sense; + + (void)printf(" sense class %d, code %d", + SENSE_ECLASS(sn), SENSE_ECODE(sn)); + if (SENSE_ISXSENSE(sn)) { + (void)printf(", key %d", XSENSE_KEY(sn)); + if (XSENSE_IVALID(sn)) + (void)printf(", blk %d", XSENSE_INFO(sn)); + } + } + (void)printf("\n"); +} + +void +do_format() +{ + struct { + struct scsi_ms6 ms; /* mode select header */ + struct scsi_ms_bd bd; /* block descriptor */ + struct scsi_ms_page_hdr mp; /* ctl mode page hdr */ + struct scsi_page_ctlmode cm; /* ctl mode page */ + u_char pad[4]; /* ??? */ + } msel; + u_char fmtbuf[128]; + static struct scsi_cdb modeselect = { + CMD_MODE_SELECT6, + SCSI_MSEL_SCSI2_DATA | SCSI_MSEL_SAVEPAGES, 0, 0, + sizeof(msel), 0 + }; + static struct scsi_cdb format = { CMD_FORMAT_UNIT }; + + /* want mostly 0s; set them all zero here */ + bzero(&msel, sizeof(msel)); + + /* one block descriptor */ + msel.ms.ms_bdl = sizeof(struct scsi_ms_bd); + + /* block length = 512 bytes */ + msel.bd.bd_blm = 512 / 256; + msel.bd.bd_bll = 512 % 256; + + /* + * In the following, the mystery pad region is copied from + * the original driver. I have no idea what it is for. + * (Anyone got SCSI-2 documents?) + */ + + /* mode page parameters: report log-activity exception conditions */ + msel.mp.mp_psc = SCSI_MS_PC_CTLMODE; + msel.mp.mp_len = sizeof(msel.cm) + sizeof(msel.pad); + msel.cm.cm_rlec = SCSI_CM_RLEC; + + do_command(fd, &modeselect, &msel, sizeof(msel)); + + bzero(fmtbuf, sizeof(fmtbuf)); + do_command(fd, &format, fmtbuf, sizeof(fmtbuf)); +} + +void +do_command(fd, cdb, buf, len) + int fd; + struct scsi_cdb *cdb; + void *buf; + int len; +{ + static int on = 1, off = 0; + int user, ret; + + bzero(buf, len); + if (ioctl(fd, SDIOCSFORMAT, &on) < 0) { + (void)fprintf(stderr, + "scsiformat: SDIOCSFORMAT (on): %s\n", strerror(errno)); + if (ioctl(fd, SDIOCGFORMAT, &user) == 0 && user != 0) + (void)fprintf(stderr, "scsiformat: pid %d has it\n", + user); + return; + } + ret = ioctl(fd, SDIOCSCSICOMMAND, cdb); +#ifdef COMPAT_HPSCSI + if (ret < 0) { + static const char scsicmdlen[8] = { 6, 10, 0, 0, 0, 12, 0, 0 }; +#define SCSICMDLEN(cmd) scsicmdlen[(cmd) >> 5] + struct scsi_fmt_cdb { + int len; + u_char cdb[28]; + } sc; +#define OSDIOCSCSICOMMAND _IOW('S', 0x3, struct scsi_fmt_cdb) + + sc.len = SCSICMDLEN(cdb->cdb_bytes[0]); + bcopy(cdb->cdb_bytes, sc.cdb, sc.len); + ret = ioctl(fd, OSDIOCSCSICOMMAND, &sc); + } +#endif + if (ret < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSCSICOMMAND: %s\n", strerror(errno)); + else if (read(fd, buf, len) < 0) { + (void)fprintf(stderr, + "scsiformat: read: %s\n", strerror(errno)); + pr_sense(fd); + } + + if (ioctl(fd, SDIOCSFORMAT, &off) < 0) + (void)fprintf(stderr, + "scsiformat: SDIOCSFORMAT (off): %s\n", strerror(errno)); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: scsiformat [-r] [-p c|d|s|v] device\n"); + exit(1); +} diff --git a/sbin/shutdown/shutdown.8 b/sbin/shutdown/shutdown.8 new file mode 100644 index 000000000000..74126f83cc66 --- /dev/null +++ b/sbin/shutdown/shutdown.8 @@ -0,0 +1,162 @@ +.\" Copyright (c) 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)shutdown.8 8.2 (Berkeley) 4/27/95 +.\" +.Dd April 27, 1995 +.Dt SHUTDOWN 8 +.Os BSD 4 +.Sh NAME +.Nm shutdown +.Nd "close down the system at a given time" +.Sh SYNOPSIS +.Nm shutdown +.Op Fl +.Op Fl fhkrn +.Ar time +.Op Ar warning-message ... +.Sh DESCRIPTION +.Nm Shutdown +provides an automated shutdown procedure for super-users +to nicely notify users when the system is shutting down, +saving them from system administrators, hackers, and gurus, who +would otherwise not bother with such niceties. +.Pp +Available friendlinesses: +.Bl -tag -width time +.It Fl f +.Nm Shutdown +arranges, in the manner of +.Xr fastboot 8 , +for the file systems +.Em not to be +checked on reboot. +.It Fl h +The system is halted at the specified +.Ar time +when +.Nm shutdown +execs +.Xr halt 8 . +.It Fl k +Kick everybody off. +The +.Fl k +option +does not actually halt the system, but leaves the +system multi-user with logins disabled (for all but super-user). +.It Fl n +Prevent the normal +.Xr sync 2 +before stopping. +.It Fl r +.Nm Shutdown +execs +.Xr reboot 8 +at the specified +.Ar time . +.It Ar time +.Ar Time +is the time at which +.Nm shutdown +will bring the system down and +may be the word +.Ar now +(indicating an immediate shutdown) or +specify a future time in one of two formats: +.Ar +number , +or +.Ar yymmddhhmm , +where the year, month, and day may be defaulted +to the current system values. The first form brings the system down in +.Ar number +minutes and the second at the absolute time specified. +.It Ar warning-message +Any other arguments comprise the warning message that is broadcast +to users currently logged into the system. +.It Fl +If +.Ql Fl +is supplied as an option, the warning message is read from the standard +input. +.El +.Pp +At intervals, becoming more frequent as apocalypse approaches +and starting at ten hours before shutdown, warning messages are displayed +on the terminals of all users logged in. Five minutes before +shutdown, or immediately if shutdown is in less than 5 minutes, +logins are disabled by creating +.Pa /etc/nologin +and copying the +warning message there. If this file exists when a user attempts to +log in, +.Xr login 1 +prints its contents and exits. The file is +removed just before +.Nm shutdown +exits. +.Pp +At shutdown time a message is written in the system log, containing the +time of shutdown, who initiated the shutdown and the reason. +A terminate +signal is then sent to +.Xr init +to bring the system down to single-user state (depending on above +options). +The time of the shutdown and the warning message +are placed in +.Pa /etc/nologin +and should be used to +inform the users about when the system will be back up +and why it is going down (or anything else). +.Sh FILES +.Bl -tag -width /etc/nologin -compact +.It Pa /etc/nologin +tells login not to let anyone log in +.It Pa /fastboot +tells +.Xr rc 8 +not to run fsck when rebooting +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr wall 1 , +.Xr fastboot 8 , +.Xr halt 8 , +.Xr reboot 8 +.Sh BACKWARD COMPATIBILITY +The hours and minutes in the second time format may be separated by +a colon (``:'') for backward compatibility. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/sbin/shutdown/shutdown.c b/sbin/shutdown/shutdown.c new file mode 100644 index 000000000000..373c6f7f6631 --- /dev/null +++ b/sbin/shutdown/shutdown.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 1988, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +#ifdef DEBUG +#undef _PATH_NOLOGIN +#define _PATH_NOLOGIN "./nologin" +#undef _PATH_FASTBOOT +#define _PATH_FASTBOOT "./fastboot" +#endif + +#define H *60*60 +#define M *60 +#define S *1 +#define NOLOG_TIME 5*60 +struct interval { + int timeleft, timetowait; +} tlist[] = { + 10 H, 5 H, 5 H, 3 H, 2 H, 1 H, 1 H, 30 M, + 30 M, 10 M, 20 M, 10 M, 10 M, 5 M, 5 M, 3 M, + 2 M, 1 M, 1 M, 30 S, 30 S, 30 S, + 0, 0, +}; +#undef H +#undef M +#undef S + +static time_t offset, shuttime; +static int dofast, dohalt, doreboot, killflg, mbuflen; +static char *nosync, *whom, mbuf[BUFSIZ]; + +void badtime __P((void)); +void die_you_gravy_sucking_pig_dog __P((void)); +void doitfast __P((void)); +void finish __P((int)); +void getoffset __P((char *)); +void loop __P((void)); +void nolog __P((void)); +void timeout __P((int)); +void timewarn __P((int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + register char *p, *endp; + struct passwd *pw; + int arglen, ch, len, readstdin; + +#ifndef DEBUG + if (geteuid()) { + (void)fprintf(stderr, "shutdown: NOT super-user\n"); + exit(1); + } +#endif + nosync = NULL; + readstdin = 0; + while ((ch = getopt(argc, argv, "-fhknr")) != EOF) + switch (ch) { + case '-': + readstdin = 1; + break; + case 'f': + dofast = 1; + break; + case 'h': + dohalt = 1; + break; + case 'k': + killflg = 1; + break; + case 'n': + nosync = "-n"; + break; + case 'r': + doreboot = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (dofast && nosync) { + (void)fprintf(stderr, + "shutdown: incompatible switches -f and -n.\n"); + usage(); + } + if (doreboot && dohalt) { + (void)fprintf(stderr, + "shutdown: incompatible switches -h and -r.\n"); + usage(); + } + getoffset(*argv++); + + if (*argv) { + for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { + arglen = strlen(*argv); + if ((len -= arglen) <= 2) + break; + if (p != mbuf) + *p++ = ' '; + memmove(p, *argv, arglen); + p += arglen; + } + *p = '\n'; + *++p = '\0'; + } + + if (readstdin) { + p = mbuf; + endp = mbuf + sizeof(mbuf) - 2; + for (;;) { + if (!fgets(p, endp - p + 1, stdin)) + break; + for (; *p && p < endp; ++p); + if (p == endp) { + *p = '\n'; + *++p = '\0'; + break; + } + } + } + mbuflen = strlen(mbuf); + + if (offset) + (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); + else + (void)printf("Shutdown NOW!\n"); + + if (!(whom = getlogin())) + whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; + +#ifdef DEBUG + (void)putc('\n', stdout); +#else + (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); + { + int forkpid; + + forkpid = fork(); + if (forkpid == -1) { + perror("shutdown: fork"); + exit(1); + } + if (forkpid) { + (void)printf("shutdown: [pid %d]\n", forkpid); + exit(0); + } + } +#endif + openlog("shutdown", LOG_CONS, LOG_AUTH); + loop(); + /* NOTREACHED */ +} + +void +loop() +{ + struct interval *tp; + u_int sltime; + int logged; + + if (offset <= NOLOG_TIME) { + logged = 1; + nolog(); + } + else + logged = 0; + tp = tlist; + if (tp->timeleft < offset) + (void)sleep((u_int)(offset - tp->timeleft)); + else { + while (offset < tp->timeleft) + ++tp; + /* + * Warn now, if going to sleep more than a fifth of + * the next wait time. + */ + if (sltime = offset - tp->timeleft) { + if (sltime > tp->timetowait / 5) + timewarn(offset); + (void)sleep(sltime); + } + } + for (;; ++tp) { + timewarn(tp->timeleft); + if (!logged && tp->timeleft <= NOLOG_TIME) { + logged = 1; + nolog(); + } + (void)sleep((u_int)tp->timetowait); + if (!tp->timeleft) + break; + } + die_you_gravy_sucking_pig_dog(); +} + +static jmp_buf alarmbuf; + +void +timewarn(timeleft) + int timeleft; +{ + static int first; + static char hostname[MAXHOSTNAMELEN + 1]; + FILE *pf; + char wcmd[MAXPATHLEN + 4]; + + if (!first++) + (void)gethostname(hostname, sizeof(hostname)); + + /* undoc -n option to wall suppresses normal wall banner */ + (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); + if (!(pf = popen(wcmd, "w"))) { + syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); + return; + } + + (void)fprintf(pf, + "\007*** %sSystem shutdown message from %s@%s ***\007\n", + timeleft ? "": "FINAL ", whom, hostname); + + if (timeleft > 10*60) + (void)fprintf(pf, "System going down at %5.5s\n\n", + ctime(&shuttime) + 11); + else if (timeleft > 59) + (void)fprintf(pf, "System going down in %d minute%s\n\n", + timeleft / 60, (timeleft > 60) ? "s" : ""); + else if (timeleft) + (void)fprintf(pf, "System going down in 30 seconds\n\n"); + else + (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); + + if (mbuflen) + (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); + + /* + * play some games, just in case wall doesn't come back + * probably unecessary, given that wall is careful. + */ + if (!setjmp(alarmbuf)) { + (void)signal(SIGALRM, timeout); + (void)alarm((u_int)30); + (void)pclose(pf); + (void)alarm((u_int)0); + (void)signal(SIGALRM, SIG_DFL); + } +} + +void +timeout(signo) + int signo; +{ + longjmp(alarmbuf, 1); +} + +void +die_you_gravy_sucking_pig_dog() +{ + + syslog(LOG_NOTICE, "%s by %s: %s", + doreboot ? "reboot" : dohalt ? "halt" : "shutdown", whom, mbuf); + (void)sleep(2); + + (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); + if (killflg) { + (void)printf("\rbut you'll have to do it yourself\r\n"); + finish(0); + } + if (dofast) + doitfast(); +#ifdef DEBUG + if (doreboot) + (void)printf("reboot"); + else if (dohalt) + (void)printf("halt"); + if (nosync) + (void)printf(" no sync"); + if (dofast) + (void)printf(" no fsck"); + (void)printf("\nkill -HUP 1\n"); +#else + if (doreboot) { + execle(_PATH_REBOOT, "reboot", "-l", nosync, 0); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); + perror("shutdown"); + } + else if (dohalt) { + execle(_PATH_HALT, "halt", "-l", nosync, 0); + syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); + perror("shutdown"); + } + (void)kill(1, SIGTERM); /* to single user */ +#endif + finish(0); +} + +#define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; + +void +getoffset(timearg) + register char *timearg; +{ + register struct tm *lt; + register char *p; + time_t now; + + if (!strcasecmp(timearg, "now")) { /* now */ + offset = 0; + return; + } + + (void)time(&now); + if (*timearg == '+') { /* +minutes */ + if (!isdigit(*++timearg)) + badtime(); + offset = atoi(timearg) * 60; + shuttime = now + offset; + return; + } + + /* handle hh:mm by getting rid of the colon */ + for (p = timearg; *p; ++p) + if (!isascii(*p) || !isdigit(*p)) + if (*p == ':' && strlen(p) == 3) { + p[0] = p[1]; + p[1] = p[2]; + p[2] = '\0'; + } + else + badtime(); + + unsetenv("TZ"); /* OUR timezone */ + lt = localtime(&now); /* current time val */ + + switch(strlen(timearg)) { + case 10: + lt->tm_year = ATOI2(timearg); + /* FALLTHROUGH */ + case 8: + lt->tm_mon = ATOI2(timearg); + if (--lt->tm_mon < 0 || lt->tm_mon > 11) + badtime(); + /* FALLTHROUGH */ + case 6: + lt->tm_mday = ATOI2(timearg); + if (lt->tm_mday < 1 || lt->tm_mday > 31) + badtime(); + /* FALLTHROUGH */ + case 4: + lt->tm_hour = ATOI2(timearg); + if (lt->tm_hour < 0 || lt->tm_hour > 23) + badtime(); + lt->tm_min = ATOI2(timearg); + if (lt->tm_min < 0 || lt->tm_min > 59) + badtime(); + lt->tm_sec = 0; + if ((shuttime = mktime(lt)) == -1) + badtime(); + if ((offset = shuttime - now) < 0) { + (void)fprintf(stderr, + "shutdown: that time is already past.\n"); + exit(1); + } + break; + default: + badtime(); + } +} + +#define FSMSG "fastboot file for fsck\n" +void +doitfast() +{ + int fastfd; + + if ((fastfd = open(_PATH_FASTBOOT, O_WRONLY|O_CREAT|O_TRUNC, + 0664)) >= 0) { + (void)write(fastfd, FSMSG, sizeof(FSMSG) - 1); + (void)close(fastfd); + } +} + +#define NOMSG "\n\nNO LOGINS: System going down at " +void +nolog() +{ + int logfd; + char *ct; + + (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ + (void)signal(SIGINT, finish); + (void)signal(SIGHUP, finish); + (void)signal(SIGQUIT, finish); + (void)signal(SIGTERM, finish); + if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, + 0664)) >= 0) { + (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); + ct = ctime(&shuttime); + (void)write(logfd, ct + 11, 5); + (void)write(logfd, "\n\n", 2); + (void)write(logfd, mbuf, strlen(mbuf)); + (void)close(logfd); + } +} + +void +finish(signo) + int signo; +{ + if (!killflg) + (void)unlink(_PATH_NOLOGIN); + exit(0); +} + +void +badtime() +{ + (void)fprintf(stderr, "shutdown: bad time format.\n"); + exit(1); +} + +void +usage() +{ + fprintf(stderr, "usage: shutdown [-fhknr] shutdowntime [ message ]\n"); + exit(1); +} diff --git a/sbin/tunefs/tunefs.8 b/sbin/tunefs/tunefs.8 new file mode 100644 index 000000000000..dc2c397e8975 --- /dev/null +++ b/sbin/tunefs/tunefs.8 @@ -0,0 +1,166 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tunefs.8 8.3 (Berkeley) 5/3/95 +.\" +.Dd May 3, 1995 +.Dt TUNEFS 8 +.Os BSD 4.2 +.Sh NAME +.Nm tunefs +.Nd tune up an existing file system +.Sh SYNOPSIS +.Nm tunefs +.Op Fl AN +.Op Fl a Ar maxcontig +.Op Fl d Ar rotdelay +.Op Fl e Ar maxbpg +.Op Fl m Ar minfree +.Bk -words +.Op Fl o Ar optimize_preference +.Ek +.Op Fl t Ar trackskew +.Op Ar special | Ar filesys +.Sh DESCRIPTION +The +.Nm tunefs +program is designed to change the dynamic parameters of a file system +which affect the layout policies. +The +.Fl N +flag displays all the settable options +(after any changes from the tuning options) +but does not cause any of them to be changed. +The +.Fl A +flag causes the values to be updated in all the alternate +superblocks instead of just the standard superblock. +If this option is not used, +then use of a backup superblock by +.Xr fsck 8 +will lose anything changed by +.Nm tunefs . +The +.Fl A +flag is ignored when the +.Fl N +flag is specified. +.Pp +The parameters which are to be changed are indicated by the flags +given below: +.Bl -tag -width Ds +.It Fl a Ar maxcontig +This specifies the maximum number of contiguous blocks that will +be laid out before forcing a rotational delay (see +.Fl d +below). +The default value is one, since most device drivers require +an interrupt per disk transfer. +Device drivers that can chain several buffers together in a single +transfer should set this to the maximum chain length. +.It Fl d Ar rotdelay +This specifies the expected time (in milliseconds) +to service a transfer completion +interrupt and initiate a new transfer on the same disk. +It is used to decide how much rotational spacing to place between +successive blocks in a file. +.It Fl e Ar maxbpg +This indicates the maximum number of blocks any single file can +allocate out of a cylinder group before it is forced to begin +allocating blocks from another cylinder group. +Typically this value is set to about one quarter of the total blocks +in a cylinder group. +The intent is to prevent any single file from using up all the +blocks in a single cylinder group, +thus degrading access times for all files subsequently allocated +in that cylinder group. +The effect of this limit is to cause big files to do long seeks +more frequently than if they were allowed to allocate all the blocks +in a cylinder group before seeking elsewhere. +For file systems with exclusively large files, +this parameter should be set higher. +.It Fl m Ar minfree +This value specifies the percentage of space held back +from normal users; the minimum free space threshold. +The default value used is 10%. +This value can be set to zero, however up to a factor of three +in throughput will be lost over the performance obtained at a 10% +threshold. +Note that if the value is raised above the current usage level, +users will be unable to allocate files until enough files have +been deleted to get under the higher threshold. +.It Fl o Ar optimize_preference +The file system can either try to minimize the time spent +allocating blocks, or it can attempt to minimize the space +fragmentation on the disk. +If the value of minfree (see above) is less than 10%, +then the file system should optimize for space to avoid +running out of full sized blocks. +For values of minfree greater than or equal to 10%, +fragmentation is unlikely to be problematical, and +the file system can be optimized for time. +.It Fl t Ar trackskew +This specifies the skew in sectors from one track to the next in a cylinder. +The default value is zero, indicating that each track in a cylinder begins at +the same rotational position. +.El +.Sh SEE ALSO +.Xr fs 5 , +.Xr dumpfs 8 , +.Xr fsck 8 , +.Xr newfs 8 , +.Xr mkfs 8 +.Rs +.%A M. McKusick +.%A W. Joy +.%A S. Leffler +.%A R. Fabry +.%T "A Fast File System for UNIX" +.%J "ACM Transactions on Computer Systems 2" +.%N 3 +.%P pp 181-197 +.%D August 1984 +.%O "(reprinted in the BSD System Manager's Manual, SMM:5)" +.Re +.Sh BUGS +This program should work on mounted and active file systems. +Because the super-block is not kept in the buffer cache, +the changes will only take effect if the program +is run on dismounted file systems. +To change the root file system, the system must be rebooted +after the file system is tuned. +.Pp +You can tune a file system, but you can't tune a fish. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/tunefs/tunefs.c b/sbin/tunefs/tunefs.c new file mode 100644 index 000000000000..d5868ebd469a --- /dev/null +++ b/sbin/tunefs/tunefs.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tunefs.c 8.3 (Berkeley) 5/3/95"; +#endif /* not lint */ + +/* + * tunefs: change layout parameters to an existing file system. + */ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* the optimization warning string template */ +#define OPTWARN "should optimize for %s with minfree %s %d%%" + +union { + struct fs sb; + char pad[MAXBSIZE]; +} sbun; +#define sblock sbun.sb + +int fi; +long dev_bsize = 1; + +void bwrite(daddr_t, char *, int); +int bread(daddr_t, char *, int); +void getsb(struct fs *, char *); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + char *cp, *special, *name; + struct stat st; + int i; + int Aflag = 0, Nflag = 0; + struct fstab *fs; + char *chg[2], device[MAXPATHLEN]; + + argc--, argv++; + if (argc < 2) + usage(); + special = argv[argc - 1]; + fs = getfsfile(special); + if (fs) + special = fs->fs_spec; +again: + if (stat(special, &st) < 0) { + if (*special != '/') { + if (*special == 'r') + special++; + (void)sprintf(device, "%s/%s", _PATH_DEV, special); + special = device; + goto again; + } + err(1, "%s", special); + } + if ((st.st_mode & S_IFMT) != S_IFBLK && + (st.st_mode & S_IFMT) != S_IFCHR) + errx(10, "%s: not a block or character device", special); + getsb(&sblock, special); + chg[FS_OPTSPACE] = "space"; + chg[FS_OPTTIME] = "time"; + for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + for (cp = &argv[0][1]; *cp; cp++) + switch (*cp) { + + case 'A': + Aflag++; + continue; + + case 'N': + Nflag++; + continue; + + case 'a': + name = "maximum contiguous block count"; + if (argc < 1) + errx(10, "-a: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 1) + errx(10, "%s must be >= 1 (was %s)", + name, *argv); + warnx("%s changes from %d to %d", + name, sblock.fs_maxcontig, i); + sblock.fs_maxcontig = i; + continue; + + case 'd': + name = + "rotational delay between contiguous blocks"; + if (argc < 1) + errx(10, "-d: missing %s", name); + argc--, argv++; + i = atoi(*argv); + warnx("%s changes from %dms to %dms", + name, sblock.fs_rotdelay, i); + sblock.fs_rotdelay = i; + continue; + + case 'e': + name = + "maximum blocks per file in a cylinder group"; + if (argc < 1) + errx(10, "-e: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 1) + errx(10, "%s must be >= 1 (was %s)", + name, *argv); + warnx("%s changes from %d to %d", + name, sblock.fs_maxbpg, i); + sblock.fs_maxbpg = i; + continue; + + case 'm': + name = "minimum percentage of free space"; + if (argc < 1) + errx(10, "-m: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 0 || i > 99) + errx(10, "bad %s (%s)", name, *argv); + warnx("%s changes from %d%% to %d%%", + name, sblock.fs_minfree, i); + sblock.fs_minfree = i; + if (i >= MINFREE && + sblock.fs_optim == FS_OPTSPACE) + warnx(OPTWARN, "time", ">=", MINFREE); + if (i < MINFREE && + sblock.fs_optim == FS_OPTTIME) + warnx(OPTWARN, "space", "<", MINFREE); + continue; + + case 'o': + name = "optimization preference"; + if (argc < 1) + errx(10, "-o: missing %s", name); + argc--, argv++; + if (strcmp(*argv, chg[FS_OPTSPACE]) == 0) + i = FS_OPTSPACE; + else if (strcmp(*argv, chg[FS_OPTTIME]) == 0) + i = FS_OPTTIME; + else + errx(10, "bad %s (options are `space' or `time')", + name); + if (sblock.fs_optim == i) { + warnx("%s remains unchanged as %s", + name, chg[i]); + continue; + } + warnx("%s changes from %s to %s", + name, chg[sblock.fs_optim], chg[i]); + sblock.fs_optim = i; + if (sblock.fs_minfree >= MINFREE && + i == FS_OPTSPACE) + warnx(OPTWARN, "time", ">=", MINFREE); + if (sblock.fs_minfree < MINFREE && + i == FS_OPTTIME) + warnx(OPTWARN, "space", "<", MINFREE); + continue; + + case 't': + name = "track skew in sectors"; + if (argc < 1) + errx(10, "-t: missing %s", name); + argc--, argv++; + i = atoi(*argv); + if (i < 0) + errx(10, "%s: %s must be >= 0", + *argv, name); + warnx("%s changes from %d to %d", + name, sblock.fs_trackskew, i); + sblock.fs_trackskew = i; + continue; + + default: + usage(); + } + } + if (argc != 1) + usage(); + if (Nflag) { + fprintf(stdout, "tunefs: current settings\n"); + fprintf(stdout, "\tmaximum contiguous block count %d\n", + sblock.fs_maxcontig); + fprintf(stdout, + "\trotational delay between contiguous blocks %dms\n", + sblock.fs_rotdelay); + fprintf(stdout, + "\tmaximum blocks per file in a cylinder group %d\n", + sblock.fs_maxbpg); + fprintf(stdout, "\tminimum percentage of free space %d%%\n", + sblock.fs_minfree); + fprintf(stdout, "\toptimization preference: %s\n", + chg[sblock.fs_optim]); + fprintf(stdout, "\ttrack skew %d sectors\n", + sblock.fs_trackskew); + fprintf(stdout, "tunefs: no changes made\n"); + exit(0); + } + fi = open(special, 1); + if (fi < 0) + err(3, "cannot open %s for writing", special); + bwrite((daddr_t)SBOFF / dev_bsize, (char *)&sblock, SBSIZE); + if (Aflag) + for (i = 0; i < sblock.fs_ncg; i++) + bwrite(fsbtodb(&sblock, cgsblock(&sblock, i)), + (char *)&sblock, SBSIZE); + close(fi); + exit(0); +} + +void +usage() +{ + + fprintf(stderr, "Usage: tunefs [-AN] tuneup-options special-device\n"); + fprintf(stderr, "where tuneup-options are:\n"); + fprintf(stderr, "\t-d rotational delay between contiguous blocks\n"); + fprintf(stderr, "\t-a maximum contiguous blocks\n"); + fprintf(stderr, "\t-e maximum blocks per file in a cylinder group\n"); + fprintf(stderr, "\t-m minimum percentage of free space\n"); + fprintf(stderr, "\t-o optimization preference (`space' or `time')\n"); + fprintf(stderr, "\t-t track skew in sectors\n"); + exit(2); +} + +void +getsb(fs, file) + register struct fs *fs; + char *file; +{ + + fi = open(file, 0); + if (fi < 0) + err(3, "cannot open %s for reading", file); + if (bread((daddr_t)SBOFF, (char *)fs, SBSIZE)) + err(4, "%s: bad super block", file); + if (fs->fs_magic != FS_MAGIC) + err(5, "%s: bad magic number", file); + dev_bsize = fs->fs_fsize / fsbtodb(fs, 1); + close(fi); +} + +void +bwrite(blk, buf, size) + daddr_t blk; + char *buf; + int size; +{ + + if (lseek(fi, (off_t)blk * dev_bsize, SEEK_SET) < 0) + err(6, "FS SEEK"); + if (write(fi, buf, size) != size) + err(7, "FS WRITE"); +} + +int +bread(bno, buf, cnt) + daddr_t bno; + char *buf; + int cnt; +{ + int i; + + if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0) + return(1); + if ((i = read(fi, buf, cnt)) != cnt) { + for(i=0; i