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