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