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