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