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