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