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