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