xref: /titanic_52/usr/src/cmd/prtvtoc/prtvtoc.c (revision 6185db853e024a486ff8837e6784dd290d866112)
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