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.2.2.1 2000/04/24 19:44:46 msmith Exp $ 27 * $DragonFly: src/usr.sbin/mlxcontrol/command.c,v 1.3 2003/08/08 04:18:46 dillon Exp $ 28 */ 29 30 #include <fcntl.h> 31 #include <paths.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 #include <err.h> 37 38 #include <dev/raid/mlx/mlxio.h> 39 #include <dev/raid/mlx/mlxreg.h> 40 41 #include "mlxcontrol.h" 42 43 static int cmd_status(int argc, char *argv[]); 44 static int cmd_rescan(int argc, char *argv[]); 45 static int cmd_detach(int argc, char *argv[]); 46 static int cmd_check(int argc, char *argv[]); 47 static int cmd_rebuild(int argc, char *argv[]); 48 #ifdef SUPPORT_PAUSE 49 static int cmd_pause(int argc, char *argv[]); 50 #endif 51 static int cmd_help(int argc, char *argv[]); 52 53 extern int cmd_config(int argc, char *argv[]); 54 55 56 struct 57 { 58 char *cmd; 59 int (*func)(int argc, char *argv[]); 60 char *desc; 61 char *text; 62 } commands[] = { 63 {"status", cmd_status, 64 "displays device status", 65 " status [-qv] [<drive>...]\n" 66 " Display status for <drive> or all drives if none is listed\n" 67 " -q Suppress output.\n" 68 " -v Display verbose information.\n" 69 " Returns 0 if all drives tested are online, 1 if one or more are\n" 70 " critical, and 2 if one or more are offline."}, 71 {"rescan", cmd_rescan, 72 "scan for new system drives", 73 " rescan <controller> [<controller>...]\n" 74 " Rescan <controller> for system drives.\n" 75 " rescan -a\n" 76 " Rescan all controllers for system drives."}, 77 {"detach", cmd_detach, 78 "detach system drives", 79 " detach <drive> [<drive>...]\n" 80 " Detaches <drive> from the controller.\n" 81 " detach -a <controller>\n" 82 " Detaches all drives on <controller>."}, 83 {"check", cmd_check, 84 "consistency-check a system drive", 85 " check <drive>\n" 86 " Requests a check and rebuild of the parity information on <drive>.\n" 87 " Note that each controller can only check one system drive at a time."}, 88 {"rebuild", cmd_rebuild, 89 "initiate a rebuild of a dead physical drive", 90 " rebuild <controller> <physdrive>\n" 91 " All system drives using space on the physical drive <physdrive>\n" 92 " are rebuilt, reconstructing all data on the drive.\n" 93 " Note that each controller can only perform one rebuild at a time."}, 94 #ifdef SUPPORT_PAUSE 95 {"pause", cmd_pause, 96 "pauses controller channels", 97 " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n" 98 " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n" 99 " all channels are paused.\n" 100 " <howlong> How long (seconds) to pause for (default 30).\n" 101 " <delay> How long (seconds) to wait before pausing (default 30).\n" 102 " pause <controller> -c\n" 103 " Cancels any pending pause operation on <controller>."}, 104 #endif 105 {"config", cmd_config, 106 "examine and update controller configuration", 107 " config <controller>\n" 108 " Print configuration for <controller>."}, 109 {"help", cmd_help, 110 "give help on usage", 111 ""}, 112 {NULL, NULL, NULL, NULL} 113 }; 114 115 /******************************************************************************** 116 * Command dispatch and global options parsing. 117 */ 118 119 int 120 main(int argc, char *argv[]) 121 { 122 int ch, i, oargc; 123 char **oargv; 124 125 oargc = argc; 126 oargv = argv; 127 while ((ch = getopt(argc, argv, "")) != -1) 128 switch(ch) { 129 default: 130 return(cmd_help(0, NULL)); 131 } 132 133 argc -= optind; 134 argv += optind; 135 136 if (argc > 0) 137 for (i = 0; commands[i].cmd != NULL; i++) 138 if (!strcmp(argv[0], commands[i].cmd)) 139 return(commands[i].func(argc, argv)); 140 141 return(cmd_help(oargc, oargv)); 142 } 143 144 /******************************************************************************** 145 * Helptext output 146 */ 147 static int 148 cmd_help(int argc, char *argv[]) 149 { 150 int i; 151 152 if (argc > 1) 153 for (i = 0; commands[i].cmd != NULL; i++) 154 if (!strcmp(argv[1], commands[i].cmd)) { 155 fprintf(stderr, "%s\n", commands[i].text); 156 fflush(stderr); 157 return(0); 158 } 159 160 if (argv != NULL) 161 fprintf(stderr, "Unknown command '%s'.\n", argv[1]); 162 fprintf(stderr, "Valid commands are:\n"); 163 for (i = 0; commands[i].cmd != NULL; i++) 164 fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc); 165 fflush(stderr); 166 return(0); 167 } 168 169 /******************************************************************************** 170 * Status output 171 * 172 * status [-qv] [<device> ...] 173 * Prints status for <device>, or all if none listed. 174 * 175 * -q Suppresses output, command returns 0 if devices are OK, 1 if one or 176 * more devices are critical, 2 if one or more devices are offline. 177 */ 178 static struct mlx_rebuild_status rs; 179 static int rs_ctrlr = -1; 180 static int status_result = 0; 181 182 /* XXX more verbosity! */ 183 static void 184 status_print(int unit, void *arg) 185 { 186 int verbosity = *(int *)arg; 187 int fd, result, ctrlr, sysdrive, statvalid; 188 189 /* Find which controller and what system drive we are */ 190 statvalid = 0; 191 if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) { 192 warnx("couldn't get controller/drive for %s", drivepath(unit)); 193 } else { 194 /* If we don't have rebuild stats for this controller, get them */ 195 if (rs_ctrlr == ctrlr) { 196 statvalid = 1; 197 } else { 198 if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) { 199 warn("can't open %s", ctrlrpath(ctrlr)); 200 } else { 201 if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) { 202 warn("ioctl MLX_REBUILDSTAT"); 203 } else { 204 rs_ctrlr = ctrlr; 205 statvalid = 1; 206 } 207 close(fd); 208 } 209 } 210 } 211 212 /* Get the device */ 213 if ((fd = open(drivepath(unit), 0)) < 0) { 214 warn("can't open %s", drivepath(unit)); 215 return; 216 } 217 218 /* Get its status */ 219 if (ioctl(fd, MLXD_STATUS, &result) < 0) { 220 warn("ioctl MLXD_STATUS"); 221 } else { 222 switch(result) { 223 case MLX_SYSD_ONLINE: 224 if (verbosity > 0) 225 printf("%s: online", drivename(unit)); 226 break; 227 case MLX_SYSD_CRITICAL: 228 if (verbosity > 0) 229 printf("%s: critical", drivename(unit)); 230 if (status_result < 1) 231 status_result = 1; 232 break; 233 case MLX_SYSD_OFFLINE: 234 if (verbosity > 0) 235 printf("%s: offline", drivename(unit)); 236 if (status_result < 2) 237 status_result = 2; 238 break; 239 default: 240 if (verbosity > 0) { 241 printf("%s: unknown status 0x%x", drivename(unit), result); 242 } 243 } 244 if (verbosity > 0) { 245 /* rebuild/check in progress on this drive? */ 246 if (statvalid && (rs_ctrlr == ctrlr) && 247 (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) { 248 switch(rs.rs_code) { 249 case MLX_REBUILDSTAT_REBUILDCHECK: 250 printf(" [consistency check"); 251 break; 252 case MLX_REBUILDSTAT_ADDCAPACITY: 253 printf(" [add capacity"); 254 break; 255 case MLX_REBUILDSTAT_ADDCAPACITYINIT: 256 printf(" [add capacity init"); 257 break; 258 default: 259 printf(" [unknown operation"); 260 } 261 printf(": %d/%d, %d%% complete]", 262 rs.rs_remaining, rs.rs_size, 263 ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100))); 264 } 265 printf("\n"); 266 } 267 } 268 close(fd); 269 } 270 271 static struct 272 { 273 int hwid; 274 char *name; 275 } mlx_controller_names[] = { 276 {0x01, "960P/PD"}, 277 {0x02, "960PL"}, 278 {0x10, "960PG"}, 279 {0x11, "960PJ"}, 280 {0x12, "960PR"}, 281 {0x13, "960PT"}, 282 {0x14, "960PTL0"}, 283 {0x15, "960PRL"}, 284 {0x16, "960PTL1"}, 285 {0x20, "1100PVX"}, 286 {-1, NULL} 287 }; 288 289 static void 290 controller_print(int unit, void *arg) 291 { 292 struct mlx_enquiry2 enq; 293 struct mlx_phys_drv pd; 294 int verbosity = *(int *)arg; 295 static char buf[80]; 296 char *model; 297 int i, channel, target; 298 299 if (verbosity == 0) 300 return; 301 302 /* fetch and print controller data */ 303 if (mlx_enquiry(unit, &enq)) { 304 printf("mlx%d: error submitting ENQUIRY2\n", unit); 305 } else { 306 307 for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) { 308 if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) { 309 model = mlx_controller_names[i].name; 310 break; 311 } 312 } 313 if (model == NULL) { 314 sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff); 315 model = buf; 316 } 317 318 printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n", 319 unit, model, 320 enq.me_actual_channels, 321 enq.me_actual_channels > 1 ? "s" : "", 322 enq.me_firmware_id & 0xff, 323 (enq.me_firmware_id >> 8) & 0xff, 324 (enq.me_firmware_id >> 16), 325 (enq.me_firmware_id >> 24) & 0xff, 326 enq.me_mem_size / (1024 * 1024)); 327 328 if (verbosity > 1) { 329 printf(" Hardware ID 0x%08x\n", enq.me_hardware_id); 330 printf(" Firmware ID 0x%08x\n", enq.me_firmware_id); 331 printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels, 332 enq.me_actual_channels); 333 printf(" Max Targets %d\n", enq.me_max_targets); 334 printf(" Max Tags %d\n", enq.me_max_tags); 335 printf(" Max System Drives %d\n", enq.me_max_sys_drives); 336 printf(" Max Arms %d\n", enq.me_max_arms); 337 printf(" Max Spans %d\n", enq.me_max_spans); 338 printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size, 339 enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size); 340 printf(" DRAM type %d\n", enq.me_mem_type); 341 printf(" Clock Speed %dns\n", enq.me_clock_speed); 342 printf(" Hardware Speed %dns\n", enq.me_hardware_speed); 343 printf(" Max Commands %d\n", enq.me_max_commands); 344 printf(" Max SG Entries %d\n", enq.me_max_sg); 345 printf(" Max DP %d\n", enq.me_max_dp); 346 printf(" Max IOD %d\n", enq.me_max_iod); 347 printf(" Max Comb %d\n", enq.me_max_comb); 348 printf(" Latency %ds\n", enq.me_latency); 349 printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout); 350 printf(" Min Free Lines %d\n", enq.me_min_freelines); 351 printf(" Rate Constant %d\n", enq.me_rate_const); 352 printf(" MAXBLK %d\n", enq.me_maxblk); 353 printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor); 354 printf(" Cache Line Size %d blocks\n", enq.me_cacheline); 355 printf(" SCSI Capability %s%dMHz, %d bit\n", 356 enq.me_scsi_cap & (1<<4) ? "differential " : "", 357 (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10, 358 8 << (enq.me_scsi_cap & 0x3)); 359 printf(" Firmware Build Number %d\n", enq.me_firmware_build); 360 printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type); 361 #if 0 362 printf(" Features %b\n", enq.me_firmware_features, 363 "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n"); 364 #endif 365 } 366 367 /* fetch and print physical drive data */ 368 for (channel = 0; channel < enq.me_configured_channels; channel++) { 369 for (target = 0; target < enq.me_max_targets; target++) { 370 if ((mlx_get_device_state(unit, channel, target, &pd) == 0) && 371 (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) { 372 mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1); 373 if (verbosity > 1) { 374 /* XXX print device statistics? */ 375 } 376 } 377 } 378 } 379 } 380 } 381 382 static int 383 cmd_status(int argc, char *argv[]) 384 { 385 int ch, verbosity = 1, i, unit; 386 387 optreset = 1; 388 optind = 1; 389 while ((ch = getopt(argc, argv, "qv")) != -1) 390 switch(ch) { 391 case 'q': 392 verbosity = 0; 393 break; 394 case 'v': 395 verbosity = 2; 396 break; 397 default: 398 return(cmd_help(argc, argv)); 399 } 400 argc -= optind; 401 argv += optind; 402 403 if (argc < 1) { 404 mlx_foreach(controller_print, &verbosity); 405 mlxd_foreach(status_print, &verbosity); 406 } else { 407 for (i = 0; i < argc; i++) { 408 if ((unit = driveunit(argv[i])) == -1) { 409 warnx("'%s' is not a valid drive", argv[i]); 410 } else { 411 status_print(unit, &verbosity); 412 } 413 } 414 } 415 return(status_result); 416 } 417 418 /******************************************************************************** 419 * Recscan for system drives on one or more controllers. 420 * 421 * rescan <controller> [<controller>...] 422 * rescan -a 423 */ 424 static void 425 rescan_ctrlr(int unit, void *junk) 426 { 427 int fd; 428 429 /* Get the device */ 430 if ((fd = open(ctrlrpath(unit), 0)) < 0) { 431 warn("can't open %s", ctrlrpath(unit)); 432 return; 433 } 434 435 if (ioctl(fd, MLX_RESCAN_DRIVES) < 0) 436 warn("can't rescan %s", ctrlrname(unit)); 437 close(fd); 438 } 439 440 static int 441 cmd_rescan(int argc, char *argv[]) 442 { 443 int all = 0, i, ch, unit; 444 445 optreset = 1; 446 optind = 1; 447 while ((ch = getopt(argc, argv, "a")) != -1) 448 switch(ch) { 449 case 'a': 450 all = 1; 451 break; 452 default: 453 return(cmd_help(argc, argv)); 454 } 455 argc -= optind; 456 argv += optind; 457 458 if (all) { 459 mlx_foreach(rescan_ctrlr, NULL); 460 } else { 461 for (i = 0; i < argc; i++) { 462 if ((unit = ctrlrunit(argv[i])) == -1) { 463 warnx("'%s' is not a valid controller", argv[i]); 464 } else { 465 rescan_ctrlr(unit, NULL); 466 } 467 } 468 } 469 return(0); 470 } 471 472 /******************************************************************************** 473 * Detach one or more system drives from a controller. 474 * 475 * detach <drive> [<drive>...] 476 * Detach <drive>. 477 * 478 * detach -a <controller> [<controller>...] 479 * Detach all drives on <controller>. 480 * 481 */ 482 static void 483 detach_drive(int unit, void *arg) 484 { 485 int fd; 486 487 /* Get the device */ 488 if ((fd = open(ctrlrpath(unit), 0)) < 0) { 489 warn("can't open %s", ctrlrpath(unit)); 490 return; 491 } 492 493 if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0) 494 warn("can't detach %s", drivename(unit)); 495 close(fd); 496 } 497 498 static int 499 cmd_detach(int argc, char *argv[]) 500 { 501 struct mlxd_foreach_action ma; 502 int all = 0, i, ch, unit; 503 504 optreset = 1; 505 optind = 1; 506 while ((ch = getopt(argc, argv, "a")) != -1) 507 switch(ch) { 508 case 'a': 509 all = 1; 510 break; 511 default: 512 return(cmd_help(argc, argv)); 513 } 514 argc -= optind; 515 argv += optind; 516 517 if (all) { 518 ma.func = detach_drive; 519 ma.arg = &unit; 520 for (i = 0; i < argc; i++) { 521 if ((unit = ctrlrunit(argv[i])) == -1) { 522 warnx("'%s' is not a valid controller", argv[i]); 523 } else { 524 mlxd_foreach_ctrlr(unit, &ma); 525 } 526 } 527 } else { 528 for (i = 0; i < argc; i++) { 529 if ((unit = driveunit(argv[i])) == -1) { 530 warnx("'%s' is not a valid drive", argv[i]); 531 } else { 532 /* run across all controllers to find this drive */ 533 mlx_foreach(detach_drive, &unit); 534 } 535 } 536 } 537 return(0); 538 } 539 540 /******************************************************************************** 541 * Initiate a consistency check on a system drive. 542 * 543 * check [<drive>] 544 * Start a check of <drive> 545 * 546 */ 547 static int 548 cmd_check(int argc, char *argv[]) 549 { 550 int unit, fd, result; 551 552 if (argc != 2) 553 return(cmd_help(argc, argv)); 554 555 if ((unit = driveunit(argv[1])) == -1) { 556 warnx("'%s' is not a valid drive", argv[1]); 557 } else { 558 559 /* Get the device */ 560 if ((fd = open(drivepath(unit), 0)) < 0) { 561 warn("can't open %s", drivepath(unit)); 562 } else { 563 /* Try to start the check */ 564 if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) { 565 switch(result) { 566 case 0x0002: 567 warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]); 568 break; 569 case 0x0105: 570 warnx("drive %s is invalid, or not a drive which can be checked", argv[1]); 571 break; 572 case 0x0106: 573 warnx("drive rebuild or consistency check is already in progress on this controller"); 574 break; 575 default: 576 warn("ioctl MLXD_CHECKASYNC"); 577 } 578 } 579 } 580 } 581 return(0); 582 } 583 584 /******************************************************************************** 585 * Initiate a physical drive rebuild 586 * 587 * rebuild <controller> <channel>:<target> 588 * Start a rebuild of <controller>:<channel>:<target> 589 * 590 */ 591 static int 592 cmd_rebuild(int argc, char *argv[]) 593 { 594 struct mlx_rebuild_request rb; 595 int unit, fd; 596 597 if (argc != 3) 598 return(cmd_help(argc, argv)); 599 600 /* parse arguments */ 601 if ((unit = ctrlrunit(argv[1])) == -1) { 602 warnx("'%s' is not a valid controller", argv[1]); 603 return(1); 604 } 605 /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */ 606 if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) && 607 (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) { 608 warnx("'%s' is not a valid physical drive", argv[2]); 609 return(1); 610 } 611 /* get the device */ 612 if ((fd = open(ctrlrpath(unit), 0)) < 0) { 613 warn("can't open %s", ctrlrpath(unit)); 614 return(1); 615 } 616 /* try to start the rebuild */ 617 if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) { 618 switch(rb.rr_status) { 619 case 0x0002: 620 warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target); 621 break; 622 case 0x0004: 623 warnx("drive failed during rebuild"); 624 break; 625 case 0x0105: 626 warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target); 627 break; 628 case 0x0106: 629 warnx("drive rebuild or consistency check is already in progress on this controller"); 630 break; 631 default: 632 warn("ioctl MLXD_CHECKASYNC"); 633 } 634 } 635 return(0); 636 } 637 638 #ifdef SUPPORT_PAUSE 639 /******************************************************************************** 640 * Pause one or more channels on a controller 641 * 642 * pause [-d <delay>] [-t <time>] <controller> [<channel>...] 643 * Pauses <channel> (or all channels) for <time> seconds after a 644 * delay of <delay> seconds. 645 * pause <controller> -c 646 * Cancels pending pause 647 */ 648 static int 649 cmd_pause(int argc, char *argv[]) 650 { 651 struct mlx_pause mp; 652 int unit, i, ch, fd, cancel = 0; 653 char *cp; 654 int oargc = argc; 655 char **oargv = argv; 656 657 mp.mp_which = 0; 658 mp.mp_when = 30; 659 mp.mp_howlong = 30; 660 optreset = 1; 661 optind = 1; 662 while ((ch = getopt(argc, argv, "cd:t:")) != -1) 663 switch(ch) { 664 case 'c': 665 cancel = 1; 666 break; 667 case 'd': 668 mp.mp_when = strtol(optarg, &cp, 0); 669 if (*cp != 0) 670 return(cmd_help(argc, argv)); 671 break; 672 case 't': 673 mp.mp_howlong = strtol(optarg, &cp, 0); 674 if (*cp != 0) 675 return(cmd_help(argc, argv)); 676 break; 677 default: 678 return(cmd_help(argc, argv)); 679 } 680 argc -= optind; 681 argv += optind; 682 683 /* get controller unit number that we're working on */ 684 if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1)) 685 return(cmd_help(oargc, oargv)); 686 687 /* Get the device */ 688 if ((fd = open(ctrlrpath(unit), 0)) < 0) { 689 warn("can't open %s", ctrlrpath(unit)); 690 return(1); 691 } 692 693 if (argc == 1) { 694 /* controller-wide pause/cancel */ 695 mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL; 696 } else { 697 for (i = 1; i < argc; i++) { 698 ch = strtol(argv[i], &cp, 0); 699 if (*cp != 0) { 700 warnx("bad channel number '%s'", argv[i]); 701 continue; 702 } else { 703 mp.mp_which |= (1 << ch); 704 } 705 } 706 } 707 if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0) 708 warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit)); 709 close(fd); 710 return(0); 711 } 712 #endif /* SUPPORT_PAUSE */ 713 714