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