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