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