b9b8f36c1f4010e4abd569d2a70cdc0956dd9989
[dragonfly.git] / usr.sbin / mlxcontrol / command.c
1 /*-
2  * Copyright (c) 1999 Michael Smith
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *      $FreeBSD: src/usr.sbin/mlxcontrol/command.c,v 1.3 2008/09/12 17:40:17 sepotvin Exp $
27  */
28
29 #include <fcntl.h>
30 #include <paths.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <err.h>
36
37 #include <dev/raid/mlx/mlxio.h>
38 #include <dev/raid/mlx/mlxreg.h>
39
40 #include "mlxcontrol.h"
41
42 static int      cmd_status(int argc, char *argv[]);
43 static int      cmd_rescan(int argc, char *argv[]);
44 static int      cmd_detach(int argc, char *argv[]);
45 static int      cmd_check(int argc, char *argv[]);
46 static int      cmd_rebuild(int argc, char *argv[]);
47 #ifdef SUPPORT_PAUSE
48 static int      cmd_pause(int argc, char *argv[]);
49 #endif
50 static int      cmd_help(int argc, char *argv[]);
51 static int      cmd_config(int argc, char *argv[]);
52
53 static void     print_span(struct mlx_sys_drv_span *span, int arms);
54 static void     print_sys_drive(struct conf_config *conf, int drvno);
55 static void     print_phys_drive(struct conf_config *conf, int chn, int targ);
56
57 struct 
58 {
59     const char  *cmd;
60     int         (*func)(int argc, char *argv[]);
61     const char  *desc;
62     const char  *text;
63 } commands[] = {
64     {"status",  cmd_status, 
65      "displays device status",
66      "  status [-qv] [<drive>...]\n"
67      "      Display status for <drive> or all drives if none is listed\n"
68      "  -q    Suppress output.\n"
69      "  -v    Display verbose information.\n"
70      "  Returns 0 if all drives tested are online, 1 if one or more are\n"
71      "  critical, and 2 if one or more are offline."},
72     {"rescan",  cmd_rescan, 
73      "scan for new system drives",
74      "  rescan <controller> [<controller>...]\n"
75      "      Rescan <controller> for system drives.\n"
76      "  rescan -a\n"
77      "      Rescan all controllers for system drives."},
78     {"detach",  cmd_detach,
79      "detach system drives",
80      "  detach <drive> [<drive>...]\n"
81      "      Detaches <drive> from the controller.\n"
82      "  detach -a <controller>\n"
83      "      Detaches all drives on <controller>."},
84     {"check",   cmd_check,
85      "consistency-check a system drive",
86      "  check <drive>\n"
87      "      Requests a check and rebuild of the parity information on <drive>.\n"
88      "      Note that each controller can only check one system drive at a time."},
89     {"rebuild", cmd_rebuild,
90      "initiate a rebuild of a dead physical drive",
91      "  rebuild <controller> <physdrive>\n"
92      "      All system drives using space on the physical drive <physdrive>\n"
93      "      are rebuilt, reconstructing all data on the drive.\n"
94      "      Note that each controller can only perform one rebuild at a time."},
95 #ifdef SUPPORT_PAUSE
96     {"pause",   cmd_pause,
97      "pauses controller channels",
98      "  pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
99      "      Pauses SCSI I/O on <channel> and <controller>.  If no channel is specified,\n"
100      "      all channels are paused.\n"
101      "  <howlong>   How long (seconds) to pause for (default 30).\n"
102      "  <delay>     How long (seconds) to wait before pausing (default 30).\n"
103      "  pause <controller> -c\n"
104      "      Cancels any pending pause operation on <controller>."},
105 #endif
106     {"config",  cmd_config,
107      "examine and update controller configuration",
108      "  config <controller>\n"
109      "      Print configuration for <controller>."},
110     {"help",    cmd_help,   
111      "give help on usage",
112      ""},
113     {NULL, NULL, NULL, NULL}
114 };
115
116 /********************************************************************************
117  * Command dispatch and global options parsing.
118  */
119
120 int
121 main(int argc, char *argv[])
122 {
123     int         ch, i, oargc;
124     char        **oargv;
125     
126     oargc = argc;
127     oargv = argv;
128     while ((ch = getopt(argc, argv, "")) != -1)
129         switch(ch) {
130         default:
131             return(cmd_help(0, NULL));
132         }
133
134     argc -= optind;
135     argv += optind;
136     
137     if (argc > 0)
138         for (i = 0; commands[i].cmd != NULL; i++)
139             if (!strcmp(argv[0], commands[i].cmd))
140                 return(commands[i].func(argc, argv));
141
142     return(cmd_help(oargc, oargv));
143 }
144
145 /********************************************************************************
146  * Helptext output
147  */
148 static int
149 cmd_help(int argc, char *argv[]) 
150 {
151     int         i;
152     
153     if (argc > 1)
154         for (i = 0; commands[i].cmd != NULL; i++)
155             if (!strcmp(argv[1], commands[i].cmd)) {
156                 fprintf(stderr, "%s\n", commands[i].text);
157                 fflush(stderr);
158                 return(0);
159             }
160
161     if (argv != NULL)
162         fprintf(stderr, "Unknown command '%s'.\n", argv[1]);    
163     fprintf(stderr, "Valid commands are:\n");
164     for (i = 0; commands[i].cmd != NULL; i++)
165         fprintf(stderr, "  %-20s %s\n", commands[i].cmd, commands[i].desc);
166     fflush(stderr);
167     return(0);
168 }
169
170 /********************************************************************************
171  * Status output
172  *
173  * status [-qv] [<device> ...]
174  *              Prints status for <device>, or all if none listed.
175  *
176  * -q   Suppresses output, command returns 0 if devices are OK, 1 if one or
177  *      more devices are critical, 2 if one or more devices are offline.
178  */
179 static struct mlx_rebuild_status        rs;
180 static int                              rs_ctrlr = -1;
181 static int                              status_result = 0;
182
183 /* XXX more verbosity! */
184 static void
185 status_print(int unit, void *arg)
186 {
187     int                         verbosity = *(int *)arg;
188     int                         fd, result, statvalid;
189     int                         ctrlr = -1, sysdrive = -1;
190     
191     /* Find which controller and what system drive we are */
192     statvalid = 0;
193     if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
194         warnx("couldn't get controller/drive for %s", drivepath(unit));
195     } else {
196         /* If we don't have rebuild stats for this controller, get them */
197         if (rs_ctrlr == ctrlr) {
198             statvalid = 1;
199         } else {
200             if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
201                 warn("can't open %s", ctrlrpath(ctrlr));
202             } else {
203                 if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
204                     warn("ioctl MLX_REBUILDSTAT");
205                 } else {
206                     rs_ctrlr = ctrlr;
207                     statvalid = 1;
208                 }
209                 close(fd);
210             }
211         }
212     }
213
214     /* Get the device */
215     if ((fd = open(drivepath(unit), 0)) < 0) {
216         warn("can't open %s", drivepath(unit));
217         return;
218     }
219
220     /* Get its status */
221     if (ioctl(fd, MLXD_STATUS, &result) < 0) {
222         warn("ioctl MLXD_STATUS");
223     } else {
224         switch(result) {
225         case MLX_SYSD_ONLINE:
226             if (verbosity > 0)
227                 printf("%s: online", drivename(unit));
228             break;
229         case MLX_SYSD_CRITICAL:
230             if (verbosity > 0)
231                 printf("%s: critical", drivename(unit));
232             if (status_result < 1)
233                 status_result = 1;
234             break;
235         case MLX_SYSD_OFFLINE:
236             if (verbosity > 0)
237                 printf("%s: offline", drivename(unit));
238             if (status_result < 2)
239                 status_result = 2;
240             break;
241         default:
242             if (verbosity > 0) {
243                 printf("%s: unknown status 0x%x", drivename(unit), result);
244             }
245         }
246         if (verbosity > 0) {
247             /* rebuild/check in progress on this drive? */
248             if (statvalid && (rs_ctrlr == ctrlr) && 
249                 (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
250                 switch(rs.rs_code) {
251                 case MLX_REBUILDSTAT_REBUILDCHECK:
252                     printf(" [consistency check");
253                     break;
254                 case MLX_REBUILDSTAT_ADDCAPACITY:
255                     printf(" [add capacity");
256                     break;
257                 case MLX_REBUILDSTAT_ADDCAPACITYINIT:
258                     printf(" [add capacity init");
259                     break;
260                 default:
261                     printf(" [unknown operation");
262                 }
263                 printf(": %d/%d, %d%% complete]",
264                        rs.rs_remaining, rs.rs_size, 
265                        ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
266             }
267             printf("\n");
268         }
269     }
270     close(fd);
271 }
272
273 /********************************************************************************
274  * Print details for the system drive (drvno) in a format that we will be
275  * able to parse later.
276  *
277  * drive?? <raidlevel> <writemode>
278  *   span? 0x????????-0x???????? ????MB on <disk> [...]
279  *   ...
280  */
281 static void
282 print_span(struct mlx_sys_drv_span *span, int arms)
283 {
284     int         i;
285
286     printf("0x%08x-0x%08x %uMB on", span->sp_start_lba, span->sp_start_lba + span->sp_nblks, span->sp_nblks / 2048);
287     for (i = 0; i < arms; i++)
288         printf(" disk%02d%02d", span->sp_arm[i] >> 4, span->sp_arm[i] & 0x0f);
289     printf("\n");
290 }
291
292 static void
293 print_sys_drive(struct conf_config *conf, int drvno)
294 {
295     struct mlx_sys_drv  *drv = &conf->cc_cfg.cc_sys_drives[drvno];
296     int                 i;
297
298     printf("drive%02d ", drvno);
299     switch(drv->sd_raidlevel & 0xf) {
300     case MLX_SYS_DRV_RAID0:
301         printf("RAID0");
302         break;
303     case MLX_SYS_DRV_RAID1:
304         printf("RAID1");
305         break;
306     case MLX_SYS_DRV_RAID3:
307         printf("RAID3");
308         break;
309     case MLX_SYS_DRV_RAID5:
310         printf("RAID5");
311         break;
312     case MLX_SYS_DRV_RAID6:
313         printf("RAID6");
314         break;
315     case MLX_SYS_DRV_JBOD:
316         printf("JBOD");
317         break;
318     default:
319         printf("RAID?");
320     }
321     printf(" write%s\n", drv->sd_raidlevel & MLX_SYS_DRV_WRITEBACK ? "back" : "through");
322
323     for (i = 0; i < drv->sd_valid_spans; i++) {
324         printf("  span%d ", i);
325         print_span(&drv->sd_span[i], drv->sd_valid_arms);
326     }
327 }
328
329 /********************************************************************************
330  * Print details for the physical drive at chn/targ in a format suitable for
331  * human consumption.
332  *
333  * <type>CCTT (<state>) "<vendor>/<model>"
334  *                       ????MB <features>
335  *
336  */
337 static void
338 print_phys_drive(struct conf_config *conf, int chn, int targ)
339 {
340     struct mlx_phys_drv         *drv = &conf->cc_cfg.cc_phys_drives[chn * 16 + targ];
341
342     /* if the drive isn't present, don't print it */
343     if (!(drv->pd_flags1 & MLX_PHYS_DRV_PRESENT))
344         return;
345
346     mlx_print_phys_drv(drv, chn, targ, "# ", 1);
347 }
348
349 static struct 
350 {
351     int         hwid;
352     const char  *name;
353 } mlx_controller_names[] = {
354     {0x01,      "960P/PD"},
355     {0x02,      "960PL"},
356     {0x10,      "960PG"},
357     {0x11,      "960PJ"},
358     {0x12,      "960PR"},
359     {0x13,      "960PT"},
360     {0x14,      "960PTL0"},
361     {0x15,      "960PRL"},
362     {0x16,      "960PTL1"},
363     {0x20,      "1100PVX"},
364     {-1, NULL}
365 };
366
367 static void
368 controller_print(int unit, void *arg)
369 {
370     struct mlx_enquiry2 enq;
371     struct mlx_phys_drv pd;
372     int                 verbosity = *(int *)arg;
373     static char         buf[80];
374     const char          *model;
375     int                 i, channel, target;
376
377     if (verbosity == 0)
378         return;
379
380     /* fetch and print controller data */
381     if (mlx_enquiry(unit, &enq)) {
382         printf("mlx%d: error submitting ENQUIRY2\n", unit);
383     } else {
384         
385         for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
386             if ((int)(enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
387                 model = mlx_controller_names[i].name;
388                 break;
389             }
390         }
391         if (model == NULL) {
392             sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
393             model = buf;
394         }
395
396         printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
397                unit, model, 
398                enq.me_actual_channels, 
399                enq.me_actual_channels > 1 ? "s" : "",
400                enq.me_firmware_id & 0xff,
401                (enq.me_firmware_id >> 8) & 0xff,
402                (enq.me_firmware_id >> 16),
403                (enq.me_firmware_id >> 24) & 0xff,
404                enq.me_mem_size / (1024 * 1024));
405
406         if (verbosity > 1) {
407             printf("  Hardware ID                 0x%08x\n", enq.me_hardware_id);
408             printf("  Firmware ID                 0x%08x\n", enq.me_firmware_id);
409             printf("  Configured/Actual channels  %d/%d\n", enq.me_configured_channels,
410                       enq.me_actual_channels);
411             printf("  Max Targets                 %d\n", enq.me_max_targets);
412             printf("  Max Tags                    %d\n", enq.me_max_tags);
413             printf("  Max System Drives           %d\n", enq.me_max_sys_drives);
414             printf("  Max Arms                    %d\n", enq.me_max_arms);
415             printf("  Max Spans                   %d\n", enq.me_max_spans);
416             printf("  DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
417                       enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
418             printf("  DRAM type                   %d\n", enq.me_mem_type);
419             printf("  Clock Speed                 %dns\n", enq.me_clock_speed);
420             printf("  Hardware Speed              %dns\n", enq.me_hardware_speed);
421             printf("  Max Commands                %d\n", enq.me_max_commands);
422             printf("  Max SG Entries              %d\n", enq.me_max_sg);
423             printf("  Max DP                      %d\n", enq.me_max_dp);
424             printf("  Max IOD                     %d\n", enq.me_max_iod);
425             printf("  Max Comb                    %d\n", enq.me_max_comb);
426             printf("  Latency                     %ds\n", enq.me_latency);
427             printf("  SCSI Timeout                %ds\n", enq.me_scsi_timeout);
428             printf("  Min Free Lines              %d\n", enq.me_min_freelines);
429             printf("  Rate Constant               %d\n", enq.me_rate_const);
430             printf("  MAXBLK                      %d\n", enq.me_maxblk);
431             printf("  Blocking Factor             %d sectors\n", enq.me_blocking_factor);
432             printf("  Cache Line Size             %d blocks\n", enq.me_cacheline);
433             printf("  SCSI Capability             %s%dMHz, %d bit\n", 
434                       enq.me_scsi_cap & (1<<4) ? "differential " : "",
435                       (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
436                       8 << (enq.me_scsi_cap & 0x3));
437             printf("  Firmware Build Number       %d\n", enq.me_firmware_build);
438             printf("  Fault Management Type       %d\n", enq.me_fault_mgmt_type);
439 #if 0
440             printf("  Features                    %b\n", enq.me_firmware_features,
441                       "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
442 #endif
443         }
444
445         /* fetch and print physical drive data */
446         for (channel = 0; channel < enq.me_configured_channels; channel++) {
447             for (target = 0; target < enq.me_max_targets; target++) {
448                 if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
449                     (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
450                     mlx_print_phys_drv(&pd, channel, target, "  ", verbosity - 1);
451                     if (verbosity > 1) {
452                         /* XXX print device statistics? */
453                     }
454                 }
455             }
456         }
457     }
458 }
459
460 static int
461 cmd_status(int argc, char *argv[])
462 {
463     int         ch, verbosity = 1, i, unit;
464
465     optreset = 1;
466     optind = 1;
467     while ((ch = getopt(argc, argv, "qv")) != -1)
468         switch(ch) {
469         case 'q':
470             verbosity = 0;
471             break;
472         case 'v':
473             verbosity = 2;
474             break;
475         default:
476             return(cmd_help(argc, argv));
477         }
478     argc -= optind;
479     argv += optind;
480
481     if (argc < 1) {
482         mlx_foreach(controller_print, &verbosity);
483         mlxd_foreach(status_print, &verbosity);
484     } else {
485         for (i = 0; i < argc; i++) {
486             if ((unit = driveunit(argv[i])) == -1) {
487                 warnx("'%s' is not a valid drive", argv[i]);
488             } else {
489                 status_print(unit, &verbosity);
490             }
491         }
492     }
493     return(status_result);
494 }
495
496 /********************************************************************************
497  * Recscan for system drives on one or more controllers.
498  *
499  * rescan <controller> [<controller>...]
500  * rescan -a
501  */
502 static void
503 rescan_ctrlr(int unit, void *junk __unused)
504 {
505     int         fd;
506     
507     /* Get the device */
508     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
509         warn("can't open %s", ctrlrpath(unit));
510         return;
511     }
512
513     if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
514         warn("can't rescan %s", ctrlrname(unit));
515     close(fd);
516 }
517
518 static int
519 cmd_rescan(int argc, char *argv[]) 
520 {
521     int         all = 0, i, ch, unit;
522
523     optreset = 1;
524     optind = 1;
525     while ((ch = getopt(argc, argv, "a")) != -1)
526         switch(ch) {
527         case 'a':
528             all = 1;
529             break;
530         default:
531             return(cmd_help(argc, argv));
532         }
533     argc -= optind;
534     argv += optind;
535
536     if (all) {
537         mlx_foreach(rescan_ctrlr, NULL);
538     } else {
539         for (i = 0; i < argc; i++) {
540             if ((unit = ctrlrunit(argv[i])) == -1) {
541                 warnx("'%s' is not a valid controller", argv[i]);
542             } else {
543                 rescan_ctrlr(unit, NULL);
544             }
545         }
546     }
547     return(0);
548 }
549
550 /********************************************************************************
551  * Detach one or more system drives from a controller.
552  *
553  * detach <drive> [<drive>...]
554  *              Detach <drive>.
555  *
556  * detach -a <controller> [<controller>...]
557  *              Detach all drives on <controller>.
558  *
559  */
560 static void
561 detach_drive(int unit, void *arg __unused)
562 {
563     int         fd;
564     
565     /* Get the device */
566     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
567         warn("can't open %s", ctrlrpath(unit));
568         return;
569     }
570
571     if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
572         warn("can't detach %s", drivename(unit));
573     close(fd);
574 }
575
576 static int
577 cmd_detach(int argc, char *argv[]) 
578 {
579     struct mlxd_foreach_action  ma;
580     int                         all = 0, i, ch, unit;
581
582     optreset = 1;
583     optind = 1;
584     while ((ch = getopt(argc, argv, "a")) != -1)
585         switch(ch) {
586         case 'a':
587             all = 1;
588             break;
589         default:
590             return(cmd_help(argc, argv));
591         }
592     argc -= optind;
593     argv += optind;
594
595     if (all) {
596         ma.func = detach_drive;
597         ma.arg = &unit;
598         for (i = 0; i < argc; i++) {
599             if ((unit = ctrlrunit(argv[i])) == -1) {
600                 warnx("'%s' is not a valid controller", argv[i]);
601             } else {
602                 mlxd_foreach_ctrlr(unit, &ma);
603             }
604         }
605     } else {
606         for (i = 0; i < argc; i++) {
607             if ((unit = driveunit(argv[i])) == -1) {
608                 warnx("'%s' is not a valid drive", argv[i]);
609             } else {
610                 /* run across all controllers to find this drive */
611                 mlx_foreach(detach_drive, &unit);
612             }
613         }
614     }
615     return(0);
616 }
617
618 /********************************************************************************
619  * Initiate a consistency check on a system drive.
620  *
621  * check [<drive>]
622  *      Start a check of <drive>
623  *
624  */
625 static int
626 cmd_check(int argc, char *argv[])
627 {
628     int         unit, fd, result;
629
630     if (argc != 2)
631         return(cmd_help(argc, argv));
632
633     if ((unit = driveunit(argv[1])) == -1) {
634         warnx("'%s' is not a valid drive", argv[1]);
635     } else {
636         
637         /* Get the device */
638         if ((fd = open(drivepath(unit), 0)) < 0) {
639             warn("can't open %s", drivepath(unit));
640         } else {
641             /* Try to start the check */
642             if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
643                 switch(result) {
644                 case 0x0002:
645                     warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
646                     break;
647                 case 0x0105:
648                     warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
649                     break;
650                 case 0x0106:
651                     warnx("drive rebuild or consistency check is already in progress on this controller");
652                     break;
653                 default:
654                     warn("ioctl MLXD_CHECKASYNC");
655                 }
656             }
657         }
658     }
659     return(0);
660 }
661
662 /********************************************************************************
663  * Initiate a physical drive rebuild
664  *
665  * rebuild <controller> <channel>:<target>
666  *      Start a rebuild of <controller>:<channel>:<target>
667  *
668  */
669 static int
670 cmd_rebuild(int argc, char *argv[])
671 {
672     struct mlx_rebuild_request  rb;
673     int                         unit, fd;
674
675     if (argc != 3)
676         return(cmd_help(argc, argv));
677
678     /* parse arguments */
679     if ((unit = ctrlrunit(argv[1])) == -1) {
680         warnx("'%s' is not a valid controller", argv[1]);
681         return(1);
682     }
683     /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
684     if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
685         (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {       
686         warnx("'%s' is not a valid physical drive", argv[2]);
687         return(1);
688     }
689     /* get the device */
690     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
691         warn("can't open %s", ctrlrpath(unit));
692         return(1);
693     }
694     /* try to start the rebuild */
695     if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
696         switch(rb.rr_status) {
697         case 0x0002:
698             warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
699             break;
700         case 0x0004:
701             warnx("drive failed during rebuild");
702             break;
703         case 0x0105:
704             warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
705             break;
706         case 0x0106:
707             warnx("drive rebuild or consistency check is already in progress on this controller");
708             break;
709         default:
710             warn("ioctl MLXD_REBUILDASYNC");
711         }
712     }
713     return(0);
714 }
715
716 /********************************************************************************
717  * Get the configuration from the selected controller.
718  *
719  * config <controller>
720  *              Print the configuration for <controller>
721  *
722  * XXX update to support adding/deleting drives.
723  */
724
725 int
726 cmd_config(int argc __unused, char *argv[] __unused)
727 {
728     struct conf_config  conf;
729     int                 unit = 0;       /* XXX */
730     int                 i, j;
731
732     bzero(&conf.cc_cfg, sizeof(conf.cc_cfg));
733     if (mlx_read_configuration(unit, &conf.cc_cfg)) {
734         printf("mlx%d: error submitting READ CONFIGURATION\n", unit);
735     } else {
736
737         printf("# Controller <INSERT DETAILS HERE>\n");
738         printf("#\n# Physical devices connected:\n");
739         for (i = 0; i < 5; i++)
740             for (j = 0; j < 16; j++)
741             print_phys_drive(&conf, i, j);
742         printf("#\n# System Drives defined:\n");
743
744         for (i = 0; i < conf.cc_cfg.cc_num_sys_drives; i++)
745             print_sys_drive(&conf, i);
746     }
747     return(0);
748 }
749
750 #ifdef SUPPORT_PAUSE
751 /********************************************************************************
752  * Pause one or more channels on a controller
753  *
754  * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
755  *              Pauses <channel> (or all channels) for <time> seconds after a
756  *              delay of <delay> seconds.
757  * pause <controller> -c
758  *              Cancels pending pause
759  */
760 static int
761 cmd_pause(int argc, char *argv[]) 
762 {
763     struct mlx_pause    mp;
764     int                 unit, i, ch, fd, cancel = 0;
765     char                *cp;
766     int                 oargc = argc;
767     char                **oargv = argv;
768
769     mp.mp_which = 0;
770     mp.mp_when = 30;
771     mp.mp_howlong = 30;
772     optreset = 1;
773     optind = 1;
774     while ((ch = getopt(argc, argv, "cd:t:")) != -1)
775         switch(ch) {
776         case 'c':
777             cancel = 1;
778             break;
779         case 'd':
780             mp.mp_when = strtol(optarg, &cp, 0);
781             if (*cp != 0)
782                 return(cmd_help(argc, argv));
783             break;
784         case 't':
785             mp.mp_howlong = strtol(optarg, &cp, 0);
786             if (*cp != 0)
787                 return(cmd_help(argc, argv));
788             break;
789         default:
790             return(cmd_help(argc, argv));
791         }
792     argc -= optind;
793     argv += optind;
794
795     /* get controller unit number that we're working on */
796     if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
797         return(cmd_help(oargc, oargv));
798
799     /* Get the device */
800     if ((fd = open(ctrlrpath(unit), 0)) < 0) {
801         warn("can't open %s", ctrlrpath(unit));
802         return(1);
803     }
804
805     if (argc == 1) {
806         /* controller-wide pause/cancel */
807         mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
808     } else {
809         for (i = 1; i < argc; i++) {
810             ch = strtol(argv[i], &cp, 0);
811             if (*cp != 0) {
812                 warnx("bad channel number '%s'", argv[i]);
813                 continue;
814             } else {
815                 mp.mp_which |= (1 << ch);
816             }
817         }
818     }
819     if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
820         warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
821     close(fd);
822     return(0);
823 }
824 #endif  /* SUPPORT_PAUSE */
825