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.
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.
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]
23 * Copyright 2013 Garrett D'Amore <garrett@damore.org>
24 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
36 #include <sys/types.h>
44 * sacadm output parsing
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 '/'
52 #define MN_NULLCHAR '\0'
55 * sacadm/pmadm exit codes (see /usr/include/sac.h)
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",
71 #define SAC_EXITVAL(x) ((x) >> 8)
73 (sacerrs[((uint_t)(x) > E_SACNOTRUN ? 0 : ((x)<<1))])
75 (sacerrs[((uint_t)(x) > E_SACNOTRUN ? 1 : (((x)<<1) + 1))])
80 * create port monitors for each group of PM_GRPSZ port devices.
85 * compute port monitor # and base index
87 #define PM_NUM(p) ((p) / PM_GRPSZ)
88 #define PM_SLOT(p) (PM_NUM(p) * PM_GRPSZ)
92 * default maxports value
93 * override by setting SUNW_port_link.maxports in default/devfsadm
95 #define MAXPORTS_DEFAULT 2048
98 * command line buffer size for sacadm
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 */
115 static struct pm_alloc
*pma
;
116 static char *modname
= "SUNW_port_link";
119 * devfsadm_print message id
121 #define PORT_MID "SUNW_port_link"
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.
129 static devfsadm_enumerate_t port_rules
[] =
130 {"^(term|cua)$/^([0-9]+)$", 1, MATCH_MINOR
, "1"};
133 static devfsadm_enumerate_t obport_rules
[] =
134 {"^(term|cua)$/^([e-z])$", 1, MATCH_MINOR
, "1"};
135 static char start_id
[] = "e";
137 static devfsadm_enumerate_t obport_rules
[] =
138 {"^(term|cua)$/^([a-z])$", 1, MATCH_MINOR
, "1"};
139 static char start_id
[] = "a";
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
);
165 * devfs create callback register
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
},
181 DEVFSADM_CREATE_INIT_V0(ports_cbt
);
184 * devfs cleanup register
185 * no cleanup rules for PCMCIA port devices
187 static devfsadm_remove_t ports_remove_cbt
[] = {
188 {"port", "^term/[0-9]+$", RM_PRE
| RM_ALWAYS
| RM_HOT
, ILEVEL_0
,
190 {"port", "^cua/[0-9]+$", RM_PRE
| RM_ALWAYS
| RM_HOT
, ILEVEL_0
,
192 {"port", "^(term|cua)/[a-z]$",
193 RM_PRE
| RM_ALWAYS
, ILEVEL_0
, devfsadm_rm_all
},
195 DEVFSADM_REMOVE_INIT_V0(ports_remove_cbt
);
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
;
208 /* close defaults file */
209 (void) defopen(NULL
);
211 maxports
= MAXPORTS_DEFAULT
;
214 devfsadm_print(CHATTY_MID
, "%s: maximum number of port devices (%d)\n",
217 if (pma_alloc() == NULL
)
218 return (DEVFSADM_FAILURE
);
220 return (DEVFSADM_SUCCESS
);
227 * update the sacadm database only if we are updating
228 * this platform (no -r option)
230 if (strcmp(devfsadm_root_path(), "/") == 0)
235 return (DEVFSADM_SUCCESS
);
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).
244 serial_port_create(di_minor_t minor
, di_node_t node
)
246 char l_path
[MAXPATHLEN
], p_path
[MAXPATHLEN
];
247 char *devfspath
, *buf
, *minor_name
;
250 devfspath
= di_devfs_path(node
);
251 if (devfspath
== NULL
) {
252 devfsadm_errprint("%s: di_devfs_path() failed\n", modname
);
253 return (DEVFSADM_CONTINUE
);
256 if ((minor_name
= di_minor_name(minor
)) == NULL
) {
257 devfsadm_errprint("%s: NULL minor name\n\t%s\n", modname
,
259 di_devfs_path_free(devfspath
);
260 return (DEVFSADM_CONTINUE
);
264 * verify dialout ports do not come in on this nodetype
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
);
274 * add the minor name to the physical path so we can
275 * enum the port# and create the link.
277 (void) strcpy(p_path
, devfspath
);
278 (void) strcat(p_path
, ":");
279 (void) strcat(p_path
, minor_name
);
280 di_devfs_path_free(devfspath
);
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",
286 return (DEVFSADM_CONTINUE
);
289 (void) strcpy(l_path
, "term/");
290 (void) strcat(l_path
, buf
);
291 (void) devfsadm_mklink(l_path
, node
, minor
, 0);
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 ..
299 (void) syslog(LOG_INFO
, "serial device /dev/%s present", l_path
);
302 * update the portmon database if this port falls within
303 * the valid range of ports.
305 if ((port_num
= parse_portno(buf
)) != -1) {
306 pma
[port_num
].flags
|= HAS_PORT_DEVICE
;
310 return (DEVFSADM_CONTINUE
);
314 * Called for all dialout devices that are NOT onboard
315 * Creates links of the form "/dev/cua/[0..n]"
318 dialout_create(di_minor_t minor
, di_node_t node
)
320 char l_path
[MAXPATHLEN
], p_path
[MAXPATHLEN
];
321 char *devfspath
, *buf
, *mn
;
323 devfspath
= di_devfs_path(node
);
324 if (devfspath
== NULL
) {
325 devfsadm_errprint("%s: di_devfs_path() failed\n", modname
);
326 return (DEVFSADM_CONTINUE
);
329 if ((mn
= di_minor_name(minor
)) == NULL
) {
330 devfsadm_errprint("%s: NULL minorname\n\t%s\n",
332 di_devfs_path_free(devfspath
);
333 return (DEVFSADM_CONTINUE
);
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
);
343 (void) strcpy(p_path
, devfspath
);
344 (void) strcat(p_path
, ":");
345 (void) strcat(p_path
, mn
);
346 di_devfs_path_free(devfspath
);
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",
352 return (DEVFSADM_CONTINUE
);
354 (void) strcpy(l_path
, "cua/");
355 (void) strcat(l_path
, buf
);
358 * add the minor name to the physical path so we can create
361 (void) devfsadm_mklink(l_path
, node
, minor
, 0);
364 return (DEVFSADM_CONTINUE
);
370 portcmp(char *devfs_path
, char *phys_path
)
377 p1
= strrchr(devfs_path
, ':');
381 p1
= strchr(p1
, ',');
385 p2
= strrchr(phys_path
, ':');
391 p2
= strchr(p2
, ',');
395 rv
= strcmp(devfs_path
, phys_path
);
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.
412 * port id if a compatibility link can be created.
416 check_compat_ports(di_node_t node
, char *phys_path
, char *minor
)
418 char portid
= *minor
;
422 if (portid
< 'a' || portid
> 'd')
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) {
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) {
444 * Neither link exists or both links point at "phys_path"
445 * We can safely create compatibility links.
450 return (s_strdup(port
));
456 * Called for all Onboard serial devices
457 * Creates links of the form "/dev/term/[a..z]"
460 onbrd_port_create(di_minor_t minor
, di_node_t node
)
462 char l_path
[MAXPATHLEN
], p_path
[MAXPATHLEN
];
463 char *devfspath
, *buf
, *minor_name
;
465 devfspath
= di_devfs_path(node
);
466 if (devfspath
== NULL
) {
467 devfsadm_errprint("%s: di_devfs_path() failed\n", modname
);
468 return (DEVFSADM_CONTINUE
);
471 if ((minor_name
= di_minor_name(minor
)) == NULL
) {
472 devfsadm_errprint("%s: NULL minor name\n\t%s\n",
474 di_devfs_path_free(devfspath
);
475 return (DEVFSADM_CONTINUE
);
479 * verify dialout ports do not come in on this nodetype
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
);
488 (void) strcpy(p_path
, devfspath
);
489 (void) strcat(p_path
, ":");
490 (void) strcat(p_path
, minor_name
);
491 di_devfs_path_free(devfspath
);
497 buf
= check_compat_ports(node
, p_path
, minor_name
);
501 * devfsadm_enumerate_char_start() is a private interface for use by the
504 if (!buf
&& devfsadm_enumerate_char_start(p_path
, 0, &buf
, obport_rules
,
506 devfsadm_errprint("%s: devfsadm_enumerate_char_start() failed"
507 "\n\t%s\n", modname
, p_path
);
508 return (DEVFSADM_CONTINUE
);
511 (void) strcpy(l_path
, "term/");
512 (void) strcat(l_path
, buf
);
513 (void) devfsadm_mklink(l_path
, node
, minor
, 0);
515 return (DEVFSADM_CONTINUE
);
519 * Onboard dialout devices
520 * Creates links of the form "/dev/cua/[a..z]"
523 onbrd_dialout_create(di_minor_t minor
, di_node_t node
)
525 char l_path
[MAXPATHLEN
], p_path
[MAXPATHLEN
];
526 char *devfspath
, *buf
, *mn
;
528 devfspath
= di_devfs_path(node
);
529 if (devfspath
== NULL
) {
530 devfsadm_errprint("%s: di_devfs_path() failed\n", modname
);
531 return (DEVFSADM_CONTINUE
);
534 if ((mn
= di_minor_name(minor
)) == NULL
) {
535 devfsadm_errprint("%s: NULL minor name\n\t%s\n",
537 di_devfs_path_free(devfspath
);
538 return (DEVFSADM_CONTINUE
);
542 * verify this is a dialout port
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
);
551 (void) strcpy(p_path
, devfspath
);
552 (void) strcat(p_path
, ":");
553 (void) strcat(p_path
, mn
);
554 di_devfs_path_free(devfspath
);
559 buf
= check_compat_ports(node
, p_path
, mn
);
563 * devfsadm_enumerate_char_start() is a private interface
564 * for use by the ports module only.
566 if (!buf
&& devfsadm_enumerate_char_start(p_path
, 0, &buf
, obport_rules
,
568 devfsadm_errprint("%s: devfsadm_enumerate_char_start() failed"
569 "\n\t%s\n", modname
, p_path
);
570 return (DEVFSADM_CONTINUE
);
574 * create the logical link
576 (void) strcpy(l_path
, "cua/");
577 (void) strcat(l_path
, buf
);
578 (void) devfsadm_mklink(l_path
, node
, minor
, 0);
580 return (DEVFSADM_CONTINUE
);
585 * Remote System Controller (RSC) serial ports
586 * Creates links of the form "/dev/rsc-control" | "/dev/term/rsc-console".
589 rsc_port_create(di_minor_t minor
, di_node_t node
)
595 devfspath
= di_devfs_path(node
);
596 if (devfspath
== NULL
) {
597 devfsadm_errprint("%s: di_devfs_path() failed\n", modname
);
598 return (DEVFSADM_CONTINUE
);
601 if ((minor_name
= di_minor_name(minor
)) == NULL
) {
602 devfsadm_errprint("%s: NULL minor name\n\t%s\n",
604 di_devfs_path_free(devfspath
);
605 return (DEVFSADM_CONTINUE
);
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
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
);
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
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
);
629 /* This is not an RSC node, continue... */
630 di_devfs_path_free(devfspath
);
631 return (DEVFSADM_CONTINUE
);
635 * Lights Out Management (LOM) serial ports
636 * Creates links of the form "/dev/term/lom-console".
639 lom_port_create(di_minor_t minor
, di_node_t node
)
644 devfspath
= di_devfs_path(node
);
645 if (devfspath
== NULL
) {
646 devfsadm_errprint("%s: di_devfs_path() failed\n", modname
);
647 return (DEVFSADM_CONTINUE
);
650 if ((minor_name
= di_minor_name(minor
)) == NULL
) {
651 devfsadm_errprint("%s: NULL minor name\n\t%s\n",
653 di_devfs_path_free(devfspath
);
654 return (DEVFSADM_CONTINUE
);
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.
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
);
668 /* This is not a LOM node, continue... */
669 di_devfs_path_free(devfspath
);
670 return (DEVFSADM_CONTINUE
);
675 * Removes port entries that no longer have devices
677 * Schedules an update the sacadm (portmon) database
680 rm_dangling_port(char *devname
)
685 devfsadm_print(PORT_MID
, "%s:rm_stale_port: %s\n",
688 if ((portstr
= strrchr(devname
, (int)'/')) == NULL
) {
689 devfsadm_errprint("%s: invalid name: %s\n",
696 * mark for removal from sacadm database
698 if ((portnum
= parse_portno(portstr
)) != -1)
699 pma
[portnum
].flags
|= PORT_REMOVED
;
701 devfsadm_rm_all(devname
);
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.
711 update_sacadm_db(void)
715 if (load_ttymondb() != DEVFSADM_SUCCESS
)
718 for (i
= 0; i
< maxports
; i
++) {
720 * if this port was removed and has a port
721 * monitor entry, remove the entry from the sacadm db
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
);
729 * if this port is present and lacks a port monitor
730 * add an entry to the sacadm db
732 if (pma
[i
].flags
& HAS_PORT_DEVICE
) {
733 if (!(pma
[i
].flags
& PM_HAS_ENTRY
))
738 * if this port has a pm entry, mark as needing
739 * a port monitor within this range of ports
741 if ((pma
[i
].flags
& PM_HAS_ENTRY
))
742 pma
[PM_SLOT(i
)].flags
|= PM_NEEDED
;
745 * continue for the range of ports per-portmon
747 if (((i
+ 1) % PM_GRPSZ
) != 0)
751 * if there are no ports active on the range we have
752 * just completed, remove the port monitor entry if
755 if ((pma
[PM_SLOT(i
)].flags
& (PM_NEEDED
| HAS_PORT_MON
)) ==
757 delete_port_monitor(i
);
763 * cleanup remaining port monitor, if active
765 if ((i
% PM_GRPSZ
!= 0) &&
766 ((pma
[PM_SLOT(i
)].flags
& (PM_NEEDED
| HAS_PORT_MON
)) ==
768 delete_port_monitor(i
);
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.
781 char cmdline
[CMDLEN
];
782 char cmdbuf
[PMTAB_MAXLINE
+1];
785 char *portname
; /* pointer to a tty name */
788 char *error_msg
= "%s: failed to load port monitor database\n";
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
);
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
);
806 devfsadm_print(PORT_MID
, "%s:load_ttymondb: port %s ",
811 * There is no reliable way to determine if we
812 * should start a port monitor on these lines.
814 if ((portnum
= parse_portno(portname
)) == -1) {
815 devfsadm_print(PORT_MID
, "ignored\n");
820 * the first field of the pmadm output is
821 * the port monitor name for this entry
823 if ((ptr
= strchr(cmdbuf
, PMTAB_SEPR
)) == NULL
) {
824 devfsadm_print(VERBOSE_MID
,
825 "load_ttymondb: no portmon tag\n");
830 if ((pma
[portnum
].pm_tag
= strdup(cmdbuf
)) == NULL
) {
831 devfsadm_errprint("load_ttymondb: failed strdup\n");
834 pma
[portnum
].flags
|= PM_HAS_ENTRY
;
835 pma
[PM_SLOT(portnum
)].flags
|= HAS_PORT_MON
;
836 devfsadm_print(PORT_MID
, "present\n");
838 (void) pclose(fs_popen
);
839 return (DEVFSADM_SUCCESS
);
844 * failed to load the port monitor database
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
));
853 return (DEVFSADM_FAILURE
);
857 * add a port monitor entry for device /dev/term/"port"
860 add_pm_entry(int port
)
862 char cmdline
[CMDLEN
];
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
);
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
));
882 pma
[port
].flags
|= PM_HAS_ENTRY
;
883 devfsadm_print(VERBOSE_MID
, "%s: /dev/term/%d added to sacadm\n",
888 remove_pm_entry(char *pmtag
, int port
)
891 char cmdline
[CMDLEN
];
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
));
906 pma
[port
].flags
&= ~PM_HAS_ENTRY
;
907 devfsadm_print(VERBOSE_MID
, "%s: /dev/term/%d removed from sacadm\n",
913 * delete_port_monitor()
914 * Check for the existence of a port monitor for "port" and remove it if
918 delete_port_monitor(int port
)
920 char cmdline
[CMDLEN
];
923 (void) sprintf(cmdline
, "/usr/sbin/sacadm -L -p ttymon%d",
925 sac_exitval
= execute(cmdline
);
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
;
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
));
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",
947 devfsadm_print(VERBOSE_MID
, "sacadm: (%s) %s\n",
948 SAC_EID(sac_exitval
), SAC_EMSG(sac_exitval
));
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
;
957 add_port_monitor(int port
)
959 char cmdline
[CMDLEN
];
962 if ((pma
[PM_SLOT(port
)].flags
& HAS_PORT_MON
) != 0) {
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",
980 devfsadm_print(VERBOSE_MID
, "sacadm: (%s) %s\n",
981 SAC_EID(sac_exitval
),
982 SAC_EMSG(sac_exitval
));
985 devfsadm_print(VERBOSE_MID
, "%s: port monitor ttymon%d added\n",
986 modname
, PM_NUM(port
));
988 pma
[PM_SLOT(port
)].flags
|= HAS_PORT_MON
;
992 * parse port number from string
993 * returns port number if in range [0..maxports]
996 parse_portno(char *dname
)
1000 if (sscanf(dname
, "%d", &pn
) != 1)
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
);
1015 * fork and exec a command, waiting for the command to
1016 * complete and return it's status
1019 execute(const char *s
)
1027 * fork a single threaded child proc to execute the
1028 * sacadm command string
1030 devfsadm_print(PORT_MID
, "%s: execute:\n\t%s\n", modname
, s
);
1031 if ((pid
= fork1()) == 0) {
1035 fd
= open("/dev/null", O_RDWR
);
1038 (void) execl("/sbin/sh", "sh", "-c", s
, 0);
1040 * return the sacadm exit status (see _exit(2))
1046 * wait for child process to terminate
1051 devfsadm_print(PORT_MID
, "%s:exit status (%d)\n",
1052 modname
, SAC_EXITVAL(status
));
1053 return (SAC_EXITVAL(status
));
1055 if (w
== (pid_t
)-1) {
1056 devfsadm_print(VERBOSE_MID
, "%s: exec failed\n",
1067 * check if the minor name is suffixed with ",cu"
1070 is_dialout(char *name
)
1074 if ((name
== NULL
) || (s_chr
= strrchr(name
, MN_SEPR
)) == NULL
)
1077 if (strcmp(s_chr
, DIALOUT_SUFFIX
) == 0) {
1086 * Get the name of the port device from a pmtab entry.
1087 * Note the /dev/term/ part is taken off.
1090 pmtab_parse_portname(char *buffer
)
1093 char *bufp
, *devnamep
, *portnamep
;
1096 * position to the device name (field 8)
1098 bufp
= strchr(buffer
, PMTAB_SEPR
);
1099 for (i
= 0; i
< PMTAB_DEVNAME_FIELD
; i
++) {
1102 bufp
= strchr(++bufp
, PMTAB_SEPR
);
1105 /* move past the ':' and locate the end of the devname */
1107 if ((bufp
= strchr(bufp
, PMTAB_SEPR
)) == NULL
)
1110 *bufp
= MN_NULLCHAR
;
1111 if ((portnamep
= strrchr(devnamep
, DEVNAME_SEPR
)) == NULL
) {
1116 /* return with "buffer" chopped after the /dev/term entry */
1118 return (++portnamep
);
1122 * port monitor array mgmt
1129 devfsadm_errprint("%s:pma_alloc:pma != NULL\n", modname
);
1133 if ((pma
= calloc(maxports
+ 1, sizeof (*pma
))) == NULL
) {
1134 devfsadm_errprint("%s:pma_alloc:pma alloc failure\n", modname
);
1138 return ((void *)pma
);
1151 * free any strings we had allocated
1153 for (i
= 0; i
<= maxports
; i
++) {
1154 free(pma
[i
].pm_tag
);