xref: /titanic_50/usr/src/cmd/prtvtoc/prtvtoc.c (revision 59ac0c1669407488b67ae9e273667a340dccc611)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*	Copyright (c) 1984 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 /*
31  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
32  * Use is subject to license terms.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  * Print a disk partition map (volume table of contents, or VTOC).
39  */
40 
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <limits.h>
48 
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/dkio.h>
52 #include <sys/vtoc.h>
53 #include <sys/mnttab.h>
54 #include <sys/vfstab.h>
55 #include <sys/mkdev.h>
56 
57 #include <sys/efi_partition.h>
58 /*
59  * Assumes V_NUMPAR must be a power of 2.
60  *
61  * for V_NUMPAR = 8, we have
62  * 	parttn(x)=(x & 0x07)	noparttn(x)=(x & 0x3fff8)
63  *
64  * for V_NUMPAR = 16, we have
65  * 	parttn(x)=(x & 0x0f)	noparttn(x)=(x & 0x3fff0)
66  */
67 #define	parttn(x)	(x % V_NUMPAR)
68 #define	noparttn(x)	(x & (MAXMIN & ~(V_NUMPAR-1)))
69 
70 /*
71  * Disk freespace structure.
72  */
73 typedef struct {
74 	u_longlong_t	fr_start;	/* Start of free space */
75 	u_longlong_t	fr_size;	/* Length of free space */
76 } freemap_t;
77 
78 static	freemap_t	*findfree(struct dk_geom *, struct vtoc *);
79 static	int	partcmp(const void *, const void *);
80 static	int	partcmp64(const void *, const void *);
81 static	int	prtvtoc(char *);
82 static	void	putfree(struct vtoc *, freemap_t *);
83 static	void	putfree64(struct dk_gpt *, freemap_t *);
84 static	void	puttable(struct dk_geom *, struct vtoc *, freemap_t *,
85 			char *, char **);
86 static	void	puttable64(struct dk_gpt *, freemap_t *,
87 			char *, char **);
88 static	int	readgeom(int, char *, struct dk_geom *);
89 static	int	readvtoc(int, char *, struct vtoc *);
90 static	int	readefi(int, char *, struct dk_gpt **);
91 static	void	usage(void);
92 static	int	warn(char *, char *);
93 static	char	*safe_strdup(char *);
94 
95 /*
96  * External variables.
97  */
98 extern char	*getfullrawname();
99 /*
100  * Static variables.
101  */
102 static short	fflag;			/* Print freespace shell assignments */
103 static short	hflag;			/* Omit headers */
104 static short	sflag;			/* Omit all but the column header */
105 static char	*fstab = VFSTAB;	/* Fstab pathname */
106 static char	*mnttab = MNTTAB;	/* mnttab pathname */
107 static char	*progname;		/* Last qualifier of arg0 */
108 
109 int
110 main(int ac, char **av)
111 {
112 	int		idx;
113 
114 	if (progname = strrchr(av[0], '/'))
115 		++progname;
116 	else
117 		progname = av[0];
118 	while ((idx = getopt(ac, av, "fhst:m:")) != -1)
119 		switch (idx) {
120 		case 'f':
121 			++fflag;
122 			break;
123 		case 'h':
124 			++hflag;
125 			break;
126 		case 's':
127 			++sflag;
128 			break;
129 		case 't':
130 			fstab = optarg;
131 			break;
132 		case 'm':
133 			mnttab = optarg;
134 			break;
135 		default:
136 			usage();
137 		}
138 	if (optind >= ac)
139 		usage();
140 	idx = 0;
141 	while (optind < ac)
142 		idx |= prtvtoc(av[optind++]);
143 	return (idx == 0 ? 0 : 1);
144 }
145 
146 static freemap_t	*freemap;
147 /*
148  * findfree(): Find free space on a disk.
149  */
150 static freemap_t *
151 findfree(struct dk_geom *geom, struct vtoc *vtoc)
152 {
153 	struct partition	*part;
154 	struct partition	**list;
155 	freemap_t		*freeidx;
156 	ulong_t			fullsize;
157 	ulong_t			cylsize;
158 	struct partition	*sorted[V_NUMPAR + 1];
159 
160 	freemap = calloc(sizeof (freemap_t), V_NUMPAR + 1);
161 	cylsize  = (geom->dkg_nsect) * (geom->dkg_nhead);
162 	fullsize = (geom->dkg_ncyl) * cylsize;
163 	if (vtoc->v_nparts > V_NUMPAR) {
164 		(void) warn("putfree()", "Too many partitions on disk!");
165 		exit(1);
166 	}
167 	list = sorted;
168 	for (part = vtoc->v_part; part < vtoc->v_part + vtoc->v_nparts; ++part)
169 		if (part->p_size && part->p_tag != V_BACKUP)
170 			*list++ = part;
171 	*list = 0;
172 	qsort((char *)sorted, (uint_t)(list - sorted),
173 		sizeof (*sorted), partcmp);
174 	freeidx = freemap;
175 	freeidx->fr_start = 0;
176 	for (list = sorted; (part = *list) != NULL; ++list)
177 		if (part->p_start <= freeidx->fr_start)
178 			freeidx->fr_start += part->p_size;
179 		else {
180 			freeidx->fr_size = part->p_start - freeidx->fr_start;
181 			(++freeidx)->fr_start = part->p_start + part->p_size;
182 		}
183 	if (freeidx->fr_start < fullsize) {
184 		freeidx->fr_size = fullsize - freeidx->fr_start;
185 		++freeidx;
186 	}
187 	freeidx->fr_start = freeidx->fr_size = 0;
188 	return (freemap);
189 }
190 
191 /*
192  * findfree64(): Find free space on a disk.
193  */
194 static freemap_t *
195 findfree64(struct dk_gpt *efi)
196 {
197 	struct dk_part		*part;
198 	struct dk_part		**list;
199 	freemap_t		*freeidx;
200 	diskaddr_t		fullsize;
201 	struct dk_part		**sorted;
202 
203 	freemap = calloc(sizeof (freemap_t), efi->efi_nparts + 1);
204 	sorted = calloc(sizeof (struct dk_part), efi->efi_nparts + 1);
205 	fullsize = efi->efi_last_u_lba;
206 	list = sorted;
207 	for (part = efi->efi_parts;
208 	    part < efi->efi_parts + efi->efi_nparts;
209 	    ++part)
210 		if (part->p_size && part->p_tag != V_BACKUP)
211 			*list++ = part;
212 	*list = 0;
213 	qsort((char *)sorted, (uint_t)(list - sorted),
214 		sizeof (*sorted), partcmp64);
215 	freeidx = freemap;
216 	freeidx->fr_start = efi->efi_first_u_lba;
217 	for (list = sorted; (part = *list) != NULL; ++list)
218 		if (part->p_start == freeidx->fr_start)
219 			freeidx->fr_start += part->p_size;
220 		else {
221 			freeidx->fr_size = part->p_start - freeidx->fr_start;
222 			(++freeidx)->fr_start = part->p_start + part->p_size;
223 		}
224 	if (freeidx->fr_start < fullsize) {
225 		freeidx->fr_size = fullsize - freeidx->fr_start;
226 		++freeidx;
227 	}
228 	freeidx->fr_start = freeidx->fr_size = 0;
229 	return (freemap);
230 }
231 
232 /*
233  * getmntpt()
234  *
235  * Get the filesystem mountpoint of each partition on the disk
236  * from the fstab or mnttab. Returns a pointer to an array of pointers to
237  * directory names (indexed by partition number).
238  */
239 static char **
240 getmntpt(major_t slot, minor_t nopartminor)
241 {
242 	int idx;
243 	FILE *file;
244 	char devbuf[PATH_MAX], *item;
245 	static char *list[V_NUMPAR];
246 	struct stat sb;
247 	struct mnttab mtab;
248 	struct vfstab vtab;
249 
250 	for (idx = 0; idx < V_NUMPAR; ++idx)
251 		list[idx] = NULL;
252 
253 	/* read mnttab for partition mountpoints */
254 	if ((file = fopen(mnttab, "r")) == NULL) {
255 		(void) warn(mnttab, strerror(errno));
256 	} else {
257 		while (getmntent(file, &mtab) == 0) {
258 			item = mtab.mnt_special;
259 			if ((item == NULL) || (mtab.mnt_mountp == NULL))
260 				continue;
261 
262 			/*
263 			 * Is it from /dev?
264 			 */
265 			if (strncmp(item, "/dev/", strlen("/dev/") != 0))
266 				continue;
267 
268 			/*
269 			 * Is it a character device?
270 			 */
271 			(void) snprintf(devbuf, sizeof (devbuf), "/dev/r%s",
272 			    item + strlen("/dev/"));
273 
274 			if ((stat(devbuf, &sb) != 0) ||
275 			    ((sb.st_mode & S_IFMT) != S_IFCHR))
276 				continue;
277 
278 			/*
279 			 * device must match input slot and nopartminor
280 			 */
281 			if ((major(sb.st_rdev) != slot) ||
282 			    (noparttn(minor(sb.st_rdev)) != nopartminor))
283 				continue;
284 
285 			list[parttn(minor(sb.st_rdev))] =
286 			    safe_strdup(mtab.mnt_mountp);
287 		}
288 		(void) fclose(file);
289 	}
290 
291 	if ((file = fopen(fstab, "r")) == NULL) {
292 		(void) warn(fstab, strerror(errno));
293 		return (list);
294 	}
295 
296 	/*
297 	 * Look for the disk in the vfstab so that we can report its mount
298 	 * point even if it isn't currently mounted.
299 	 */
300 	while (getvfsent(file, &vtab) == 0) {
301 		item = vtab.vfs_special;
302 		if ((item == NULL) || (vtab.vfs_mountp == NULL))
303 			continue;
304 
305 		if (strncmp(item, "/dev/", strlen("/dev/")) != 0)
306 			continue;
307 
308 		/*
309 		 * Is it a character device?
310 		 */
311 		(void) snprintf(devbuf, sizeof (devbuf), "/dev/r%s",
312 		    item + strlen("/dev/"));
313 
314 		if ((stat(devbuf, &sb) != 0) ||
315 		    ((sb.st_mode & S_IFMT) != S_IFCHR))
316 			continue;
317 
318 		/*
319 		 * device must match input slot and nopartminor
320 		 */
321 		if ((major(sb.st_rdev) != slot) ||
322 		    (noparttn(minor(sb.st_rdev)) != nopartminor))
323 			continue;
324 
325 		/*
326 		 * use mnttab entry if both tables have entries
327 		 */
328 		if (list[parttn(minor(sb.st_rdev))] != NULL)
329 			continue;
330 
331 		list[parttn(minor(sb.st_rdev))] = safe_strdup(vtab.vfs_mountp);
332 	}
333 	(void) fclose(file);
334 
335 	return (list);
336 }
337 
338 /*
339  * partcmp(): Qsort() key comparison of partitions by starting sector numbers.
340  */
341 static int
342 partcmp(const void *one, const void *two)
343 {
344 	return ((*(struct partition **)one)->p_start -
345 		(*(struct partition **)two)->p_start);
346 }
347 
348 static int
349 partcmp64(const void *one, const void *two)
350 {
351 	if ((*(struct dk_part **)one)->p_start >
352 		(*(struct dk_part **)two)->p_start)
353 	    return (1);
354 	else if ((*(struct dk_part **)one)->p_start <
355 		(*(struct dk_part **)two)->p_start)
356 	    return (-1);
357 	else
358 	    return (0);
359 
360 }
361 
362 /*
363  * prtvtoc(): Read and print a VTOC.
364  */
365 static int
366 prtvtoc(char *devname)
367 {
368 	int		fd;
369 	int		idx;
370 	freemap_t	*freemap;
371 	struct stat	sb;
372 	struct vtoc	vtoc;
373 	int		geo;
374 	struct dk_geom	geom;
375 	char		*name;
376 	int		newvtoc = 0;
377 	struct dk_gpt	*efi;
378 
379 	name = getfullrawname(devname);
380 	if (name == NULL)
381 		return (warn(devname,
382 		    "internal administrative call (getfullrawname) failed"));
383 	if (strcmp(name, "") == 0)
384 		name = devname;
385 	if ((fd = open(name, O_NONBLOCK|O_RDONLY)) < 0)
386 		return (warn(name, strerror(errno)));
387 	if (fstat(fd, &sb) < 0)
388 		return (warn(name, strerror(errno)));
389 	if ((sb.st_mode & S_IFMT) != S_IFCHR)
390 		return (warn(name, "Not a raw device"));
391 
392 	geo = (readgeom(fd, name, &geom) == 0);
393 	if (geo) {
394 		if ((idx = readvtoc(fd, name, &vtoc)) == VT_ENOTSUP) {
395 			idx = (readefi(fd, name, &efi) == 0);
396 			newvtoc = 1;
397 		} else
398 			idx = (idx == 0);
399 	}
400 	(void) close(fd);
401 	if ((!geo) || (!idx))
402 		return (-1);
403 	if (!newvtoc)
404 		freemap = findfree(&geom, &vtoc);
405 	else
406 		freemap = findfree64(efi);
407 	if (fflag) {
408 		if (!newvtoc)
409 			putfree(&vtoc, freemap);
410 		else
411 			putfree64(efi, freemap);
412 	} else {
413 		if (!newvtoc)
414 			puttable(&geom, &vtoc, freemap, devname,
415 			    getmntpt(major(sb.st_rdev),
416 			    noparttn(minor(sb.st_rdev))));
417 		else
418 			puttable64(efi, freemap, devname,
419 			    getmntpt(major(sb.st_rdev),
420 			    noparttn(minor(sb.st_rdev))));
421 	}
422 	if (newvtoc)
423 		efi_free(efi);
424 	return (0);
425 }
426 
427 /*
428  * putfree():
429  *
430  * Print shell assignments for disk free space. FREE_START and FREE_SIZE
431  * represent the starting block and number of blocks of the first chunk
432  * of free space. FREE_PART lists the unassigned partitions.
433  */
434 static void
435 putfree(struct vtoc *vtoc, freemap_t *freemap)
436 {
437 	freemap_t *freeidx;
438 	ushort_t idx;
439 	int free_count = 0;
440 
441 	for (freeidx = freemap; freeidx->fr_size; ++freeidx)
442 		free_count++;
443 
444 	(void) printf("FREE_START=%llu FREE_SIZE=%llu FREE_COUNT=%d FREE_PART=",
445 	    freemap->fr_start, freemap->fr_size, free_count);
446 
447 	for (idx = 0; idx < vtoc->v_nparts; ++idx) {
448 		if (vtoc->v_part[idx].p_size == 0 && idx != 2)
449 			(void) printf("%x", idx);
450 	}
451 	(void) printf("\n");
452 }
453 
454 static void
455 putfree64(struct dk_gpt *efi, freemap_t *freemap)
456 {
457 	freemap_t *freeidx;
458 	ushort_t idx;
459 	int free_count = 0;
460 
461 	for (freeidx = freemap; freeidx->fr_size; ++freeidx)
462 		free_count++;
463 
464 	(void) printf("FREE_START=%llu FREE_SIZE=%llu FREE_COUNT=%d FREE_PART=",
465 	    freemap->fr_start, freemap->fr_size, free_count);
466 
467 	for (idx = 0; idx < efi->efi_nparts; ++idx) {
468 		if (efi->efi_parts[idx].p_size == 0 && idx != 2)
469 			(void) printf("%x", idx);
470 	}
471 	(void) printf("\n");
472 }
473 
474 /*
475  * puttable(): Print a human-readable VTOC.
476  */
477 static void
478 puttable(struct dk_geom *geom, struct vtoc *vtoc, freemap_t *freemap,
479     char *name, char **mtab)
480 {
481 	ushort_t	idx;
482 	ulong_t	cylsize;
483 
484 	cylsize = (geom->dkg_nsect) * (geom->dkg_nhead);
485 	if (!hflag && !sflag) {
486 		(void) printf("* %s", name);
487 		if (*vtoc->v_volume)
488 			(void) printf(" (volume \"%.8s\")", vtoc->v_volume);
489 
490 		(void) printf(" partition map\n");
491 		(void) printf("*\n* Dimensions:\n");
492 		(void) printf("* %7u bytes/sector\n", vtoc->v_sectorsz);
493 		(void) printf("* %7u sectors/track\n", geom->dkg_nsect);
494 		(void) printf("* %7u tracks/cylinder\n", geom->dkg_nhead);
495 		(void) printf("* %7lu sectors/cylinder\n", cylsize);
496 		(void) printf("* %7u cylinders\n", geom->dkg_pcyl);
497 		(void) printf("* %7u accessible cylinders\n", geom->dkg_ncyl);
498 		(void) printf("*\n* Flags:\n");
499 		(void) printf("*   1: unmountable\n");
500 		(void) printf("*  10: read-only\n*\n");
501 
502 		if (freemap->fr_size) {
503 			(void) printf("* Unallocated space:\n");
504 			(void) printf("*\tFirst     Sector    Last\n");
505 			(void) printf("*\tSector     Count    Sector \n");
506 			do {
507 				(void) printf("*   %9llu %9llu %9llu\n",
508 				    freemap->fr_start, freemap->fr_size,
509 				    freemap->fr_size + freemap->fr_start - 1);
510 			} while ((++freemap)->fr_size);
511 			(void) printf("*\n");
512 		}
513 	}
514 	if (!hflag)  {
515 		(void) printf(\
516 "*                          First     Sector    Last\n"
517 "* Partition  Tag  Flags    Sector     Count    Sector  Mount Directory\n");
518 	}
519 	for (idx = 0; idx < vtoc->v_nparts; ++idx) {
520 		if (vtoc->v_part[idx].p_size == 0)
521 			continue;
522 		(void) printf("      %2u  %5u    %02x  %9lu %9lu %9lu",
523 		    idx, vtoc->v_part[idx].p_tag, vtoc->v_part[idx].p_flag,
524 		    vtoc->v_part[idx].p_start, vtoc->v_part[idx].p_size,
525 		    vtoc->v_part[idx].p_start + vtoc->v_part[idx].p_size - 1);
526 		if (mtab && mtab[idx])
527 			(void) printf("   %s", mtab[idx]);
528 		(void) printf("\n");
529 	}
530 }
531 
532 /*
533  * puttable(): Print a human-readable VTOC.
534  */
535 static void
536 puttable64(struct dk_gpt *efi, freemap_t *freemap, char *name,
537 	char **mtab)
538 {
539 	ushort_t	idx;
540 
541 	if (!hflag && !sflag) {
542 		(void) printf("* %s", name);
543 		for (idx = 0; idx < efi->efi_nparts; idx++)
544 		    if (efi->efi_parts[idx].p_tag == V_RESERVED &&
545 			*efi->efi_parts[idx].p_name)
546 			    (void) printf(" (volume \"%.8s\")",
547 				    efi->efi_parts[idx].p_name);
548 		(void) printf(" partition map\n");
549 		(void) printf("*\n* Dimensions:\n");
550 		(void) printf("* %7u bytes/sector\n", efi->efi_lbasize);
551 		(void) printf("* %llu sectors\n", efi->efi_last_lba + 1);
552 		(void) printf("* %llu accessible sectors\n",
553 		    efi->efi_last_u_lba - efi->efi_first_u_lba + 1);
554 		(void) printf("*\n* Flags:\n");
555 		(void) printf("*   1: unmountable\n");
556 		(void) printf("*  10: read-only\n*\n");
557 
558 		if (freemap->fr_size) {
559 			(void) printf("* Unallocated space:\n");
560 			(void) printf("*\tFirst     Sector    Last\n");
561 			(void) printf("*\tSector     Count    Sector \n");
562 			do {
563 				(void) printf("*   %9llu %9llu %9llu\n",
564 				    freemap->fr_start, freemap->fr_size,
565 				    freemap->fr_size + freemap->fr_start - 1);
566 			} while ((++freemap)->fr_size);
567 			(void) printf("*\n");
568 		}
569 	}
570 	if (!hflag)  {
571 		(void) printf(\
572 "*                          First     Sector    Last\n"
573 "* Partition  Tag  Flags    Sector     Count    Sector  Mount Directory\n");
574 	}
575 	for (idx = 0; idx < efi->efi_nparts; ++idx) {
576 	    if (efi->efi_parts[idx].p_size == 0)
577 		    continue;
578 	    (void) printf("      %2u  %5u    %02x  %9llu %9llu %9llu",
579 		idx, efi->efi_parts[idx].p_tag, efi->efi_parts[idx].p_flag,
580 		efi->efi_parts[idx].p_start, efi->efi_parts[idx].p_size,
581 		efi->efi_parts[idx].p_start + efi->efi_parts[idx].p_size - 1);
582 	    if ((idx < 7) && mtab && mtab[idx])
583 		    (void) printf("   %s", mtab[idx]);
584 	    (void) printf("\n");
585 	}
586 }
587 
588 /*
589  * readgeom(): Read the disk geometry.
590  */
591 static int
592 readgeom(int fd, char *name, struct dk_geom *geom)
593 {
594 	char err_string[128];
595 
596 	if ((ioctl(fd, DKIOCGGEOM, geom) < 0) && (errno != ENOTSUP)) {
597 		(void) sprintf(err_string,
598 		    "Unable to read Disk geometry errno = 0x%x",
599 		    errno);
600 		return (warn(name, err_string));
601 	} else if (errno == ENOTSUP) {
602 		(void) memset(geom, 0, sizeof (struct dk_geom));
603 	}
604 	return (0);
605 }
606 
607 /*
608  * readvtoc(): Read a partition map.
609  */
610 static int
611 readvtoc(int fd, char *name, struct vtoc *vtoc)
612 {
613 	int	retval;
614 
615 	if ((retval = read_vtoc(fd, vtoc)) >= 0)
616 		return (0);
617 
618 	switch (retval) {
619 	case (VT_EIO):
620 		return (warn(name, "Unable to read VTOC"));
621 	case (VT_EINVAL):
622 		return (warn(name, "Invalid VTOC"));
623 	case (VT_ERROR):
624 		return (warn(name, "Unknown problem reading VTOC"));
625 	}
626 	return (retval);
627 }
628 
629 /*
630  * readefi(): Read a partition map.
631  */
632 static int
633 readefi(int fd, char *name, struct dk_gpt **efi)
634 {
635 	int	retval;
636 
637 	if ((retval = efi_alloc_and_read(fd, efi)) >= 0)
638 		return (0);
639 
640 	switch (retval) {
641 	case (VT_EIO):
642 		return (warn(name, "Unable to read VTOC"));
643 	case (VT_EINVAL):
644 		return (warn(name, "Invalid VTOC"));
645 	case (VT_ERROR):
646 		return (warn(name, "Unknown problem reading VTOC"));
647 	}
648 	return (retval);
649 }
650 
651 static char *
652 safe_strdup(char *str)
653 {
654 	char *ret;
655 	if ((ret = strdup(str)) == NULL) {
656 		(void) warn("memory allocation", strerror(errno));
657 		exit(1);
658 	}
659 	return (ret);
660 }
661 
662 /*
663  * usage(): Print a helpful message and exit.
664  */
665 static void
666 usage()
667 {
668 	(void) fprintf(stderr, "Usage:\t%s [ -fhs ] [ -t fstab ] [ -m mnttab ] "
669 	    "rawdisk ...\n", progname);
670 	exit(1);
671 }
672 
673 /*
674  * warn(): Print an error message. Always returns -1.
675  */
676 static int
677 warn(char *what, char *why)
678 {
679 	(void) fprintf(stderr, "%s: %s: %s\n", progname, what, why);
680 	return (-1);
681 }
682