/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2013 Garrett D'Amore <garrett@damore.org>
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <sac.h>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <fcntl.h>
#include <devfsadm.h>
#include <syslog.h>
/*
* sacadm output parsing
*/
/*
*/
static char *sacerrs[] = {
"UNKNOWN", "Unknown exit code",
"E_BADARGS", "Invalid arguments",
"E_NOPRIV", "Not privileged",
"E_SAFERR", "SAF error",
"E_SYSERR", "System error",
"E_NOEXIST", "Entry does not exist",
"E_DUP", "Entry already exists",
"E_PMRUN", "Port monitor already running",
"E_PMNOTRUN", "Port monitor not running",
"E_RECOVER", "In recovery",
"E_SACNOTRUN", "SAC daemon not running",
};
#define SAC_EID(x) \
#define SAC_EMSG(x) \
/*
* create port monitors for each group of PM_GRPSZ port devices.
*/
/*
* compute port monitor # and base index
*/
/*
* default maxports value
*/
/*
* command line buffer size for sacadm
*/
struct pm_alloc {
char *pm_tag;
};
/* port monitor entry flags */
static int maxports;
/*
* devfsadm_print message id
*/
/*
* enumeration regular expressions, port and onboard port devices
* a-d are assigned based on minor name. e-z are
* assigned via enumeration.
*/
#ifdef __i386
#else
#endif
static void rm_dangling_port(char *devname);
static void update_sacadm_db(void);
static int parse_portno(char *dname);
static int is_dialout(char *dname);
static int load_ttymondb(void);
static void add_pm_entry(int port);
static void delete_port_monitor(int port);
static void add_port_monitor(int port);
static int execute(const char *s);
static char *pmtab_parse_portname(char *cmdbuf);
static void *pma_alloc(void);
static void pma_free(void);
/*
* devfs create callback register
*/
{"pseudo", "ddi_pseudo", "su",
{"port", "ddi_serial:lomcon", "su",
};
/*
* devfs cleanup register
* no cleanup rules for PCMCIA port devices
*/
{"port", "^(term|cua)/[a-z]$",
};
int
{
char *maxport_str;
if ((maxport_str == NULL) ||
}
/* close defaults file */
} else {
}
return (DEVFSADM_FAILURE);
return (DEVFSADM_SUCCESS);
}
int
{
/*
* update the sacadm database only if we are updating
* this platform (no -r option)
*/
pma_free();
return (DEVFSADM_SUCCESS);
}
/*
* Called for all serial devices that are NOT onboard
* Schedules an update the sacadm (portmon).
*/
static int
{
int port_num;
return (DEVFSADM_CONTINUE);
}
return (DEVFSADM_CONTINUE);
}
/*
* verify dialout ports do not come in on this nodetype
*/
if (is_dialout(minor_name)) {
devfsadm_errprint("%s: dialout device\n\t%s:%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* add the minor name to the physical path so we can
* enum the port# and create the link.
*/
devfsadm_errprint("%s:serial_port_create:"
" enumerate_int() failed\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* This is probably a USB serial port coming into the system
* because someone just plugged one in. Log an indication of
* this to syslog just in case someone wants to know what the
* name of the new serial device is ..
*/
/*
* update the portmon database if this port falls within
* the valid range of ports.
*/
}
return (DEVFSADM_CONTINUE);
}
/*
* Called for all dialout devices that are NOT onboard
*/
static int
{
return (DEVFSADM_CONTINUE);
}
devfsadm_errprint("%s: NULL minorname\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
if (!is_dialout(mn)) {
devfsadm_errprint("%s: invalid minor name\n\t%s:%s\n",
return (DEVFSADM_CONTINUE);
}
devfsadm_errprint("%s:dialout_create:"
" enumerate_int() failed\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* add the minor name to the physical path so we can create
* the link.
*/
return (DEVFSADM_CONTINUE);
}
#ifdef __i386
static int
{
int rv;
return (1);
if (p1)
*p1 = '\0';
rv = -1;
goto out;
}
if (p2)
*p2 = '\0';
out:
if (p1)
*p1 = ',';
if (p2)
*p2 = ',';
return (rv);
}
/*
* If the minor name begins with [a-d] and the
* don't point at a different minor, then we can
* create compatibility links for this minor.
* Returns:
* port id if a compatibility link can be created.
* NULL otherwise
*/
static char *
{
char *devfs_path;
return (NULL);
return (NULL);
}
return (NULL);
}
/*
* Neither link exists or both links point at "phys_path"
* We can safely create compatibility links.
*/
}
#endif
/*
* Called for all Onboard serial devices
*/
static int
{
return (DEVFSADM_CONTINUE);
}
devfsadm_errprint("%s: NULL minor name\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* verify dialout ports do not come in on this nodetype
*/
if (is_dialout(minor_name)) {
return (DEVFSADM_CONTINUE);
}
#ifdef __i386
#endif
/*
* devfsadm_enumerate_char_start() is a private interface for use by the
* ports module only
*/
1, start_id)) {
devfsadm_errprint("%s: devfsadm_enumerate_char_start() failed"
return (DEVFSADM_CONTINUE);
}
return (DEVFSADM_CONTINUE);
}
/*
* Onboard dialout devices
*/
static int
{
return (DEVFSADM_CONTINUE);
}
devfsadm_errprint("%s: NULL minor name\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* verify this is a dialout port
*/
if (!is_dialout(mn)) {
devfsadm_errprint("%s: not a dialout device\n\t%s:%s\n",
return (DEVFSADM_CONTINUE);
}
#ifdef __i386
#endif
/*
* devfsadm_enumerate_char_start() is a private interface
* for use by the ports module only.
*/
1, start_id)) {
devfsadm_errprint("%s: devfsadm_enumerate_char_start() failed"
return (DEVFSADM_CONTINUE);
}
/*
* create the logical link
*/
return (DEVFSADM_CONTINUE);
}
/*
* Remote System Controller (RSC) serial ports
* Creates links of the form "/dev/rsc-control" | "/dev/term/rsc-console".
*/
static int
{
char *devfspath;
char *minor_name;
return (DEVFSADM_CONTINUE);
}
devfsadm_errprint("%s: NULL minor name\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* if this is the RSC console serial port (i.e. the minor name == ssp),
* create /dev/term/rsc-console link and then we are done with this
* node.
*/
return (DEVFSADM_TERMINATE);
/*
* else if this is the RSC control serial port (i.e. the minor name ==
* sspctl), create /dev/rsc-control link and then we are done with this
* node.
*/
return (DEVFSADM_TERMINATE);
}
/* This is not an RSC node, continue... */
return (DEVFSADM_CONTINUE);
}
/*
* Lights Out Management (LOM) serial ports
* Creates links of the form "/dev/term/lom-console".
*/
static int
{
char *devfspath;
char *minor_name;
return (DEVFSADM_CONTINUE);
}
devfsadm_errprint("%s: NULL minor name\n\t%s\n",
return (DEVFSADM_CONTINUE);
}
/*
* if this is the LOM console serial port (i.e. the minor
* name == lom-console ), create /dev/term/lom-console link and
* then we are done with this node.
*/
return (DEVFSADM_TERMINATE);
}
/* This is not a LOM node, continue... */
return (DEVFSADM_CONTINUE);
}
/*
* Removes port entries that no longer have devices
* backing them
* Schedules an update the sacadm (portmon) database
*/
static void
{
char *portstr;
int portnum;
devfsadm_errprint("%s: invalid name: %s\n",
return;
}
portstr++;
/*
* mark for removal from sacadm database
*/
}
/*
* Algorithm is to step through ports; checking for unneeded PM entries
* entries that should be there but are not. Every PM_GRPSZ entries
* check to see if there are any entries for the port monitor group;
* if not, delete the group.
*/
static void
update_sacadm_db(void)
{
int i;
if (load_ttymondb() != DEVFSADM_SUCCESS)
return;
for (i = 0; i < maxports; i++) {
/*
* if this port was removed and has a port
* monitor entry, remove the entry from the sacadm db
*/
}
/*
* if this port is present and lacks a port monitor
* add an entry to the sacadm db
*/
add_pm_entry(i);
}
/*
* if this port has a pm entry, mark as needing
* a port monitor within this range of ports
*/
/*
* continue for the range of ports per-portmon
*/
if (((i + 1) % PM_GRPSZ) != 0)
continue;
/*
* if there are no ports active on the range we have
* just completed, remove the port monitor entry if
* it exists
*/
HAS_PORT_MON) {
}
}
/*
* cleanup remaining port monitor, if active
*/
if ((i % PM_GRPSZ != 0) &&
HAS_PORT_MON)) {
}
}
/*
* Determine which port monitor entries already exist by invoking pmadm(1m)
* to list all configured 'ttymon' port monitor entries.
* Do not explicitly report errors from executing pmadm(1m) or sacadm(1m)
* commands to remain compatible with the ports(1m) implementation.
*/
static int
load_ttymondb(void)
{
int sac_exitval;
int portnum;
char *ptr;
return (DEVFSADM_FAILURE);
}
"load_ttymondb: failed to parse portname\n");
"load_ttymondb: buffer \"%s\"\n", cmdbuf);
goto load_failed;
}
/*
* skip onboard ports
* There is no reliable way to determine if we
* should start a port monitor on these lines.
*/
continue;
}
/*
* the first field of the pmadm output is
* the port monitor name for this entry
*/
"load_ttymondb: no portmon tag\n");
goto load_failed;
}
*ptr = MN_NULLCHAR;
devfsadm_errprint("load_ttymondb: failed strdup\n");
goto load_failed;
}
}
return (DEVFSADM_SUCCESS);
/*
* failed to load the port monitor database
*/
if (sac_exitval != 0) {
}
return (DEVFSADM_FAILURE);
}
/*
*/
static void
{
int sac_exitval;
if (devfsadm_noupdate() == DEVFSADM_FALSE) {
"failed to add port monitor entry"
}
}
}
static void
{
int sac_exitval;
if (devfsadm_noupdate() == DEVFSADM_FALSE) {
"failed to remove port monitor entry"
}
}
}
/*
* delete_port_monitor()
* Check for the existence of a port monitor for "port" and remove it if
* one exists
*/
static void
{
int sac_exitval;
/* clear the PM tag and return if the port monitor is not active */
if (sac_exitval == E_NOEXIST) {
return;
}
/* some other sacadm(1m) error, log and return */
if (sac_exitval != 0) {
return;
}
if (devfsadm_noupdate() == DEVFSADM_FALSE) {
"failed to remove port monitor ttymon%d\n",
}
}
}
static void
{
int sac_exitval;
return;
}
if (sac_exitval == E_NOEXIST) {
if (devfsadm_noupdate() == DEVFSADM_FALSE) {
"failed to add port monitor ttymon%d\n",
}
}
}
}
/*
* parse port number from string
* returns port number if in range [0..maxports]
*/
static int
{
int pn;
return (-1);
"%s:parse_portno: %d not in range (0..%d)\n",
return (-1);
}
return (pn);
}
/*
* fork and exec a command, waiting for the command to
* complete and return it's status
*/
static int
execute(const char *s)
{
int status;
int fd;
pid_t w;
/*
* fork a single threaded child proc to execute the
* sacadm command string
*/
(void) close(0);
(void) close(1);
(void) close(2);
/*
* return the sacadm exit status (see _exit(2))
*/
_exit(127);
}
/*
* wait for child process to terminate
*/
for (;;) {
if (w == pid) {
return (SAC_EXITVAL(status));
}
if (w == (pid_t)-1) {
modname);
return (-1);
}
}
/* NOTREACHED */
}
/*
* check if the minor name is suffixed with ",cu"
*/
static int
{
char *s_chr;
return (0);
return (1);
} else {
return (0);
}
}
/*
* Get the name of the port device from a pmtab entry.
*/
static char *
{
int i;
/*
* position to the device name (field 8)
*/
for (i = 0; i < PMTAB_DEVNAME_FIELD; i++) {
return (NULL);
}
/* move past the ':' and locate the end of the devname */
return (NULL);
*bufp = MN_NULLCHAR;
*bufp = PMTAB_SEPR;
return (NULL);
}
return (++portnamep);
}
/*
* port monitor array mgmt
*/
static void *
pma_alloc(void)
{
return (NULL);
}
return (NULL);
}
return ((void *)pma);
}
static void
pma_free(void)
{
int i;
return;
/*
* free any strings we had allocated
*/
for (i = 0; i <= maxports; i++) {
}
}