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