1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <unistd.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <regex.h> 34 #include <sac.h> 35 #include <errno.h> 36 #include <dirent.h> 37 #include <limits.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <sys/wait.h> 41 #include <fcntl.h> 42 #include <devfsadm.h> 43 44 /* 45 * sacadm output parsing 46 */ 47 #define PMTAB_MAXLINE 512 48 #define PMTAB_SEPR ':' 49 #define PMTAB_DEVNAME_FIELD 7 /* field containing /dev/term/n */ 50 #define DIALOUT_SUFFIX ",cu" 51 #define DEVNAME_SEPR '/' 52 #define MN_SEPR ',' 53 #define MN_NULLCHAR '\0' 54 55 /* 56 * sacadm/pmadm exit codes (see /usr/include/sac.h) 57 */ 58 static char *sacerrs[] = { 59 "UNKNOWN", "Unknown exit code", 60 "E_BADARGS", "Invalid arguments", 61 "E_NOPRIV", "Not privileged", 62 "E_SAFERR", "SAF error", 63 "E_SYSERR", "System error", 64 "E_NOEXIST", "Entry does not exist", 65 "E_DUP", "Entry already exists", 66 "E_PMRUN", "Port monitor already running", 67 "E_PMNOTRUN", "Port monitor not running", 68 "E_RECOVER", "In recovery", 69 "E_SACNOTRUN", "SAC daemon not running", 70 }; 71 72 #define SAC_EXITVAL(x) ((x) >> 8) 73 #define SAC_EID(x) \ 74 (sacerrs[((uint_t)(x) > E_SACNOTRUN ? 0 : ((x)<<1))]) 75 #define SAC_EMSG(x) \ 76 (sacerrs[((uint_t)(x) > E_SACNOTRUN ? 1 : (((x)<<1) + 1))]) 77 78 79 80 /* 81 * create port monitors for each group of PM_GRPSZ port devices. 82 */ 83 #define PM_GRPSZ 64 84 85 /* 86 * compute port monitor # and base index 87 */ 88 #define PM_NUM(p) ((p) / PM_GRPSZ) 89 #define PM_SLOT(p) (PM_NUM(p) * PM_GRPSZ) 90 91 92 /* 93 * default maxports value 94 * override by setting SUNW_port_link.maxports in default/devfsadm 95 */ 96 #define MAXPORTS_DEFAULT 2048 97 98 /* 99 * command line buffer size for sacadm 100 */ 101 #define CMDLEN 1024 102 103 struct pm_alloc { 104 uint_t flags; 105 char *pm_tag; 106 }; 107 108 /* port monitor entry flags */ 109 #define PM_HAS_ENTRY 0x1 /* pm entry for this port */ 110 #define HAS_PORT_DEVICE 0x2 /* device exists */ 111 #define PORT_REMOVED 0x4 /* dangling port */ 112 #define HAS_PORT_MON 0x8 /* port monitor active */ 113 #define PM_NEEDED 0x10 /* port monitor needed */ 114 115 static int maxports; 116 static struct pm_alloc *pma; 117 static char *modname = "SUNW_port_link"; 118 119 /* 120 * devfsadm_print message id 121 */ 122 #define PORT_MID "SUNW_port_link" 123 124 /* 125 * enumeration regular expressions, port and onboard port devices 126 * On x86, /dev/term|cua/[a..z] namespace is split into 2: 127 * a-d are assigned based on minor name. e-z are 128 * assigned via enumeration. 129 */ 130 static devfsadm_enumerate_t port_rules[] = 131 {"^(term|cua)$/^([0-9]+)$", 1, MATCH_MINOR, "1"}; 132 133 #ifdef __i386 134 static devfsadm_enumerate_t obport_rules[] = 135 {"^(term|cua)$/^([e-z])$", 1, MATCH_MINOR, "1"}; 136 static char start_id[] = "e"; 137 #else 138 static devfsadm_enumerate_t obport_rules[] = 139 {"^(term|cua)$/^([a-z])$", 1, MATCH_MINOR, "1"}; 140 static char start_id[] = "a"; 141 #endif 142 143 static int serial_port_create(di_minor_t minor, di_node_t node); 144 static int onbrd_port_create(di_minor_t minor, di_node_t node); 145 static int dialout_create(di_minor_t minor, di_node_t node); 146 static int onbrd_dialout_create(di_minor_t minor, di_node_t node); 147 static int rsc_port_create(di_minor_t minor, di_node_t node); 148 static int lom_port_create(di_minor_t minor, di_node_t node); 149 static int pcmcia_port_create(di_minor_t minor, di_node_t node); 150 static int pcmcia_dialout_create(di_minor_t minor, di_node_t node); 151 static void rm_dangling_port(char *devname); 152 static void update_sacadm_db(void); 153 static int parse_portno(char *dname); 154 static int is_dialout(char *dname); 155 static int load_ttymondb(void); 156 static void remove_pm_entry(char *pmtag, int port); 157 static void add_pm_entry(int port); 158 static void delete_port_monitor(int port); 159 static void add_port_monitor(int port); 160 static int execute(const char *s); 161 static char *pmtab_parse_portname(char *cmdbuf); 162 static void *pma_alloc(void); 163 static void pma_free(void); 164 extern char *defread(char *varname); 165 166 /* 167 * devfs create callback register 168 */ 169 static devfsadm_create_t ports_cbt[] = { 170 {"pseudo", "ddi_pseudo", "su", 171 TYPE_EXACT | DRV_EXACT, ILEVEL_1, rsc_port_create}, 172 {"port", "ddi_serial:lomcon", "su", 173 TYPE_EXACT | DRV_EXACT, ILEVEL_1, lom_port_create}, 174 {"port", "ddi_serial", "pcser", 175 TYPE_EXACT | DRV_EXACT, ILEVEL_1, pcmcia_port_create}, 176 {"port", "ddi_serial:dialout", "pcser", 177 TYPE_EXACT | DRV_EXACT, ILEVEL_1, pcmcia_dialout_create}, 178 {"port", "ddi_serial", NULL, 179 TYPE_EXACT, ILEVEL_0, serial_port_create}, 180 {"port", "ddi_serial:mb", NULL, 181 TYPE_EXACT, ILEVEL_0, onbrd_port_create}, 182 {"port", "ddi_serial:dialout", NULL, 183 TYPE_EXACT, ILEVEL_0, dialout_create}, 184 {"port", "ddi_serial:dialout,mb", NULL, 185 TYPE_EXACT, ILEVEL_0, onbrd_dialout_create}, 186 }; 187 DEVFSADM_CREATE_INIT_V0(ports_cbt); 188 189 /* 190 * devfs cleanup register 191 * no cleanup rules for PCMCIA port devices 192 */ 193 static devfsadm_remove_t ports_remove_cbt[] = { 194 {"port", "^term/[0-9]+$", RM_PRE | RM_HOT, ILEVEL_0, rm_dangling_port}, 195 {"port", "^cua/[0-9]+$", RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all}, 196 {"port", "^(term|cua)/[a-z]$", 197 RM_PRE, ILEVEL_0, devfsadm_rm_all}, 198 }; 199 DEVFSADM_REMOVE_INIT_V0(ports_remove_cbt); 200 201 int 202 minor_init() 203 { 204 char *maxport_str; 205 206 maxport_str = defread("SUNW_port_link.maxports"); 207 208 if ((maxport_str == NULL) || 209 (sscanf(maxport_str, "%d", &maxports) != 1)) 210 maxports = MAXPORTS_DEFAULT; 211 212 devfsadm_print(CHATTY_MID, "%s: maximum number of port devices (%d)\n", 213 modname, maxports); 214 215 if (pma_alloc() == NULL) 216 return (DEVFSADM_FAILURE); 217 218 return (DEVFSADM_SUCCESS); 219 } 220 221 int 222 minor_fini() 223 { 224 /* 225 * update the sacadm database only if we are updating 226 * this platform (no -r option) 227 */ 228 if (strcmp(devfsadm_root_path(), "/") == 0) 229 update_sacadm_db(); 230 231 pma_free(); 232 233 return (DEVFSADM_SUCCESS); 234 } 235 236 /* 237 * Called for all serial devices that are NOT onboard 238 * Creates links of the form "/dev/term/[0..n]" 239 * Schedules an update the sacadm (portmon). 240 */ 241 static int 242 serial_port_create(di_minor_t minor, di_node_t node) 243 { 244 char l_path[MAXPATHLEN], p_path[MAXPATHLEN]; 245 char *devfspath, *buf, *minor_name; 246 int port_num; 247 248 devfspath = di_devfs_path(node); 249 if (devfspath == NULL) { 250 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 251 return (DEVFSADM_CONTINUE); 252 } 253 254 if ((minor_name = di_minor_name(minor)) == NULL) { 255 devfsadm_errprint("%s: NULL minor name\n\t%s\n", modname, 256 devfspath); 257 di_devfs_path_free(devfspath); 258 return (DEVFSADM_CONTINUE); 259 } 260 261 /* 262 * verify dialout ports do not come in on this nodetype 263 */ 264 if (is_dialout(minor_name)) { 265 devfsadm_errprint("%s: dialout device\n\t%s:%s\n", 266 modname, devfspath, minor_name); 267 di_devfs_path_free(devfspath); 268 return (DEVFSADM_CONTINUE); 269 } 270 271 /* 272 * add the minor name to the physical path so we can 273 * enum the port# and create the the link. 274 */ 275 (void) strcpy(p_path, devfspath); 276 (void) strcat(p_path, ":"); 277 (void) strcat(p_path, minor_name); 278 di_devfs_path_free(devfspath); 279 280 if (devfsadm_enumerate_int(p_path, 0, &buf, port_rules, 1)) { 281 devfsadm_errprint("%s:serial_port_create:" 282 " enumerate_int() failed\n\t%s\n", 283 modname, p_path); 284 return (DEVFSADM_CONTINUE); 285 } 286 287 (void) strcpy(l_path, "term/"); 288 (void) strcat(l_path, buf); 289 (void) devfsadm_mklink(l_path, node, minor, 0); 290 291 /* 292 * update the portmon database if this port falls within 293 * the valid range of ports. 294 */ 295 if ((port_num = parse_portno(buf)) != -1) { 296 pma[port_num].flags |= HAS_PORT_DEVICE; 297 } 298 299 free(buf); 300 return (DEVFSADM_CONTINUE); 301 } 302 303 /* 304 * Called for all dialout devices that are NOT onboard 305 * Creates links of the form "/dev/cua/[0..n]" 306 */ 307 static int 308 dialout_create(di_minor_t minor, di_node_t node) 309 { 310 char l_path[MAXPATHLEN], p_path[MAXPATHLEN]; 311 char *devfspath, *buf, *mn; 312 313 devfspath = di_devfs_path(node); 314 if (devfspath == NULL) { 315 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 316 return (DEVFSADM_CONTINUE); 317 } 318 319 if ((mn = di_minor_name(minor)) == NULL) { 320 devfsadm_errprint("%s: NULL minorname\n\t%s\n", 321 modname, devfspath); 322 di_devfs_path_free(devfspath); 323 return (DEVFSADM_CONTINUE); 324 } 325 326 if (!is_dialout(mn)) { 327 devfsadm_errprint("%s: invalid minor name\n\t%s:%s\n", 328 modname, devfspath, mn); 329 di_devfs_path_free(devfspath); 330 return (DEVFSADM_CONTINUE); 331 } 332 333 (void) strcpy(p_path, devfspath); 334 (void) strcat(p_path, ":"); 335 (void) strcat(p_path, mn); 336 di_devfs_path_free(devfspath); 337 338 if (devfsadm_enumerate_int(p_path, 0, &buf, port_rules, 1)) { 339 devfsadm_errprint("%s:dialout_create:" 340 " enumerate_int() failed\n\t%s\n", 341 modname, p_path); 342 return (DEVFSADM_CONTINUE); 343 } 344 (void) strcpy(l_path, "cua/"); 345 (void) strcat(l_path, buf); 346 347 /* 348 * add the minor name to the physical path so we can create 349 * the link. 350 */ 351 (void) devfsadm_mklink(l_path, node, minor, 0); 352 353 free(buf); 354 return (DEVFSADM_CONTINUE); 355 } 356 357 #ifdef __i386 358 359 static int 360 portcmp(char *devfs_path, char *phys_path) 361 { 362 char *p1, *p2; 363 int rv; 364 365 p2 = NULL; 366 367 p1 = strrchr(devfs_path, ':'); 368 if (p1 == NULL) 369 return (1); 370 371 p1 = strchr(p1, ','); 372 if (p1) 373 *p1 = '\0'; 374 375 p2 = strrchr(phys_path, ':'); 376 if (p2 == NULL) { 377 rv = -1; 378 goto out; 379 } 380 381 p2 = strchr(p2, ','); 382 if (p2) 383 *p2 = '\0'; 384 385 rv = strcmp(devfs_path, phys_path); 386 387 out: 388 if (p1) 389 *p1 = ','; 390 if (p2) 391 *p2 = ','; 392 393 return (rv); 394 } 395 396 /* 397 * If the minor name begins with [a-d] and the 398 * links in /dev/term/<char> and /dev/cua/<char> 399 * don't point at a different minor, then we can 400 * create compatibility links for this minor. 401 * Returns: 402 * port id if a compatibility link can be created. 403 * NULL otherwise 404 */ 405 static char * 406 check_compat_ports(char *phys_path, char *minor) 407 { 408 char portid = *minor; 409 char port[PATH_MAX]; 410 char *devfs_path; 411 412 if (portid < 'a' || portid > 'd') 413 return (NULL); 414 415 (void) snprintf(port, sizeof (port), "term/%c", portid); 416 if (devfsadm_read_link(port, &devfs_path) == DEVFSADM_SUCCESS && 417 portcmp(devfs_path, phys_path) != 0) { 418 free(devfs_path); 419 return (NULL); 420 } 421 422 free(devfs_path); 423 424 (void) snprintf(port, sizeof (port), "cua/%c", portid); 425 if (devfsadm_read_link(port, &devfs_path) == DEVFSADM_SUCCESS && 426 portcmp(devfs_path, phys_path) != 0) { 427 free(devfs_path); 428 return (NULL); 429 } 430 431 free(devfs_path); 432 433 /* 434 * Neither link exists or both links point at "phys_path" 435 * We can safely create compatibility links. 436 */ 437 port[0] = portid; 438 port[1] = '\0'; 439 440 return (s_strdup(port)); 441 } 442 443 #endif 444 445 /* 446 * Called for all Onboard serial devices 447 * Creates links of the form "/dev/term/[a..z]" 448 */ 449 static int 450 onbrd_port_create(di_minor_t minor, di_node_t node) 451 { 452 char l_path[MAXPATHLEN], p_path[MAXPATHLEN]; 453 char *devfspath, *buf, *minor_name; 454 455 devfspath = di_devfs_path(node); 456 if (devfspath == NULL) { 457 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 458 return (DEVFSADM_CONTINUE); 459 } 460 461 if ((minor_name = di_minor_name(minor)) == NULL) { 462 devfsadm_errprint("%s: NULL minor name\n\t%s\n", 463 modname, devfspath); 464 di_devfs_path_free(devfspath); 465 return (DEVFSADM_CONTINUE); 466 } 467 468 /* 469 * verify dialout ports do not come in on this nodetype 470 */ 471 if (is_dialout(minor_name)) { 472 devfsadm_errprint("%s: dialout device\n\t%s:%s\n", modname, 473 devfspath, minor_name); 474 di_devfs_path_free(devfspath); 475 return (DEVFSADM_CONTINUE); 476 } 477 478 (void) strcpy(p_path, devfspath); 479 (void) strcat(p_path, ":"); 480 (void) strcat(p_path, minor_name); 481 di_devfs_path_free(devfspath); 482 483 484 buf = NULL; 485 486 #ifdef __i386 487 buf = check_compat_ports(p_path, minor_name); 488 #endif 489 490 /* 491 * devfsadm_enumerate_char_start() is a private interface for use by the 492 * ports module only 493 */ 494 if (!buf && devfsadm_enumerate_char_start(p_path, 0, &buf, obport_rules, 495 1, start_id)) { 496 devfsadm_errprint("%s: devfsadm_enumerate_char_start() failed" 497 "\n\t%s\n", modname, p_path); 498 return (DEVFSADM_CONTINUE); 499 } 500 501 (void) strcpy(l_path, "term/"); 502 (void) strcat(l_path, buf); 503 (void) devfsadm_mklink(l_path, node, minor, 0); 504 free(buf); 505 return (DEVFSADM_CONTINUE); 506 } 507 508 /* 509 * Onboard dialout devices 510 * Creates links of the form "/dev/cua/[a..z]" 511 */ 512 static int 513 onbrd_dialout_create(di_minor_t minor, di_node_t node) 514 { 515 char l_path[MAXPATHLEN], p_path[MAXPATHLEN]; 516 char *devfspath, *buf, *mn; 517 518 devfspath = di_devfs_path(node); 519 if (devfspath == NULL) { 520 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 521 return (DEVFSADM_CONTINUE); 522 } 523 524 if ((mn = di_minor_name(minor)) == NULL) { 525 devfsadm_errprint("%s: NULL minor name\n\t%s\n", 526 modname, devfspath); 527 di_devfs_path_free(devfspath); 528 return (DEVFSADM_CONTINUE); 529 } 530 531 /* 532 * verify this is a dialout port 533 */ 534 if (!is_dialout(mn)) { 535 devfsadm_errprint("%s: not a dialout device\n\t%s:%s\n", 536 modname, devfspath, mn); 537 di_devfs_path_free(devfspath); 538 return (DEVFSADM_CONTINUE); 539 } 540 541 (void) strcpy(p_path, devfspath); 542 (void) strcat(p_path, ":"); 543 (void) strcat(p_path, mn); 544 di_devfs_path_free(devfspath); 545 546 buf = NULL; 547 548 #ifdef __i386 549 buf = check_compat_ports(p_path, mn); 550 #endif 551 552 /* 553 * devfsadm_enumerate_char_start() is a private interface 554 * for use by the ports module only. 555 */ 556 if (!buf && devfsadm_enumerate_char_start(p_path, 0, &buf, obport_rules, 557 1, start_id)) { 558 devfsadm_errprint("%s: devfsadm_enumerate_char_start() failed" 559 "\n\t%s\n", modname, p_path); 560 return (DEVFSADM_CONTINUE); 561 } 562 563 /* 564 * create the logical link 565 */ 566 (void) strcpy(l_path, "cua/"); 567 (void) strcat(l_path, buf); 568 (void) devfsadm_mklink(l_path, node, minor, 0); 569 free(buf); 570 return (DEVFSADM_CONTINUE); 571 } 572 573 574 /* 575 * Remote System Controller (RSC) serial ports 576 * Creates links of the form "/dev/rsc-control" | "/dev/term/rsc-console". 577 */ 578 static int 579 rsc_port_create(di_minor_t minor, di_node_t node) 580 { 581 char *devfspath; 582 char *minor_name; 583 584 585 devfspath = di_devfs_path(node); 586 if (devfspath == NULL) { 587 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 588 return (DEVFSADM_CONTINUE); 589 } 590 591 if ((minor_name = di_minor_name(minor)) == NULL) { 592 devfsadm_errprint("%s: NULL minor name\n\t%s\n", 593 modname, devfspath); 594 di_devfs_path_free(devfspath); 595 return (DEVFSADM_CONTINUE); 596 } 597 598 /* 599 * if this is the RSC console serial port (i.e. the minor name == ssp), 600 * create /dev/term/rsc-console link and then we are done with this 601 * node. 602 */ 603 if (strcmp(minor_name, "ssp") == 0) { 604 (void) devfsadm_mklink("term/rsc-console", node, minor, 0); 605 di_devfs_path_free(devfspath); 606 return (DEVFSADM_TERMINATE); 607 608 /* 609 * else if this is the RSC control serial port (i.e. the minor name == 610 * sspctl), create /dev/rsc-control link and then we are done with this 611 * node. 612 */ 613 } else if (strcmp(minor_name, "sspctl") == 0) { 614 (void) devfsadm_mklink("rsc-control", node, minor, 0); 615 di_devfs_path_free(devfspath); 616 return (DEVFSADM_TERMINATE); 617 } 618 619 /* This is not an RSC node, continue... */ 620 di_devfs_path_free(devfspath); 621 return (DEVFSADM_CONTINUE); 622 } 623 624 /* 625 * Lights Out Management (LOM) serial ports 626 * Creates links of the form "/dev/term/lom-console". 627 */ 628 static int 629 lom_port_create(di_minor_t minor, di_node_t node) 630 { 631 char *devfspath; 632 char *minor_name; 633 634 devfspath = di_devfs_path(node); 635 if (devfspath == NULL) { 636 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 637 return (DEVFSADM_CONTINUE); 638 } 639 640 if ((minor_name = di_minor_name(minor)) == NULL) { 641 devfsadm_errprint("%s: NULL minor name\n\t%s\n", 642 modname, devfspath); 643 di_devfs_path_free(devfspath); 644 return (DEVFSADM_CONTINUE); 645 } 646 647 /* 648 * if this is the LOM console serial port (i.e. the minor 649 * name == lom-console ), create /dev/term/lom-console link and 650 * then we are done with this node. 651 */ 652 if (strcmp(minor_name, "lom-console") == 0) { 653 (void) devfsadm_mklink("term/lom-console", node, minor, 0); 654 di_devfs_path_free(devfspath); 655 return (DEVFSADM_TERMINATE); 656 } 657 658 /* This is not a LOM node, continue... */ 659 di_devfs_path_free(devfspath); 660 return (DEVFSADM_CONTINUE); 661 } 662 663 /* 664 * PCMCIA serial ports 665 * Creates links of the form "/dev/term/pcN", where N is the PCMCIA 666 * socket # the device is plugged into. 667 */ 668 #define PCMCIA_MAX_SOCKETS 64 669 #define PCMCIA_SOCKETNO(x) ((x) & (PCMCIA_MAX_SOCKETS - 1)) 670 671 static int 672 pcmcia_port_create(di_minor_t minor, di_node_t node) 673 { 674 char l_path[MAXPATHLEN]; 675 char *devfspath; 676 int socket, *intp; 677 678 devfspath = di_devfs_path(node); 679 if (devfspath == NULL) { 680 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 681 return (DEVFSADM_TERMINATE); 682 } 683 684 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "socket", &intp) <= 0) { 685 devfsadm_errprint("%s: failed pcmcia socket lookup\n\t%s\n", 686 modname, devfspath); 687 di_devfs_path_free(devfspath); 688 return (DEVFSADM_TERMINATE); 689 } 690 691 socket = PCMCIA_SOCKETNO(*intp); 692 693 di_devfs_path_free(devfspath); 694 695 (void) sprintf(l_path, "term/pc%d", socket); 696 (void) devfsadm_mklink(l_path, node, minor, 0); 697 698 return (DEVFSADM_TERMINATE); 699 } 700 701 /* 702 * PCMCIA dialout serial ports 703 * Creates links of the form "/dev/cua/pcN", where N is the PCMCIA 704 * socket number the device is plugged into. 705 */ 706 static int 707 pcmcia_dialout_create(di_minor_t minor, di_node_t node) 708 { 709 char l_path[MAXPATHLEN]; 710 char *devfspath; 711 int socket, *intp; 712 713 devfspath = di_devfs_path(node); 714 if (devfspath == NULL) { 715 devfsadm_errprint("%s: di_devfs_path() failed\n", modname); 716 return (DEVFSADM_TERMINATE); 717 } 718 719 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "socket", &intp) <= 0) { 720 devfsadm_errprint("%s: failed socket lookup\n\t%s\n", 721 modname, devfspath); 722 di_devfs_path_free(devfspath); 723 return (DEVFSADM_TERMINATE); 724 } 725 726 socket = PCMCIA_SOCKETNO(*intp); 727 728 di_devfs_path_free(devfspath); 729 (void) sprintf(l_path, "cua/pc%d", socket); 730 (void) devfsadm_mklink(l_path, node, minor, 0); 731 732 return (DEVFSADM_TERMINATE); 733 } 734 735 736 /* 737 * Removes port entries that no longer have devices 738 * backing them 739 * Schedules an update the sacadm (portmon) database 740 */ 741 static void 742 rm_dangling_port(char *devname) 743 { 744 char *portstr; 745 int portnum; 746 747 devfsadm_print(PORT_MID, "%s:rm_stale_port: %s\n", 748 modname, devname); 749 750 if ((portstr = strrchr(devname, (int)'/')) == NULL) { 751 devfsadm_errprint("%s: invalid name: %s\n", 752 modname, devname); 753 return; 754 } 755 portstr++; 756 757 /* 758 * mark for removal from sacadm database 759 */ 760 if ((portnum = parse_portno(portstr)) != -1) 761 pma[portnum].flags |= PORT_REMOVED; 762 763 devfsadm_rm_all(devname); 764 } 765 766 /* 767 * Algorithm is to step through ports; checking for unneeded PM entries 768 * entries that should be there but are not. Every PM_GRPSZ entries 769 * check to see if there are any entries for the port monitor group; 770 * if not, delete the group. 771 */ 772 static void 773 update_sacadm_db(void) 774 { 775 int i; 776 777 if (load_ttymondb() != DEVFSADM_SUCCESS) 778 return; 779 780 for (i = 0; i < maxports; i++) { 781 /* 782 * if this port was removed and has a port 783 * monitor entry, remove the entry from the sacadm db 784 */ 785 if ((pma[i].flags & PORT_REMOVED) != 0) { 786 if ((pma[i].flags & PM_HAS_ENTRY) != 0) 787 remove_pm_entry(pma[i].pm_tag, i); 788 } 789 790 /* 791 * if this port is present and lacks a port monitor 792 * add an entry to the sacadm db 793 */ 794 if (pma[i].flags & HAS_PORT_DEVICE) { 795 if (!(pma[i].flags & PM_HAS_ENTRY)) 796 add_pm_entry(i); 797 } 798 799 /* 800 * if this port has a pm entry, mark as needing 801 * a port monitor within this range of ports 802 */ 803 if ((pma[i].flags & PM_HAS_ENTRY)) 804 pma[PM_SLOT(i)].flags |= PM_NEEDED; 805 806 /* 807 * continue for the range of ports per-portmon 808 */ 809 if (((i + 1) % PM_GRPSZ) != 0) 810 continue; 811 812 /* 813 * if there are no ports active on the range we have 814 * just completed, remove the port monitor entry if 815 * it exists 816 */ 817 if ((pma[PM_SLOT(i)].flags & (PM_NEEDED | HAS_PORT_MON)) == 818 HAS_PORT_MON) { 819 delete_port_monitor(i); 820 } 821 822 } 823 824 /* 825 * cleanup remaining port monitor, if active 826 */ 827 if ((i % PM_GRPSZ != 0) && 828 ((pma[PM_SLOT(i)].flags & (PM_NEEDED | HAS_PORT_MON)) == 829 HAS_PORT_MON)) { 830 delete_port_monitor(i); 831 } 832 } 833 834 /* 835 * Determine which port monitor entries already exist by invoking pmadm(1m) 836 * to list all configured 'ttymon' port monitor entries. 837 * Do not explicitly report errors from executing pmadm(1m) or sacadm(1m) 838 * commands to remain compatible with the ports(1m) implementation. 839 */ 840 static int 841 load_ttymondb(void) 842 { 843 char cmdline[CMDLEN]; 844 char cmdbuf[PMTAB_MAXLINE+1]; 845 int sac_exitval; 846 FILE *fs_popen; 847 char *portname; /* pointer to a tty name */ 848 int portnum; 849 char *ptr; 850 char *error_msg = "%s: failed to load port monitor database\n"; 851 852 (void) strcpy(cmdline, "/usr/sbin/pmadm -L -t ttymon"); 853 fs_popen = popen(cmdline, "r"); 854 if (fs_popen == NULL) { 855 devfsadm_print(VERBOSE_MID, error_msg, modname); 856 return (DEVFSADM_FAILURE); 857 } 858 859 while (fgets(cmdbuf, PMTAB_MAXLINE, fs_popen) != NULL) { 860 if ((portname = pmtab_parse_portname(cmdbuf)) == NULL) { 861 devfsadm_print(VERBOSE_MID, 862 "load_ttymondb: failed to parse portname\n"); 863 devfsadm_print(VERBOSE_MID, 864 "load_ttymondb: buffer \"%s\"\n", cmdbuf); 865 goto load_failed; 866 } 867 868 devfsadm_print(PORT_MID, "%s:load_ttymondb: port %s ", 869 modname, portname); 870 871 /* 872 * skip onboard ports 873 * There is no reliable way to determine if we 874 * should start a port monitor on these lines. 875 */ 876 if ((portnum = parse_portno(portname)) == -1) { 877 devfsadm_print(PORT_MID, "ignored\n"); 878 continue; 879 } 880 881 /* 882 * the first field of the pmadm output is 883 * the port monitor name for this entry 884 */ 885 if ((ptr = strchr(cmdbuf, PMTAB_SEPR)) == NULL) { 886 devfsadm_print(VERBOSE_MID, 887 "load_ttymondb: no portmon tag\n"); 888 goto load_failed; 889 } 890 891 *ptr = MN_NULLCHAR; 892 if ((pma[portnum].pm_tag = strdup(cmdbuf)) == NULL) { 893 devfsadm_errprint("load_ttymondb: failed strdup\n"); 894 goto load_failed; 895 } 896 pma[portnum].flags |= PM_HAS_ENTRY; 897 pma[PM_SLOT(portnum)].flags |= HAS_PORT_MON; 898 devfsadm_print(PORT_MID, "present\n"); 899 } 900 (void) pclose(fs_popen); 901 return (DEVFSADM_SUCCESS); 902 903 load_failed: 904 905 /* 906 * failed to load the port monitor database 907 */ 908 devfsadm_print(VERBOSE_MID, error_msg, modname); 909 sac_exitval = SAC_EXITVAL(pclose(fs_popen)); 910 if (sac_exitval != 0) { 911 devfsadm_print(VERBOSE_MID, 912 "pmadm: (%s) %s\n", SAC_EID(sac_exitval), 913 SAC_EMSG(sac_exitval)); 914 } 915 return (DEVFSADM_FAILURE); 916 } 917 918 /* 919 * add a port monitor entry for device /dev/term/"port" 920 */ 921 static void 922 add_pm_entry(int port) 923 { 924 char cmdline[CMDLEN]; 925 int sac_exitval; 926 927 add_port_monitor(port); 928 (void) sprintf(cmdline, 929 "/usr/sbin/pmadm -a -p ttymon%d -s %d -i root" 930 " -v `/usr/sbin/ttyadm -V` -fux -y\"/dev/term/%d\"" 931 " -m \"`/usr/sbin/ttyadm -d /dev/term/%d -s /usr/bin/login" 932 " -l 9600 -p \\\"login: \\\"`\"", PM_NUM(port), port, port, port); 933 934 if (devfsadm_noupdate() == DEVFSADM_FALSE) { 935 sac_exitval = execute(cmdline); 936 if ((sac_exitval != 0) && (sac_exitval != E_SACNOTRUN)) { 937 devfsadm_print(VERBOSE_MID, 938 "failed to add port monitor entry" 939 " for /dev/term/%d\n", port); 940 devfsadm_print(VERBOSE_MID, "pmadm: (%s) %s\n", 941 SAC_EID(sac_exitval), SAC_EMSG(sac_exitval)); 942 } 943 } 944 pma[port].flags |= PM_HAS_ENTRY; 945 devfsadm_print(VERBOSE_MID, "%s: /dev/term/%d added to sacadm\n", 946 modname, port); 947 } 948 949 static void 950 remove_pm_entry(char *pmtag, int port) 951 { 952 953 char cmdline[CMDLEN]; 954 int sac_exitval; 955 956 if (devfsadm_noupdate() == DEVFSADM_FALSE) { 957 (void) snprintf(cmdline, sizeof (cmdline), 958 "/usr/sbin/pmadm -r -p %s -s %d", pmtag, port); 959 sac_exitval = execute(cmdline); 960 if ((sac_exitval != 0) && (sac_exitval != E_SACNOTRUN)) { 961 devfsadm_print(VERBOSE_MID, 962 "failed to remove port monitor entry" 963 " for /dev/term/%d\n", port); 964 devfsadm_print(VERBOSE_MID, "pmadm: (%s) %s\n", 965 SAC_EID(sac_exitval), SAC_EMSG(sac_exitval)); 966 } 967 } 968 pma[port].flags &= ~PM_HAS_ENTRY; 969 devfsadm_print(VERBOSE_MID, "%s: /dev/term/%d removed from sacadm\n", 970 modname, port); 971 } 972 973 974 /* 975 * delete_port_monitor() 976 * Check for the existence of a port monitor for "port" and remove it if 977 * one exists 978 */ 979 static void 980 delete_port_monitor(int port) 981 { 982 char cmdline[CMDLEN]; 983 int sac_exitval; 984 985 (void) sprintf(cmdline, "/usr/sbin/sacadm -L -p ttymon%d", 986 PM_NUM(port)); 987 sac_exitval = execute(cmdline); 988 989 /* clear the PM tag and return if the port monitor is not active */ 990 if (sac_exitval == E_NOEXIST) { 991 pma[PM_SLOT(port)].flags &= ~HAS_PORT_MON; 992 return; 993 } 994 995 /* some other sacadm(1m) error, log and return */ 996 if (sac_exitval != 0) { 997 devfsadm_print(VERBOSE_MID, "sacadm: (%s) %s\n", 998 SAC_EID(sac_exitval), SAC_EMSG(sac_exitval)); 999 return; 1000 } 1001 1002 if (devfsadm_noupdate() == DEVFSADM_FALSE) { 1003 (void) sprintf(cmdline, 1004 "/usr/sbin/sacadm -r -p ttymon%d", PM_NUM(port)); 1005 if (sac_exitval = execute(cmdline)) { 1006 devfsadm_print(VERBOSE_MID, 1007 "failed to remove port monitor ttymon%d\n", 1008 PM_NUM(port)); 1009 devfsadm_print(VERBOSE_MID, "sacadm: (%s) %s\n", 1010 SAC_EID(sac_exitval), SAC_EMSG(sac_exitval)); 1011 } 1012 } 1013 devfsadm_print(VERBOSE_MID, "%s: port monitor ttymon%d removed\n", 1014 modname, PM_NUM(port)); 1015 pma[PM_SLOT(port)].flags &= ~HAS_PORT_MON; 1016 } 1017 1018 static void 1019 add_port_monitor(int port) 1020 { 1021 char cmdline[CMDLEN]; 1022 int sac_exitval; 1023 1024 if ((pma[PM_SLOT(port)].flags & HAS_PORT_MON) != 0) { 1025 return; 1026 } 1027 1028 (void) sprintf(cmdline, 1029 "/usr/sbin/sacadm -l -p ttymon%d", PM_NUM(port)); 1030 sac_exitval = execute(cmdline); 1031 if (sac_exitval == E_NOEXIST) { 1032 (void) sprintf(cmdline, 1033 "/usr/sbin/sacadm -a -n 2 -p ttymon%d -t ttymon" 1034 " -c /usr/lib/saf/ttymon -v \"`/usr/sbin/ttyadm" 1035 " -V`\" -y \"Ports %d-%d\"", PM_NUM(port), PM_SLOT(port), 1036 PM_SLOT(port) + (PM_GRPSZ - 1)); 1037 if (devfsadm_noupdate() == DEVFSADM_FALSE) { 1038 if (sac_exitval = execute(cmdline)) { 1039 devfsadm_print(VERBOSE_MID, 1040 "failed to add port monitor ttymon%d\n", 1041 PM_NUM(port)); 1042 devfsadm_print(VERBOSE_MID, "sacadm: (%s) %s\n", 1043 SAC_EID(sac_exitval), 1044 SAC_EMSG(sac_exitval)); 1045 } 1046 } 1047 devfsadm_print(VERBOSE_MID, "%s: port monitor ttymon%d added\n", 1048 modname, PM_NUM(port)); 1049 } 1050 pma[PM_SLOT(port)].flags |= HAS_PORT_MON; 1051 } 1052 1053 /* 1054 * parse port number from string 1055 * returns port number if in range [0..maxports] 1056 */ 1057 static int 1058 parse_portno(char *dname) 1059 { 1060 int pn; 1061 1062 if (sscanf(dname, "%d", &pn) != 1) 1063 return (-1); 1064 1065 if ((pn < 0) || (pn > maxports)) { 1066 devfsadm_print(VERBOSE_MID, 1067 "%s:parse_portno: %d not in range (0..%d)\n", 1068 modname, pn, maxports); 1069 return (-1); 1070 } 1071 1072 return (pn); 1073 } 1074 1075 1076 /* 1077 * fork and exec a command, waiting for the command to 1078 * complete and return it's status 1079 */ 1080 static int 1081 execute(const char *s) 1082 { 1083 int status; 1084 int fd; 1085 pid_t pid; 1086 pid_t w; 1087 1088 /* 1089 * fork a single threaded child proc to execute the 1090 * sacadm command string 1091 */ 1092 devfsadm_print(PORT_MID, "%s: execute:\n\t%s\n", modname, s); 1093 if ((pid = fork1()) == 0) { 1094 (void) close(0); 1095 (void) close(1); 1096 (void) close(2); 1097 fd = open("/dev/null", O_RDWR); 1098 (void) dup(fd); 1099 (void) dup(fd); 1100 (void) execl("/sbin/sh", "sh", "-c", s, 0); 1101 /* 1102 * return the sacadm exit status (see _exit(2)) 1103 */ 1104 _exit(127); 1105 } 1106 1107 /* 1108 * wait for child process to terminate 1109 */ 1110 for (;;) { 1111 w = wait(&status); 1112 if (w == pid) { 1113 devfsadm_print(PORT_MID, "%s:exit status (%d)\n", 1114 modname, SAC_EXITVAL(status)); 1115 return (SAC_EXITVAL(status)); 1116 } 1117 if (w == (pid_t)-1) { 1118 devfsadm_print(VERBOSE_MID, "%s: exec failed\n", 1119 modname); 1120 return (-1); 1121 } 1122 } 1123 1124 /* NOTREACHED */ 1125 } 1126 1127 1128 /* 1129 * check if the minor name is suffixed with ",cu" 1130 */ 1131 static int 1132 is_dialout(char *name) 1133 { 1134 char *s_chr; 1135 1136 if ((name == NULL) || (s_chr = strrchr(name, MN_SEPR)) == NULL) 1137 return (0); 1138 1139 if (strcmp(s_chr, DIALOUT_SUFFIX) == 0) { 1140 return (1); 1141 } else { 1142 return (0); 1143 } 1144 } 1145 1146 1147 /* 1148 * Get the name of the port device from a pmtab entry. 1149 * Note the /dev/term/ part is taken off. 1150 */ 1151 static char * 1152 pmtab_parse_portname(char *buffer) 1153 { 1154 int i; 1155 char *bufp, *devnamep, *portnamep; 1156 1157 /* 1158 * position to the device name (field 8) 1159 */ 1160 bufp = strchr(buffer, PMTAB_SEPR); 1161 for (i = 0; i < PMTAB_DEVNAME_FIELD; i++) { 1162 if (bufp == NULL) 1163 return (NULL); 1164 bufp = strchr(++bufp, PMTAB_SEPR); 1165 } 1166 1167 /* move past the ':' and locate the end of the devname */ 1168 devnamep = bufp++; 1169 if ((bufp = strchr(bufp, PMTAB_SEPR)) == NULL) 1170 return (NULL); 1171 1172 *bufp = MN_NULLCHAR; 1173 if ((portnamep = strrchr(devnamep, DEVNAME_SEPR)) == NULL) { 1174 *bufp = PMTAB_SEPR; 1175 return (NULL); 1176 } 1177 1178 /* return with "buffer" chopped after the /dev/term entry */ 1179 1180 return (++portnamep); 1181 } 1182 1183 /* 1184 * port monitor array mgmt 1185 */ 1186 static void * 1187 pma_alloc(void) 1188 { 1189 1190 if (pma != NULL) { 1191 devfsadm_errprint("%s:pma_alloc:pma != NULL\n", modname); 1192 return (NULL); 1193 } 1194 1195 if ((pma = calloc(maxports + 1, sizeof (*pma))) == NULL) { 1196 devfsadm_errprint("%s:pma_alloc:pma alloc failure\n", modname); 1197 return (NULL); 1198 } 1199 1200 return ((void *)pma); 1201 } 1202 1203 static void 1204 pma_free(void) 1205 { 1206 1207 int i; 1208 1209 if (pma == NULL) 1210 return; 1211 1212 /* 1213 * free any strings we had allocated 1214 */ 1215 for (i = 0; i <= maxports; i++) { 1216 if (pma[i].pm_tag != NULL) 1217 free(pma[i].pm_tag); 1218 } 1219 1220 free(pma); 1221 pma = NULL; 1222 } 1223