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