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