Do some fairly major include file cleanups to further separate kernelland
[dragonfly.git] / sbin / vinum / list.c
1 /*      list.c: vinum interface program, list routines
2  */
3 /*-
4  * Copyright (c) 1997, 1998
5  *      Nan Yang Computer Services Limited.  All rights reserved.
6  *
7  *  Parts copyright (c) 1997, 1998 Cybernet Corporation, NetMAX project.
8  *
9  *  Written by Greg Lehey
10  *
11  *  This software is distributed under the so-called ``Berkeley
12  *  License'':
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. All advertising materials mentioning features or use of this software
23  *    must display the following acknowledgement:
24  *      This product includes software developed by Nan Yang Computer
25  *      Services Limited.
26  * 4. Neither the name of the Company nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * This software is provided ``as is'', and any express or implied
31  * warranties, including, but not limited to, the implied warranties of
32  * merchantability and fitness for a particular purpose are disclaimed.
33  * In no event shall the company or contributors be liable for any
34  * direct, indirect, incidental, special, exemplary, or consequential
35  * damages (including, but not limited to, procurement of substitute
36  * goods or services; loss of use, data, or profits; or business
37  * interruption) however caused and on any theory of liability, whether
38  * in contract, strict liability, or tort (including negligence or
39  * otherwise) arising in any way out of the use of this software, even if
40  * advised of the possibility of such damage.
41  *
42  * $Id: list.c,v 1.25 2000/12/20 03:38:43 grog Exp grog $
43  * $FreeBSD: src/sbin/vinum/list.c,v 1.25.2.4 2001/05/28 05:58:04 grog Exp $
44  * $DragonFly: src/sbin/vinum/list.c,v 1.4 2003/11/21 22:46:13 dillon Exp $
45  */
46
47 #define _KERNEL_STRUCTURES
48
49 #include <ctype.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <sys/mman.h>
53 #include <netdb.h>
54 #include <setjmp.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <sys/ioctl.h>
61 #include <sys/utsname.h>
62 #include <dev/raid/vinum/vinumhdr.h>
63 #include "vext.h"
64 #include <dev/raid/vinum/request.h>
65 #include <devstat.h>
66
67 /*
68  * When a subdisk is reviving or initializing, we
69  * check to see whether it is still progressing
70  * and print a warning if not.  We check every 50
71  * ms, up to a maximum of 5 seconds.  This is the
72  * counter value.
73  */
74 #define STALLCOUNT      100
75
76 /*
77  * Take a size in sectors and return a pointer to
78  * a string which represents the size best.  If lj
79  * is != 0, return left justified, otherwise in a
80  * fixed 10 character field suitable for columnar
81  * printing.
82  *
83  * Note this uses a static string: it's only
84  * intended to be used immediately for printing.
85  */
86 char *
87 roughlength(int64_t bytes, int lj)
88 {
89     static char description[16];
90
91     if (bytes > (int64_t) MEGABYTE * 10000)                 /* gigabytes */
92         sprintf(description, lj ? "%lld GB" : "%10d GB", bytes / GIGABYTE);
93     else if (bytes > KILOBYTE * 10000)                      /* megabytes */
94         sprintf(description, lj ? "%lld MB" : "%10d MB", bytes / MEGABYTE);
95     else if (bytes > 10000)                                 /* kilobytes */
96         sprintf(description, lj ? "%lld kB" : "%10d kB", bytes / KILOBYTE);
97     else                                                    /* bytes */
98         sprintf(description, lj ? "%lld  B" : "%10d  B", bytes);
99     return description;
100 }
101
102 void
103 vinum_list(int argc, char *argv[], char *argv0[])
104 {
105     int object;
106     int i;
107     enum objecttype type;
108
109     if (sflag & (!vflag))                                   /* just summary stats, */
110         printf("Object\t\t  Reads\t\tBytes\tAverage\tRecover\t Writes"
111             "\t\tBytes\tAverage\t  Mblock  Mstripe\n\n");
112     if (argc == 0)
113         listconfig();                                       /* list everything */
114     else {
115         for (i = 0; i < argc; i++) {
116             object = find_object(argv[i], &type);           /* look for it */
117             if (vinum_li(object, type))
118                 fprintf(stderr, "Can't find object: %s\n", argv[i]);
119         }
120     }
121 }
122
123 /* List an object */
124 int
125 vinum_li(int object, enum objecttype type)
126 {
127     switch (type) {
128     case drive_object:
129         vinum_ldi(object, recurse);
130         break;
131
132     case sd_object:
133         vinum_lsi(object, recurse);
134         break;
135
136     case plex_object:
137         vinum_lpi(object, recurse);
138         break;
139
140     case volume_object:
141         vinum_lvi(object, recurse);
142         break;
143
144     default:
145         return -1;
146     }
147     return 0;
148 }
149
150 void
151 vinum_ldi(int driveno, int recurse)
152 {
153     time_t t;                                               /* because Bruce says so */
154     int sdno;                                               /* for recursion */
155
156     get_drive_info(&drive, driveno);
157     if (drive.state != drive_unallocated) {
158         if (vflag) {
159             printf("Drive %s:\tDevice %s\n",
160                 drive.label.name,
161                 drive.devicename);
162             t = drive.label.date_of_birth.tv_sec;
163             printf("\t\tCreated on %s at %s",
164                 drive.label.sysname,
165                 ctime(&t));
166             t = drive.label.last_update.tv_sec;
167             printf("\t\tConfig last updated %s",            /* care: \n at end */
168                 ctime(&t));
169             printf("\t\tSize: %16lld bytes (%lld MB)\n\t\tUsed: %16lld bytes (%lld MB)\n"
170                 "\t\tAvailable: %11qd bytes (%d MB)\n",
171                 (long long) drive.label.drive_size,         /* bytes used */
172                 (long long) (drive.label.drive_size / MEGABYTE),
173                 (long long) (drive.label.drive_size - drive.sectors_available
174                     * DEV_BSIZE),
175                 (long long) (drive.label.drive_size - drive.sectors_available
176                     * DEV_BSIZE) / MEGABYTE,
177                 (long long) drive.sectors_available * DEV_BSIZE,
178                 (int) (drive.sectors_available * DEV_BSIZE / MEGABYTE));
179             printf("\t\tState: %s\n", drive_state(drive.state));
180             if (drive.lasterror != 0)
181                 printf("\t\tLast error: %s\n", strerror(drive.lasterror));
182             else
183                 printf("\t\tLast error: none\n");
184             printf("\t\tActive requests:\t%d\n\t\tMaximum active:\t\t%d\n",
185                 drive.active,
186                 drive.maxactive);
187             if (Verbose) {                                  /* print the free list */
188                 int fe;                                     /* freelist entry */
189                 struct drive_freelist freelist;
190                 struct ferq {                               /* request to pass to ioctl */
191                     int driveno;
192                     int fe;
193                 } *ferq = (struct ferq *) &freelist;
194
195                 printf("\t\tFree list contains %d entries:\n\t\t   Offset\t     Size\n",
196                     drive.freelist_entries);
197                 for (fe = 0; fe < drive.freelist_entries; fe++) {
198                     ferq->driveno = drive.driveno;
199                     ferq->fe = fe;
200                     if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) {
201                         fprintf(stderr,
202                             "Can't get free list element %d: %s\n",
203                             fe,
204                             strerror(errno));
205                         longjmp(command_fail, -1);
206                     }
207                     printf("\t\t%9lld\t%9lld\n",
208                         (long long) freelist.offset,
209                         (long long) freelist.sectors);
210                 }
211             }
212         } else if (!sflag) {
213             printf("D %-21s State: %s\tDevice %s\tAvail: %lld/%lld MB",
214                 drive.label.name,
215                 drive_state(drive.state),
216                 drive.devicename,
217                 (long long) drive.sectors_available * DEV_BSIZE / MEGABYTE,
218                 (long long) (drive.label.drive_size / MEGABYTE));
219             if (drive.label.drive_size != 0)
220                 printf(" (%d%%)",
221                     (int) ((drive.sectors_available * 100 * DEV_BSIZE)
222                         / (drive.label.drive_size - (DATASTART * DEV_BSIZE))));
223         }
224         if (sflag) {
225             if (vflag || Verbose) {
226                 printf("\t\tReads:  \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
227                     (long long) drive.reads,
228                     (long long) drive.bytes_read,
229                     roughlength(drive.bytes_read, 1));
230                 if (drive.reads != 0)
231                     printf("\t\tAverage read:\t%16lld bytes\n",
232                         (long long) drive.bytes_read / drive.reads);
233                 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
234                     (long long) drive.writes,
235                     (long long) drive.bytes_written,
236                     roughlength(drive.bytes_written, 1));
237                 if (drive.writes != 0)
238                     printf("\t\tAverage write:\t%16lld bytes\n",
239                         (long long) (drive.bytes_written / drive.writes));
240             } else {                                        /* non-verbose stats */
241                 printf("%-15s\t%7lld\t%15lld\t",
242                     drive.label.name,
243                     (long long) drive.reads,
244                     (long long) drive.bytes_read);
245                 if (drive.reads != 0)
246                     printf("%7lld\t\t",
247                         (long long) (drive.bytes_read / drive.reads));
248                 else
249                     printf("\t\t");
250                 printf("%7lld\t%15lld\t",
251                     (long long) drive.writes,
252                     (long long) drive.bytes_written);
253                 if (drive.writes != 0)
254                     printf("%7lld",
255                         (long long) (drive.bytes_written / drive.writes));
256             }
257         }
258         if (recurse) {
259             printf("\n");
260             for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++) {
261                 get_sd_info(&sd, sdno);
262                 if ((sd.state != sd_unallocated)
263                     && (sd.driveno == drive.driveno))
264                     vinum_lsi(sd.sdno, 0);
265             }
266         }
267         printf("\n");
268     }
269 }
270
271 void
272 vinum_ld(int argc, char *argv[], char *argv0[])
273 {
274     int i;
275     int driveno;
276     enum objecttype type;
277
278     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
279         perror("Can't get vinum config");
280         return;
281     }
282     if (argc == 0) {
283         for (driveno = 0; driveno < vinum_conf.drives_allocated; driveno++)
284             vinum_ldi(driveno, recurse);
285     } else {
286         for (i = 0; i < argc; i++) {
287             driveno = find_object(argv[i], &type);
288             if (type == drive_object)
289                 vinum_ldi(driveno, recurse);
290             else
291                 fprintf(stderr, "%s is not a drive\n", argv[i]);
292         }
293     }
294 }
295
296 void
297 vinum_lvi(int volno, int recurse)
298 {
299     get_volume_info(&vol, volno);
300     if (vol.state != volume_unallocated) {
301         if (vflag) {
302             printf("Volume %s:\tSize: %lld bytes (%lld MB)\n"
303                 "\t\tState: %s\n\t\tFlags: %s%s%s\n",
304                 vol.name,
305                 ((long long) vol.size) * DEV_BSIZE,
306                 ((long long) vol.size) * DEV_BSIZE / MEGABYTE,
307                 volume_state(vol.state),
308                 vol.flags & VF_OPEN ? "open " : "",
309                 (vol.flags & VF_WRITETHROUGH ? "writethrough " : ""),
310                 (vol.flags & VF_RAW ? "raw" : ""));
311             printf("\t\t%d plexes\n\t\tRead policy: ", vol.plexes);
312             if (vol.preferred_plex < 0)                     /* round robin */
313                 printf("round robin\n");
314             else {
315                 get_plex_info(&plex, vol.plex[vol.preferred_plex]);
316                 printf("plex %d (%s)\n", vol.preferred_plex, plex.name);
317             }
318         } else if (!sflag)                                  /* brief */
319             printf("V %-21s State: %s\tPlexes: %7d\tSize: %s\n",
320                 vol.name,
321                 volume_state(vol.state),
322                 vol.plexes,
323                 roughlength(vol.size << DEV_BSHIFT, 0));
324         if (sflag) {
325             if (vflag || Verbose) {
326                 printf("\t\tReads:  \t%16lld\n\t\tRecovered:\t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
327                     (long long) vol.reads,
328                     (long long) vol.recovered_reads,
329                     (long long) vol.bytes_read,
330                     roughlength(vol.bytes_read, 1));
331                 if (vol.reads != 0)
332                     printf("\t\tAverage read:\t%16lld bytes\n",
333                         (long long) (vol.bytes_read / vol.reads));
334                 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
335                     (long long) vol.writes,
336                     (long long) vol.bytes_written,
337                     roughlength(vol.bytes_written, 1));
338                 if (vol.writes != 0)
339                     printf("\t\tAverage write:\t%16lld bytes\n",
340                         (long long) (vol.bytes_written / vol.writes));
341                 printf("\t\tActive requests:\t%8d\n", vol.active);
342             } else {                                        /* brief stats listing */
343                 printf("%-15s\t%7lld\t%15lld\t",
344                     vol.name,
345                     (long long) vol.reads,
346                     (long long) vol.bytes_read);
347                 if (vol.reads != 0)
348                     printf("%7lld\t",
349                         (long long) (vol.bytes_read / vol.reads));
350                 else
351                     printf("\t");
352                 printf("%7lld\t", (long long) vol.recovered_reads);
353                 printf("%7lld\t%15lld\t",
354                     (long long) vol.writes,
355                     vol.bytes_written);
356                 if (vol.writes != 0)
357                     printf("%7lld\n",
358                         (long long) (vol.bytes_written / vol.writes));
359                 else
360                     printf("\n");
361             }
362         }
363         if (vol.plexes > 0) {
364             int plexno;
365             if (Verbose) {                                  /* brief list */
366                 for (plexno = 0; plexno < vol.plexes; plexno++) {
367                     get_plex_info(&plex, vol.plex[plexno]);
368                                                             /* Just a brief summary here */
369                     printf("\t\tPlex %2d:\t%s\t(%s), %s\n",
370                         plexno,
371                         plex.name,
372                         plex_org(plex.organization),
373                         roughlength(plex.length << DEV_BSHIFT, 0));
374                 }
375             }
376             if (recurse) {
377                 for (plexno = 0; plexno < vol.plexes; plexno++)
378                     vinum_lpi(vol.plex[plexno], 0);         /* first show the plexes */
379                 for (plexno = 0; plexno < vol.plexes; plexno++) { /* then the subdisks */
380                     get_plex_info(&plex, vol.plex[plexno]);
381                     if (plex.subdisks > 0) {
382                         int sdno;
383
384                         for (sdno = 0; sdno < plex.subdisks; sdno++) {
385                             get_plex_sd_info(&sd, vol.plex[plexno], sdno);
386                             vinum_lsi(sd.sdno, 0);
387                         }
388                     }
389                 }
390                 printf("\n");
391             }
392         }
393     }
394 }
395
396 void
397 vinum_lv(int argc, char *argv[], char *argv0[])
398 {
399     int i;
400     int volno;
401     enum objecttype type;
402
403     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
404         perror("Can't get vinum config");
405         return;
406     }
407     if (argc == 0)
408         for (volno = 0; volno < vinum_conf.volumes_allocated; volno++)
409             vinum_lvi(volno, recurse);
410     else {
411         for (i = 0; i < argc; i++) {
412             volno = find_object(argv[i], &type);
413             if (type == volume_object)
414                 vinum_lvi(volno, recurse);
415             else
416                 fprintf(stderr, "%s is not a volume\n", argv[i]);
417         }
418     }
419 }
420
421 void
422 vinum_lpi(int plexno, int recurse)
423 {
424     get_plex_info(&plex, plexno);
425     if (plex.state != plex_unallocated) {
426         if (vflag) {
427             printf("Plex %s:\tSize:\t%9lld bytes (%lld MB)\n\t\tSubdisks: %8d\n",
428                 plex.name,
429                 (long long) plex.length * DEV_BSIZE,
430                 (long long) plex.length * DEV_BSIZE / MEGABYTE,
431                 plex.subdisks);
432             printf("\t\tState: %s\n\t\tOrganization: %s",
433                 plex_state(plex.state),
434                 plex_org(plex.organization));
435             if (isstriped((&plex)))
436                 printf("\tStripe size: %s\n", roughlength(plex.stripesize * DEV_BSIZE, 1));
437             else
438                 printf("\n");
439             if ((isparity((&plex)))
440                 && (plex.checkblock != 0))
441                 printf("\t\tCheck block pointer:\t\t%s (%d%%)\n",
442                     roughlength((plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1), 0),
443                     (int) (((u_int64_t) (plex.checkblock * 100)) * (plex.subdisks - 1) / plex.length));
444             if (plex.volno >= 0) {
445                 get_volume_info(&vol, plex.volno);
446                 printf("\t\tPart of volume %s\n", vol.name);
447             }
448         } else if (!sflag) {                                /* non-verbose list */
449             char *org = "";                                 /* organization */
450
451             switch (plex.organization) {
452             case plex_disorg:                               /* disorganized */
453                 org = "??";
454                 break;
455             case plex_concat:                               /* concatenated plex */
456                 org = "C";
457                 break;
458             case plex_striped:                              /* striped plex */
459                 org = "S";
460                 break;
461             case plex_raid4:                                /* RAID4 plex */
462                 org = "R4";
463                 break;
464             case plex_raid5:                                /* RAID5 plex */
465                 org = "R5";
466                 break;
467             }
468             printf("P %-18s %2s State: %s\tSubdisks: %5d\tSize: %s",
469                 plex.name,
470                 org,
471                 plex_state(plex.state),
472                 plex.subdisks,
473                 roughlength(plex.length << DEV_BSHIFT, 0));
474         }
475         if (sflag) {
476             if (vflag || Verbose) {
477                 printf("\t\tReads:  \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
478                     (long long) plex.reads,
479                     (long long) plex.bytes_read,
480                     roughlength(plex.bytes_read, 1));
481                 if (plex.reads != 0)
482                     printf("\t\tAverage read:\t%16lld bytes\n",
483                         (long long) (plex.bytes_read / plex.reads));
484                 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
485                     (long long) plex.writes,
486                     (long long) plex.bytes_written,
487                     roughlength(plex.bytes_written, 1));
488                 if (plex.writes != 0)
489                     printf("\t\tAverage write:\t%16lld bytes\n",
490                         (long long) (plex.bytes_written / plex.writes));
491                 if (((plex.reads + plex.writes) > 0)
492                     && isstriped((&plex)))
493                     printf("\t\tMultiblock:\t%16lld (%d%%)\n"
494                         "\t\tMultistripe:\t%16lld (%d%%)\n",
495                         (long long) plex.multiblock,
496                         (int) (plex.multiblock * 100 / (plex.reads + plex.writes)),
497                         (long long) plex.multistripe,
498                         (int) (plex.multistripe * 100 / (plex.reads + plex.writes)));
499                 if (plex.recovered_reads)
500                     printf("\t\tRecovered reads:%16lld\n",
501                         (long long) plex.recovered_reads);
502                 if (plex.degraded_writes)
503                     printf("\t\tDegraded writes:%16lld\n",
504                         (long long) plex.degraded_writes);
505                 if (plex.parityless_writes)
506                     printf("\t\tParityless writes:%14lld\n",
507                         (long long) plex.parityless_writes);
508             } else {
509                 printf("%-15s\t%7lld\t%15lld\t",
510                     plex.name,
511                     (long long) plex.reads,
512                     (long long) plex.bytes_read);
513                 if (plex.reads != 0)
514                     printf("%7lld\t",
515                         (long long) (plex.bytes_read / plex.reads));
516                 else
517                     printf("\t");
518                 printf("%7lld\t", (long long) plex.recovered_reads);
519                 printf("%7lld\t%15lld\t",
520                     (long long) plex.writes,
521                     (long long) plex.bytes_written);
522                 if (plex.writes != 0)
523                     printf("%7lld\t",
524                         (long long) (plex.bytes_written / plex.writes));
525                 else
526                     printf("\t");
527                 printf("%7lld\t%7lld\n",
528                     (long long) plex.multiblock,
529                     (long long) plex.multistripe);
530             }
531         }
532         if (plex.subdisks > 0) {
533             int sdno;
534
535             if (Verbose) {
536                 printf("\n");
537                 for (sdno = 0; sdno < plex.subdisks; sdno++) {
538                     get_plex_sd_info(&sd, plexno, sdno);
539                     printf("\t\tSubdisk %d:\t%s\n\t\t  state: %s\tsize %11lld (%lld MB)\n",
540                         sdno,
541                         sd.name,
542                         sd_state(sd.state),
543                         (long long) sd.sectors * DEV_BSIZE,
544                         (long long) sd.sectors * DEV_BSIZE / MEGABYTE);
545                     if (plex.organization == plex_concat)
546                         printf("\t\t\toffset %9ld (0x%lx)\n",
547                             (long) sd.plexoffset,
548                             (long) sd.plexoffset);
549                 }
550             }
551             if (recurse) {
552                 printf("\n");
553                 for (sdno = 0; sdno < plex.subdisks; sdno++) {
554                     get_plex_sd_info(&sd, plexno, sdno);
555                     vinum_lsi(sd.sdno, 0);
556                 }
557             }
558         }
559         printf("\n");
560     }
561 }
562
563 void
564 vinum_lp(int argc, char *argv[], char *argv0[])
565 {
566     int i;
567     int plexno;
568     enum objecttype type;
569
570     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
571         perror("Can't get vinum config");
572         return;
573     }
574     if (argc == 0) {
575         for (plexno = 0; plexno < vinum_conf.plexes_allocated; plexno++)
576             vinum_lpi(plexno, recurse);
577     } else {
578         for (i = 0; i < argc; i++) {
579             plexno = find_object(argv[i], &type);
580             if (type == plex_object)
581                 vinum_lpi(plexno, recurse);
582             else
583                 fprintf(stderr, "%s is not a plex\n", argv[i]);
584         }
585     }
586 }
587
588 void
589 vinum_lsi(int sdno, int recurse)
590 {
591     long long revived;                                      /* keep an eye on revive progress */
592     int times;
593
594     get_sd_info(&sd, sdno);
595     if (sd.state != sd_unallocated) {
596         if (vflag) {
597             printf("Subdisk %s:\n\t\tSize: %16lld bytes (%lld MB)\n\t\tState: %s\n",
598                 sd.name,
599                 (long long) sd.sectors * DEV_BSIZE,
600                 (long long) sd.sectors / (MEGABYTE / DEV_BSIZE),
601                 sd_state(sd.state));
602             if (sd.flags & VF_RETRYERRORS)
603                 printf("\t\tretryerrors\n"); 
604             if (sd.plexno >= 0) {
605                 get_plex_info(&plex, sd.plexno);
606                 printf("\t\tPlex %s", plex.name);
607                 printf(" at offset %lld (%s)\n",
608                     (long long) sd.plexoffset * DEV_BSIZE,
609                     roughlength((long long) sd.plexoffset * DEV_BSIZE, 1));
610             }
611             if (sd.state == sd_reviving) {
612                 if (sd.reviver == 0)
613                     printf("\t\t*** Start subdisk with 'start' command ***\n");
614                 else {
615                     printf("\t\tReviver PID:\t%d\n", sd.reviver);
616                     if (kill(sd.reviver, 0) == -1) {
617                         if (errno == ESRCH)                 /* no process */
618                             printf("\t\t*** Revive process has died ***\n");
619                                                             /* Don't report a problem that "can't happen" */
620                     } else {
621                         revived = sd.revived;               /* note how far we were */
622
623                         /*
624                          * Wait for up to a second until we
625                          * see some progress with the revive.
626                          * Do it like this so we don't have
627                          * annoying delays in the listing.
628                          */
629                         for (times = 0; times < STALLCOUNT; times++) {
630                             get_sd_info(&sd, sdno);
631                             if (sd.revived != revived)      /* progress? */
632                                 break;
633                             usleep(50000);
634                         }
635                         if (times == STALLCOUNT)
636                             printf("\t\t*** Revive has stalled ***\n");
637                     }
638                 }
639                 printf("\t\tRevive pointer:\t\t%s (%d%%)\n",
640                     roughlength(sd.revived << DEV_BSHIFT, 0),
641                     (int) (((u_int64_t) (sd.revived * 100)) / sd.sectors));
642                 printf("\t\tRevive blocksize:\t%s\n"
643                     "\t\tRevive interval:\t%10d seconds\n",
644                     roughlength(sd.revive_blocksize, 0),
645                     sd.revive_interval);
646             }
647             if (sd.state == sd_initializing) {
648                 printf("\t\tInitialize pointer:\t%s (%d%%)\n",
649                     roughlength(sd.initialized << DEV_BSHIFT, 0),
650                     (int) (((u_int64_t) (sd.initialized * 100)) / sd.sectors));
651                 printf("\t\tInitialize blocksize:\t%s\n"
652                     "\t\tInitialize interval:\t%10d seconds\n",
653                     roughlength(sd.init_blocksize, 0),
654                     sd.init_interval);
655             }
656             get_drive_info(&drive, sd.driveno);
657             if (sd.driveoffset < 0)
658                 printf("\t\tDrive %s (%s), no offset\n",
659                     drive.label.name,
660                     drive.devicename);
661             else if (drive.devicename[0] != '\0')           /* has a name */
662                 printf("\t\tDrive %s (%s) at offset %lld (%s)\n",
663                     drive.label.name,
664                     drive.devicename,
665                     (long long) (sd.driveoffset * DEV_BSIZE),
666                     roughlength(sd.driveoffset * DEV_BSIZE, 1));
667             else
668                 printf("\t\tDrive %s (*missing*) at offset %lld (%s)\n",
669                     drive.label.name,
670                     (long long) (sd.driveoffset * DEV_BSIZE),
671                     roughlength(sd.driveoffset * DEV_BSIZE, 1));
672         } else if (!sflag) {                                /* brief listing, no stats */
673             if (sd.state == sd_reviving)
674                 printf("S %-21s State: R %d%%\t",
675                     sd.name,
676                     (int) (((u_int64_t) (sd.revived * 100)) / sd.sectors));
677             else if (sd.state == sd_initializing)
678                 printf("S %-21s State: I %d%%\t",
679                     sd.name,
680                     (int) (((u_int64_t) (sd.initialized * 100)) / sd.sectors));
681             else
682                 printf("S %-21s State: %s\t",
683                     sd.name,
684                     sd_state(sd.state));
685             if (sd.plexno == -1)
686                 printf("(detached)\t");
687             else
688                 printf("PO: %s ",
689                     &(roughlength(sd.plexoffset << DEV_BSHIFT, 0))[2]); /* what a kludge! */
690             printf("Size: %s\n",
691                 roughlength(sd.sectors << DEV_BSHIFT, 0));
692             if (sd.state == sd_reviving) {
693                 if (sd.reviver == 0)
694                     printf("\t\t\t*** Start %s with 'start' command ***\n",
695                         sd.name);
696                 else if (kill(sd.reviver, 0) == -1) {
697                     if (errno == ESRCH)                     /* no process */
698                         printf("\t\t\t*** Revive process for %s has died ***\n",
699                             sd.name);
700                                                             /* Don't report a problem that "can't happen" */
701                 } else {
702                     revived = sd.revived;                   /* note how far we were */
703
704                     /*
705                      * Wait for up to a second until we
706                      * see some progress with the revive.
707                      * Do it like this so we don't have
708                      * annoying delays in the listing.
709                      */
710                     for (times = 0; times < STALLCOUNT; times++) {
711                         get_sd_info(&sd, sdno);
712                         if (sd.revived != revived)          /* progress? */
713                             break;
714                         usleep(50000);
715                     }
716                     if (times == STALLCOUNT)
717                         printf("\t\t\t*** Revive of %s has stalled ***\n",
718                             sd.name);
719                 }
720             }
721         }
722         if (sflag) {
723             if (vflag || Verbose) {
724                 printf("\t\tReads:  \t%16lld\n\t\tBytes read:\t%16lld (%s)\n",
725                     (long long) sd.reads,
726                     (long long) sd.bytes_read,
727                     roughlength(sd.bytes_read, 1));
728                 if (sd.reads != 0)
729                     printf("\t\tAverage read:\t%16lld bytes\n",
730                         (long long) (sd.bytes_read / sd.reads));
731                 printf("\t\tWrites: \t%16lld\n\t\tBytes written:\t%16lld (%s)\n",
732                     (long long) sd.writes,
733                     (long long) sd.bytes_written,
734                     roughlength(sd.bytes_written, 1));
735                 if (sd.writes != 0)
736                     printf("\t\tAverage write:\t%16lld bytes\n",
737                         (long long) (sd.bytes_written / sd.writes));
738             } else {
739                 printf("%-15s\t%7lld\t%15lld\t",
740                     sd.name,
741                     (long long) sd.reads,
742                     (long long) sd.bytes_read);
743                 if (sd.reads != 0)
744                     printf("%7lld\t\t",
745                         (long long) (sd.bytes_read / sd.reads));
746                 else
747                     printf("\t\t");
748                 printf("%7lld\t%15lld\t",
749                     (long long) sd.writes,
750                     (long long) sd.bytes_written);
751                 if (sd.writes != 0)
752                     printf("%7lld\n",
753                         (long long) (sd.bytes_written / sd.writes));
754                 else
755                     printf("\n");
756             }
757         }
758         if (recurse)
759             vinum_ldi(sd.driveno, 0);
760         if (vflag)
761             printf("\n");                                   /* make it more readable */
762     }
763 }
764
765 void
766 vinum_ls(int argc, char *argv[], char *argv0[])
767 {
768     int i;
769     int sdno;
770
771     /* Structures to read kernel data into */
772     struct _vinum_conf vinum_conf;
773     enum objecttype type;
774
775     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
776         perror("Can't get vinum config");
777         return;
778     }
779     if (argc == 0) {
780         for (sdno = 0; sdno < vinum_conf.subdisks_allocated; sdno++)
781             vinum_lsi(sdno, recurse);
782     } else {                                                /* specific subdisks */
783         for (i = 0; i < argc; i++) {
784             sdno = find_object(argv[i], &type);
785             if (type == sd_object)
786                 vinum_lsi(sdno, recurse);
787             else
788                 fprintf(stderr, "%s is not a subdisk\n", argv[i]);
789         }
790     }
791 }
792
793
794 /* List the complete configuration.
795
796  * XXX Change this to specific lists */
797 void
798 listconfig()
799 {
800     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
801         perror("Can't get vinum config");
802         return;
803     }
804     printf("%d drives:\n", vinum_conf.drives_used);
805     if (vinum_conf.drives_used > 0) {
806         vinum_ld(0, NULL, NULL);
807         printf("\n");
808     }
809     printf("%d volumes:\n", vinum_conf.volumes_used);
810     if (vinum_conf.volumes_used > 0) {
811         vinum_lv(0, NULL, NULL);
812         printf("\n");
813     }
814     printf("%d plexes:\n", vinum_conf.plexes_used);
815     if (vinum_conf.plexes_used > 0) {
816         vinum_lp(0, NULL, NULL);
817         printf("\n");
818     }
819     printf("%d subdisks:\n", vinum_conf.subdisks_used);
820     if (vinum_conf.subdisks_used > 0)
821         vinum_ls(0, NULL, NULL);
822 }
823
824 /* Convert a timeval to Tue Oct 13 13:54:14.0434324
825  * Return pointer to text */
826 char *
827 timetext(struct timeval *time)
828 {
829     static char text[30];
830     time_t t;                                               /* to keep Bruce happy */
831
832     t = time->tv_sec;
833     strcpy(text, ctime(&t));                                /* to the second */
834     sprintf(&text[19], ".%06ld", time->tv_usec);            /* and the microseconds */
835     return &text[11];
836 }
837
838 void
839 vinum_info(int argc, char *argv[], char *argv0[])
840 {
841     struct meminfo meminfo;
842     struct mc malloced;
843     int i;
844 #if VINUMDEBUG
845     struct rqinfo rq;
846 #endif
847
848     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
849         perror("Can't get vinum config");
850         return;
851     }
852     printf("Flags: 0x%x\n", vinum_conf.flags);
853     if (ioctl(superdev, VINUM_MEMINFO, &meminfo) < 0) {
854         perror("Can't get information");
855         return;
856     }
857     printf("Total of %d blocks malloced, total memory: %d\nMaximum allocs: %8d, malloc table at 0x%08x\n",
858         meminfo.mallocs,
859         meminfo.total_malloced,
860         meminfo.highwater,
861         (int) meminfo.malloced);
862
863     printf("%d requests active, maximum %d active\n",
864         vinum_conf.active,
865         vinum_conf.maxactive);
866     if (vflag && (!Verbose))
867         for (i = 0; i < meminfo.mallocs; i++) {
868             malloced.seq = i;
869             if (ioctl(superdev, VINUM_MALLOCINFO, &malloced) < 0) {
870                 perror("Can't get information");
871                 return;
872             }
873             if (!(i & 63))
874                 printf("Block\tSequence\t  size\t  address\t  line\t\tfile\n\n");
875             printf("%6d\t%6d\t\t%6d\t0x%08x\t%6d\t\t%s\n",
876                 i,
877                 malloced.seq,
878                 malloced.size,
879                 (int) malloced.address,
880                 malloced.line,
881                 (char *) &malloced.file);
882         }
883 #if VINUMDEBUG
884     if (Verbose) {
885         printf("\nTime\t\t Event\t     Buf\tDev\t  Offset\tBytes\tSD\tSDoff\tDoffset\tGoffset\n\n");
886         for (i = RQINFO_SIZE - 1; i >= 0; i--) {            /* go through the request list in order */
887             *((int *) &rq) = i;
888             if (ioctl(superdev, VINUM_RQINFO, &rq) < 0) {
889                 perror("Can't get information");
890                 return;
891             }
892             /* Compress devminor into something printable. */
893             rq.devminor = (rq.devminor & 0xff)
894                 | ((rq.devminor & 0xfff0000) >> 8);
895             switch (rq.type) {
896             case loginfo_unused:                            /* never been used */
897                 break;
898
899             case loginfo_user_bp:                           /* this is the bp when strategy is called */
900                 printf("%s %dVS %s %p\t%d.%-6d 0x%-9x\t%ld\n",
901                     timetext(&rq.timestamp),
902                     rq.type,
903                     rq.info.b.b_flags & B_READ ? "Read " : "Write",
904                     rq.bp,
905                     rq.devmajor,
906                     rq.devminor,
907                     rq.info.b.b_blkno,
908                     rq.info.b.b_bcount);
909                 break;
910
911             case loginfo_sdiol:                             /* subdisk I/O launch */
912             case loginfo_user_bpl:                          /* and this is the bp at launch time */
913                 printf("%s %dLR %s %p\t%d.%-6d 0x%-9x\t%ld\n",
914                     timetext(&rq.timestamp),
915                     rq.type,
916                     rq.info.b.b_flags & B_READ ? "Read " : "Write",
917                     rq.bp,
918                     rq.devmajor,
919                     rq.devminor,
920                     rq.info.b.b_blkno,
921                     rq.info.b.b_bcount);
922                 break;
923
924             case loginfo_rqe:                               /* user RQE */
925                 printf("%s 3RQ %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
926                     timetext(&rq.timestamp),
927                     rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
928                     rq.bp,
929                     rq.devmajor,
930                     rq.devminor,
931                     rq.info.rqe.b.b_blkno,
932                     rq.info.rqe.b.b_bcount,
933                     rq.info.rqe.sdno,
934                     rq.info.rqe.sdoffset,
935                     rq.info.rqe.dataoffset,
936                     rq.info.rqe.groupoffset);
937                 break;
938
939             case loginfo_iodone:                            /* iodone called */
940                 printf("%s 4DN %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
941                     timetext(&rq.timestamp),
942                     rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
943                     rq.bp,
944                     rq.devmajor,
945                     rq.devminor,
946                     rq.info.rqe.b.b_blkno,
947                     rq.info.rqe.b.b_bcount,
948                     rq.info.rqe.sdno,
949                     rq.info.rqe.sdoffset,
950                     rq.info.rqe.dataoffset,
951                     rq.info.rqe.groupoffset);
952                 break;
953
954             case loginfo_raid5_data:                        /* RAID-5 write data block */
955                 printf("%s 5RD %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
956                     timetext(&rq.timestamp),
957                     rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
958                     rq.bp,
959                     rq.devmajor,
960                     rq.devminor,
961                     rq.info.rqe.b.b_blkno,
962                     rq.info.rqe.b.b_bcount,
963                     rq.info.rqe.sdno,
964                     rq.info.rqe.sdoffset,
965                     rq.info.rqe.dataoffset,
966                     rq.info.rqe.groupoffset);
967                 break;
968
969             case loginfo_raid5_parity:                      /* RAID-5 write parity block */
970                 printf("%s 6RP %s %p\t%d.%-6d 0x%-9x\t%ld\t%d\t%x\t%x\t%x\n",
971                     timetext(&rq.timestamp),
972                     rq.info.rqe.b.b_flags & B_READ ? "Read " : "Write",
973                     rq.bp,
974                     rq.devmajor,
975                     rq.devminor,
976                     rq.info.rqe.b.b_blkno,
977                     rq.info.rqe.b.b_bcount,
978                     rq.info.rqe.sdno,
979                     rq.info.rqe.sdoffset,
980                     rq.info.rqe.dataoffset,
981                     rq.info.rqe.groupoffset);
982                 break;
983
984             case loginfo_sdio:                              /* subdisk I/O */
985                 printf("%s %dVS %s %p\t\t  0x%-9x\t%ld\t%d\n",
986                     timetext(&rq.timestamp),
987                     rq.type,
988                     rq.info.b.b_flags & B_READ ? "Read " : "Write",
989                     rq.bp,
990                     rq.info.b.b_blkno,
991                     rq.info.b.b_bcount,
992                     rq.devminor);
993                 break;
994
995             case loginfo_sdiodone:                          /* subdisk I/O done */
996                 printf("%s %dSD %s %p\t\t  0x%-9x\t%ld\t%d\n",
997                     timetext(&rq.timestamp),
998                     rq.type,
999                     rq.info.b.b_flags & B_READ ? "Read " : "Write",
1000                     rq.bp,
1001                     rq.info.b.b_blkno,
1002                     rq.info.b.b_bcount,
1003                     rq.devminor);
1004                 break;
1005
1006             case loginfo_lockwait:
1007                 printf("%s Lockwait  %p\t  0x%x\n",
1008                     timetext(&rq.timestamp),
1009                     rq.bp,
1010                     rq.info.lockinfo.stripe);
1011                 break;
1012
1013             case loginfo_lock:
1014                 printf("%s Lock      %p\t  0x%x\n",
1015                     timetext(&rq.timestamp),
1016                     rq.bp,
1017                     rq.info.lockinfo.stripe);
1018                 break;
1019
1020             case loginfo_unlock:
1021                 printf("%s Unlock\t  %p\t  0x%x\n",
1022                     timetext(&rq.timestamp),
1023                     rq.bp,
1024                     rq.info.lockinfo.stripe);
1025                 break;
1026             }
1027         }
1028     }
1029 #endif
1030 }
1031
1032 /*
1033  * Print config file to a file.  This is a userland version
1034  * of kernel format_config
1035  */
1036 void
1037 vinum_printconfig(int argc, char *argv[], char *argv0[])
1038 {
1039     FILE *of;
1040
1041     if (argc > 1) {
1042         fprintf(stderr, "Usage: \tprintconfig [<outfile>]\n");
1043         return;
1044     } else if (argc == 1)
1045         of = fopen(argv[0], "w");
1046     else
1047         of = stdout;
1048     if (of == NULL) {
1049         fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno));
1050         return;
1051     }
1052     printconfig(of, "");
1053     if (argc == 1)
1054         fclose(of);
1055 }
1056
1057 /*
1058  * The guts of printconfig.  This is called from
1059  * vinum_printconfig and from vinum_create when
1060  * called without an argument, in order to give
1061  * the user something to edit.
1062  */
1063 void
1064 printconfig(FILE * of, char *comment)
1065 {
1066     struct utsname uname_s;
1067     time_t now;
1068     int i;
1069     struct volume vol;
1070     struct plex plex;
1071     struct sd sd;
1072     struct drive drive;
1073
1074     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
1075         perror("Can't get vinum config");
1076         return;
1077     }
1078     uname(&uname_s);                                        /* get our system name */
1079     time(&now);                                             /* and the current time */
1080     fprintf(of,
1081         "# Vinum configuration of %s, saved at %s",
1082         uname_s.nodename,
1083         ctime(&now));                                       /* say who did it */
1084
1085     if (comment[0] != 0)                                    /* abuse this for commented version */
1086         fprintf(of, "# Current configuration:\n");
1087     for (i = 0; i < vinum_conf.drives_allocated; i++) {
1088         get_drive_info(&drive, i);
1089         if (drive.state != drive_unallocated) {
1090             fprintf(of,
1091                 "%sdrive %s device %s\n",
1092                 comment,
1093                 drive.label.name,
1094                 drive.devicename);
1095         }
1096     }
1097
1098     for (i = 0; i < vinum_conf.volumes_allocated; i++) {
1099         get_volume_info(&vol, i);
1100         if (vol.state != volume_unallocated) {
1101             if (vol.preferred_plex >= 0)                    /* preferences, */
1102                 fprintf(of,
1103                     "%svolume %s readpol prefer %s\n",
1104                     comment,
1105                     vol.name,
1106                     vinum_conf.plex[vol.preferred_plex].name);
1107             else                                            /* default round-robin */
1108                 fprintf(of, "%svolume %s\n", comment, vol.name);
1109         }
1110     }
1111
1112     /* Then the plex configuration */
1113     for (i = 0; i < vinum_conf.plexes_allocated; i++) {
1114         get_plex_info(&plex, i);
1115         if (plex.state != plex_unallocated) {
1116             fprintf(of, "%splex name %s org %s ",
1117                 comment,
1118                 plex.name,
1119                 plex_org(plex.organization));
1120             if (isstriped((&plex)))
1121                 fprintf(of, "%ds ", (int) plex.stripesize);
1122             if (plex.volno >= 0) {                          /* we have a volume */
1123                 get_volume_info(&vol, plex.volno);
1124                 fprintf(of, "vol %s ", vol.name);
1125             } else
1126                 fprintf(of, "detached ");
1127             fprintf(of, "\n");
1128         }
1129     }
1130
1131     /* And finally the subdisk configuration */
1132     for (i = 0; i < vinum_conf.subdisks_allocated; i++) {
1133         get_sd_info(&sd, i);
1134         if (sd.state != sd_unallocated) {
1135             get_drive_info(&drive, sd.driveno);
1136             if (sd.plexno >= 0) {
1137                 get_plex_info(&plex, sd.plexno);
1138                 fprintf(of,
1139                     "%ssd name %s drive %s plex %s len %llds driveoffset %llds plexoffset %llds\n",
1140                     comment,
1141                     sd.name,
1142                     drive.label.name,
1143                     plex.name,
1144                     (long long) sd.sectors,
1145                     (long long) sd.driveoffset,
1146                     (long long) sd.plexoffset);
1147             } else
1148                 fprintf(of,
1149                     "%ssd name %s drive %s detached len %llds driveoffset %llds\n",
1150                     comment,
1151                     sd.name,
1152                     drive.label.name,
1153                     (long long) sd.sectors,
1154                     (long long) sd.driveoffset);
1155         }
1156     }
1157 }
1158
1159 void
1160 list_defective_objects()
1161 {
1162     int o;                                                  /* object */
1163     int heading_needed = 1;
1164
1165     if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) {
1166         perror("Can't get vinum config");
1167         return;
1168     }
1169     for (o = 0; o < vinum_conf.drives_allocated; o++) {
1170         get_drive_info(&drive, o);
1171         if ((drive.state != drive_unallocated)              /* drive exists */
1172         &&(drive.state != drive_up)) {                      /* but it's not up */
1173             if (heading_needed) {
1174                 printf("Warning: defective objects\n\n");
1175                 heading_needed = 0;
1176             }
1177             vinum_ldi(o, 0);                                /* print info */
1178         }
1179     }
1180
1181     for (o = 0; o < vinum_conf.volumes_allocated; o++) {
1182         get_volume_info(&vol, o);
1183         if ((vol.state != volume_unallocated)               /* volume exists */
1184         &&(vol.state != volume_up)) {                       /* but it's not up */
1185             if (heading_needed) {
1186                 printf("Warning: defective objects\n\n");
1187                 heading_needed = 0;
1188             }
1189             vinum_lvi(o, 0);                                /* print info */
1190         }
1191     }
1192
1193     for (o = 0; o < vinum_conf.plexes_allocated; o++) {
1194         get_plex_info(&plex, o);
1195         if ((plex.state != plex_unallocated)                /* plex exists */
1196         &&(plex.state != plex_up)) {                        /* but it's not up */
1197             if (heading_needed) {
1198                 printf("Warning: defective objects\n\n");
1199                 heading_needed = 0;
1200             }
1201             vinum_lpi(o, 0);                                /* print info */
1202         }
1203     }
1204
1205     for (o = 0; o < vinum_conf.subdisks_allocated; o++) {
1206         get_sd_info(&sd, o);
1207         if ((sd.state != sd_unallocated)                    /* sd exists */
1208         &&(sd.state != sd_up)) {                            /* but it's not up */
1209             if (heading_needed) {
1210                 printf("Warning: defective objects\n\n");
1211                 heading_needed = 0;
1212             }
1213             vinum_lsi(o, 0);                                /* print info */
1214         }
1215     }
1216 }
1217
1218 /* Dump config from specified disk drives */
1219 void
1220 vinum_dumpconfig(int argc, char *argv[], char *argv0[])
1221 {
1222     int i;
1223
1224     if (argc == 0) {                                        /* start everything */
1225         int devs = getnumdevs();
1226         struct statinfo statinfo;
1227         char *namelist;
1228         char *enamelist;                                    /* end of name list */
1229         int i;
1230         char **token;                                       /* list of tokens */
1231         int tokens;                                         /* and their number */
1232
1233         bzero(&statinfo, sizeof(struct statinfo));
1234         statinfo.dinfo = malloc(devs * sizeof(struct statinfo));
1235         namelist = malloc(devs * (DEVSTAT_NAME_LEN + 8));
1236         token = malloc((devs + 1) * sizeof(char *));
1237         if ((statinfo.dinfo == NULL) || (namelist == NULL) || (token == NULL)) {
1238             fprintf(stderr, "Can't allocate memory for drive list\n");
1239             return;
1240         }
1241         bzero(statinfo.dinfo, sizeof(struct devinfo));
1242
1243         tokens = 0;                                         /* no tokens yet */
1244         if (getdevs(&statinfo) < 0) {                       /* find out what devices we have */
1245             perror("Can't get device list");
1246             return;
1247         }
1248         namelist[0] = '\0';                                 /* start with empty namelist */
1249         enamelist = namelist;                               /* point to the end of the list */
1250
1251         for (i = 0; i < devs; i++) {
1252             struct devstat *stat = &statinfo.dinfo->devices[i];
1253
1254             if (((stat->device_type & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT) /* disk device */
1255             &&((stat->device_type & DEVSTAT_TYPE_PASS) == 0) /* and not passthrough */
1256             &&((stat->device_name[0] != '\0'))) {           /* and it has a name */
1257                 sprintf(enamelist, "/dev/%s%d", stat->device_name, stat->unit_number);
1258                 token[tokens] = enamelist;                  /* point to it */
1259                 tokens++;                                   /* one more token */
1260                 enamelist = &enamelist[strlen(enamelist) + 1]; /* and start beyond the end */
1261             }
1262         }
1263         free(statinfo.dinfo);                               /* don't need the list any more */
1264         for (i = 0; i < tokens; i++)
1265             dumpconfig(token[i]);
1266         free(namelist);
1267         free(token);
1268     } else {                                                /* list specified drives */
1269         for (i = 0; i < argc; i++)
1270             dumpconfig(argv[i]);
1271     }
1272 }
1273
1274 #define DEVLEN 5
1275 void
1276 dumpconfig(char *part)
1277 {
1278     char partname[MAXPATHLEN];
1279     char *partid;
1280     char partition;                                         /* UNIX partition */
1281     int slice;
1282     int founddrive;                                         /* flag when we find a vinum drive */
1283     struct disklabel label;                                 /* label of this drive */
1284     int driveno;                                            /* fd of drive */
1285     int found;
1286     u_int64_t drivelength;
1287
1288     if (memcmp(part, "/dev/", DEVLEN) == 0)                 /* starts with /dev */
1289         memcpy(partname, part, MAXPATHLEN);
1290     else {                                                  /* prepend */
1291         strcpy(partname, "/dev/");
1292         strncat(&partname[DEVLEN], part, MAXPATHLEN - DEVLEN);
1293     }
1294     partid = &partname[strlen(partname)];
1295     founddrive = 0;                                         /* no vinum drive found yet on this spindle */
1296     /* first try the partition table */
1297     for (slice = 1; slice < 5; slice++) {
1298         sprintf(partid, "s%dc", slice);                     /* c partition */
1299         driveno = open(partname, O_RDONLY);
1300         if (driveno < 0) {
1301             if (errno != ENOENT)
1302                 fprintf(stderr, "Can't open %s: %s (%d)\n", partname, strerror(errno), errno);
1303             continue;
1304         }
1305         if (ioctl(driveno, DIOCGDINFO, &label) < 0) {
1306             fprintf(stderr, "Can't get label from %s: %s (%d)\n", partname, strerror(errno), errno);
1307             continue;
1308         }
1309         for (partition = 'a'; partition < 'i'; partition++) {
1310             if ((partition != 'c')                          /* it's not the c partition */
1311             &&((label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) /* and it's a Vinum partition */
1312             ||Verbose)) {                                   /* or we're just plain curious */
1313                 sprintf(partid, "s%d%c", slice, partition);
1314                 found = check_drive(partname);              /* try to open it */
1315                 founddrive |= found;                        /* and note if we were successful at all */
1316                 if (label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) { /* it's a Vinum partition */
1317                     drivelength = ((u_int64_t) label.d_partitions[partition - 'a'].p_size) * DEV_BSIZE;
1318                     printf("Drive %s: %s (%lld bytes)\n",
1319                         partname,
1320                         roughlength(drivelength, 1),
1321                         drivelength);
1322                     if ((!found) && vflag)                  /* we're talkative */
1323                         printf("*** no configuration found ***\n");
1324                 }
1325             }
1326         }
1327     }
1328     if (founddrive == 0) {                                  /* didn't find anything, */
1329         sprintf(partid, "c");                               /* c partition */
1330         driveno = open(partname, O_RDONLY);
1331         if (driveno < 0) {
1332             if (errno != ENOENT)
1333                 fprintf(stderr, "Can't open %s: %s (%d)\n", partname, strerror(errno), errno);
1334             return;
1335         }
1336         if (ioctl(driveno, DIOCGDINFO, &label) < 0) {
1337             fprintf(stderr, "Can't get label from %s: %s (%d)\n", partname, strerror(errno), errno);
1338             return;
1339         }
1340         for (partition = 'a'; partition < 'i'; partition++) { /* try the compatibility partition */
1341             if ((partition != 'c')                          /* it's not the c partition */
1342             &&((label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) /* and it's a Vinum partition */
1343             ||Verbose)) {                                   /* or we're just plain curious */
1344                 sprintf(partid, "%c", partition);
1345                 found = check_drive(partname);              /* try to open it */
1346                 founddrive |= found;                        /* and note if we were successful at all */
1347                 if (label.d_partitions[partition - 'a'].p_fstype == FS_VINUM) { /* it's a Vinum partition */
1348                     drivelength = ((u_int64_t) label.d_partitions[partition - 'a'].p_size) * DEV_BSIZE;
1349                     printf("Drive %s: %s (%lld bytes)\n",
1350                         partname,
1351                         roughlength(drivelength, 1),
1352                         drivelength);
1353                     if ((!found) && vflag)                  /* we're talkative */
1354                         printf("*** no configuration found ***\n");
1355                 }
1356             }
1357         }
1358     }
1359 }
1360
1361 /*
1362  * Check a drive for a Vinum header.  If found,
1363  * print configuration information from the drive.
1364  *
1365  * Return 1 if Vinum config found.
1366  */
1367 int
1368 check_drive(char *devicename)
1369 {
1370     int fd;
1371     char vinumlabel[DEV_BSIZE];                             /* one sector for label */
1372     struct vinum_hdr *hdr = (struct vinum_hdr *) vinumlabel; /* with this structure */
1373     char *config_text;                                      /* read the config info from disk into here */
1374     time_t t;
1375
1376     fd = open(devicename, O_RDONLY);
1377     if (fd >= 0) {
1378         if (lseek(fd, VINUM_LABEL_OFFSET, SEEK_SET) < 0) {
1379             fprintf(stderr,
1380                 "Can't seek label for %s: %s (%d)\n",
1381                 devicename,
1382                 strerror(errno),
1383                 errno);
1384             close(fd);
1385             return 0;
1386         }
1387         if (read(fd, vinumlabel, DEV_BSIZE) != DEV_BSIZE) {
1388             if (errno != EINVAL)
1389                 fprintf(stderr,
1390                     "Can't read label from %s: %s (%d)\n",
1391                     devicename,
1392                     strerror(errno),
1393                     errno);
1394             close(fd);
1395             return 0;
1396         }
1397         if ((hdr->magic == VINUM_MAGIC)
1398             || (vflag && (hdr->magic == VINUM_NOMAGIC))) {
1399             printf("Drive %s:\tDevice %s\n",
1400                 hdr->label.name,
1401                 devicename);
1402             if (hdr->magic == VINUM_NOMAGIC)
1403                 printf("*** Drive has been obliterated ***\n");
1404             t = hdr->label.date_of_birth.tv_sec;
1405             printf("\t\tCreated on %s at %s",
1406                 hdr->label.sysname,
1407                 ctime(&t));
1408             t = hdr->label.last_update.tv_sec;
1409             printf("\t\tConfig last updated %s",            /* care: \n at end */
1410                 ctime(&t));
1411             printf("\t\tSize: %16lld bytes (%lld MB)\n",
1412                 (long long) hdr->label.drive_size,          /* bytes used */
1413                 (long long) (hdr->label.drive_size / MEGABYTE));
1414             config_text = (char *) malloc(MAXCONFIG);
1415             if (config_text == NULL)
1416                 fprintf(stderr, "Can't allocate memory\n");
1417             else {
1418                 if (read(fd, config_text, MAXCONFIG) != MAXCONFIG)
1419                     fprintf(stderr,
1420                         "Can't read config from %s: %s (%d)\n",
1421                         devicename,
1422                         strerror(errno),
1423                         errno);
1424                 else
1425                     puts(config_text);
1426                 free(config_text);
1427             }
1428         }
1429         close(fd);
1430         return 1;
1431     }
1432     return 0;
1433 }
1434
1435 /* Local Variables: */
1436 /* fill-column: 50 */
1437 /* End: */