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