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