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