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