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