void set_serial_opts(struct termios *tty, obj_t *serial, seropt_t *opts)
{
/*  Sets serial device options specified by 'opts' for the
 *   'tty' terminal settings associated with the 'serial' object.
 *  Updates the 'tty' struct as appropriate.
 */
    assert(tty != NULL);
    assert(serial != NULL);
    assert(is_serial_obj(serial));
    assert(opts != NULL);
    assert(opts->bps > 0);
    assert((opts->databits >= 5) && (opts->databits <= 8));
    assert((opts->parity >= 0) && (opts->parity <= 2));
    assert((opts->stopbits >= 1) && (opts->stopbits <= 2));

    DPRINTF((10, "Setting [%s] dev=%s to %d,%d%s%d.\n",
        serial->name, serial->aux.serial.dev, bps_to_int(opts->bps),
        opts->databits, parity_to_str(opts->parity), opts->stopbits));

    if (cfsetispeed(tty, opts->bps) < 0)
        log_err(errno, "Unable to set [%s] input baud rate to %d",
            serial->name, opts->bps);
    if (cfsetospeed(tty, opts->bps) < 0)
        log_err(errno, "Unable to set [%s] output baud rate to %d",
            serial->name, opts->bps);

    tty->c_cflag &= ~CSIZE;
    if (opts->databits == 5) {
        tty->c_cflag |= CS5;
    }
    else if (opts->databits == 6) {
        tty->c_cflag |= CS6;
    }
    else if (opts->databits == 7) {
        tty->c_cflag |= CS7;
    }
    else /* (opts->databits == 8) */ {  /* safe default in case value is bad */
        tty->c_cflag |= CS8;
    }

    if (opts->parity == 1) {
        tty->c_cflag |= (PARENB | PARODD);
    }
    else if (opts->parity == 2) {
        tty->c_cflag |= PARENB;
        tty->c_cflag &= ~PARODD;
    }
    else /* (opts->parity == 0) */ {    /* safe default in case value is bad */
        tty->c_cflag &= ~(PARENB | PARODD);
    }

    if (opts->stopbits == 2) {
        tty->c_cflag |= CSTOPB;
    }
    else /* (opts->stopbits == 1) */ {  /* safe default in case value is bad */
        tty->c_cflag &= ~CSTOPB;
    }

    return;
}
obj_t * create_serial_obj(server_conf_t *conf, char *name,
    char *dev, seropt_t *opts, char *errbuf, int errlen)
{
/*  Creates a new serial device object and adds it to the master objs list.
 *    Note: the console is open and set for non-blocking I/O.
 *  Returns the new object, or NULL on error.
 */
    ListIterator i;
    obj_t *serial;

    assert(conf != NULL);
    assert((name != NULL) && (name[0] != '\0'));
    assert((dev != NULL) && (dev[0] != '\0'));
    assert(opts != NULL);

    /*  Check for duplicate console and device names.
     *  While the write-lock will protect against two separate daemons
     *    using the same device, it will not protect against two console
     *    objects within the same daemon process using the same device.
     *    So that check is performed here.
     */
    i = list_iterator_create(conf->objs);
    while ((serial = list_next(i))) {
        if (is_console_obj(serial) && !strcmp(serial->name, name)) {
            snprintf(errbuf, errlen,
                "console [%s] specifies duplicate console name", name);
            break;
        }
        if (is_serial_obj(serial) && !strcmp(serial->aux.serial.dev, dev)) {
            snprintf(errbuf, errlen,
                "console [%s] specifies duplicate device \"%s\"", name, dev);
            break;
        }
    }
    list_iterator_destroy(i);
    if (serial != NULL) {
        return(NULL);
    }
    serial = create_obj(conf, name, -1, CONMAN_OBJ_SERIAL);
    serial->aux.serial.dev = create_string(dev);
    serial->aux.serial.opts = *opts;
    serial->aux.serial.logfile = NULL;
    /*
     *  Add obj to the master conf->objs list.
     */
    list_append(conf->objs, serial);

    return(serial);
}
obj_t * get_console_logfile_obj(obj_t *console)
{
/*  Returns a ptr to the logfile obj associated with 'console'
 *    if one exists and is currently active; o/w, returns NULL.
 */
    obj_t *logfile = NULL;

    assert(console != NULL);
    assert(is_console_obj(console));

    if (is_process_obj(console)) {
        logfile = console->aux.process.logfile;
    }
    else if (is_serial_obj(console)) {
        logfile = console->aux.serial.logfile;
    }
    else if (is_telnet_obj(console)) {
        logfile = console->aux.telnet.logfile;
    }
    else if (is_unixsock_obj(console)) {
        logfile = console->aux.unixsock.logfile;
    }
#if WITH_FREEIPMI
    else if (is_ipmi_obj(console)) {
        logfile = console->aux.ipmi.logfile;
    }
#endif /* WITH_FREEIPMI */
    else {
        log_err(0, "INTERNAL: Unrecognized console [%s] type=%d",
            console->name, console->type);
    }
    if (!logfile || (logfile->fd < 0)) {
        return(NULL);
    }
    assert(is_logfile_obj(logfile));
    return(logfile);
}
obj_t * create_logfile_obj(server_conf_t *conf, char *name,
    obj_t *console, logopt_t *opts, char *errbuf, int errlen)
{
/*  Creates a new logfile object and adds it to the master objs list.
 *    Note: the logfile is open and set for non-blocking I/O.
 *  Note: the logfile will later be opened and set for non-blocking I/O
 *    by main:open_objs:reopen_obj:open_logfile_obj().
 *  Returns the new object, or NULL on error.
 */
    ListIterator i;
    obj_t *logfile;
    char buf[MAX_LINE];
    char *pname;
    obj_t *obj;

    assert(conf != NULL);
    assert((name != NULL) && (name[0] != '\0'));
    assert(console != NULL);
    assert(opts != NULL);

    /*  Check for duplicate logfile names.
     *  While the write-lock will protect against two separate daemons
     *    using the same logfile, it will not protect against two logfile
     *    objects within the same daemon process using the same filename.
     *    So that check is performed here.
     */
    if (strchr(name, '%')
            && (format_obj_string(buf, sizeof(buf), console, name) >= 0)) {
        pname = buf;
    }
    else {
        pname = name;
    }

    i = list_iterator_create(conf->objs);
    while ((logfile = list_next(i))) {
        if (!is_logfile_obj(logfile)) {
            continue;
        }
        if (!strcmp(logfile->name, pname)) {
            break;
        }
    }
    list_iterator_destroy(i);

    if (logfile) {
        snprintf(errbuf, errlen, "console [%s] already logging to \"%s\"",
            logfile->aux.logfile.console->name, pname);
        return(NULL);
    }
    logfile = create_obj(conf, name, -1, CONMAN_OBJ_LOGFILE);
    logfile->aux.logfile.console = console;
    logfile->aux.logfile.lineState = CONMAN_LOG_LINE_INIT;
    logfile->aux.logfile.opts = *opts;
    logfile->aux.logfile.gotTruncate = !!conf->enableZeroLogs;

    if (logfile->aux.logfile.opts.enableSanitize
            || logfile->aux.logfile.opts.enableTimestamp) {
        logfile->aux.logfile.gotProcessing = 1;
    }
    else {
        logfile->aux.logfile.gotProcessing = 0;
    }

    if (strchr(name, '%')) {
        logfile->aux.logfile.fmtName = create_string(name);
    }
    else {
        logfile->aux.logfile.fmtName = NULL;
    }

    if (is_process_obj(console)) {
        console->aux.process.logfile = logfile;
    }
    else if (is_serial_obj(console)) {
        console->aux.serial.logfile = logfile;
    }
    else if (is_telnet_obj(console)) {
        console->aux.telnet.logfile = logfile;
    }
    else if (is_unixsock_obj(console)) {
        console->aux.unixsock.logfile = logfile;
    }
#if WITH_FREEIPMI
    else if (is_ipmi_obj(console)) {
        console->aux.ipmi.logfile = logfile;
    }
#endif /* WITH_FREEIPMI */
    else {
        log_err(0, "INTERNAL: Unrecognized console [%s] type=%d",
            console->name, console->type);
    }
    /*  Add obj to the master conf->objs list
     *    before its corresponding console obj.
     */
    i = list_iterator_create(conf->objs);
    while ((obj = list_next(i))) {
        if (obj == console) {
            list_insert(i, logfile);
            break;
        }
    }
    list_iterator_destroy(i);
    if (!obj) {
        log_err(0, "INTERNAL: Console [%s] object not found in master list",
            console->name);
    }
    return(logfile);
}
Exemple #5
0
static void mux_io(server_conf_t *conf)
{
/*  Multiplexes I/O between all of the objs in the configuration.
 *  This routine is the heart of ConMan.
 */
    ListIterator i;
    int n;
    obj_t *obj;
    int inevent_fd;

    assert(conf->tp != NULL);
    assert(!list_is_empty(conf->objs));

    i = list_iterator_create(conf->objs);

    while (!done) {

        if (reconfig) {
            /*
             *  FIXME: A reconfig should pro'ly resurrect "downed" serial objs
             *    and reset reconnect timers of "downed" telnet objs.
             */
            log_msg(LOG_NOTICE, "Performing reconfig on signal=%d", reconfig);
            reopen_logfiles(conf);
            reconfig = 0;
        }

        /*  FIXME: Switch from recomputing the tpoll set on each loop iteration
         *    to modifying it based on events.  This will eliminate the 1sec
         *    tpoll() sleep timeout and greatly reduce cpu utilization.
         *    It will also eliminate the maze of twisty conditions below.
         */
        DPRINTF((25, "Recomputing tpoll fd set\n"));
        (void) tpoll_zero(conf->tp, TPOLL_ZERO_FDS);
        tpoll_set(conf->tp, conf->ld, POLLIN);

        inevent_fd = inevent_get_fd();
        if (inevent_fd >= 0) {
            tpoll_set(conf->tp, inevent_get_fd(), POLLIN);
        }
        list_iterator_reset(i);
        while ((obj = list_next(i))) {

            if (obj->gotReset) {
                reset_console(obj, conf->resetCmd);
            }
            if (obj->fd < 0) {
                continue;
            }
            if ( (
                   ( is_telnet_obj(obj) &&
                     obj->aux.telnet.state == CONMAN_TELNET_UP ) ||
                   ( is_process_obj(obj) &&
                     obj->aux.process.state == CONMAN_PROCESS_UP ) ||
#if WITH_FREEIPMI
                   ( is_ipmi_obj(obj) &&
                     obj->aux.ipmi.state == CONMAN_IPMI_UP ) ||
#endif /* WITH_FREEIPMI */
                   ( is_unixsock_obj(obj) &&
                     obj->aux.unixsock.state == CONMAN_UNIXSOCK_UP ) ||
                   is_serial_obj(obj)  ||
                   is_client_obj(obj)
                 )
                 &&
                 ( ! obj->gotEOF ) )
            {
                tpoll_set(conf->tp, obj->fd, POLLIN);
            }
            if ( ( (obj->bufInPtr != obj->bufOutPtr) ||
                   (obj->gotEOF) ) &&
                 ( ! (is_telnet_obj(obj) &&
                      obj->aux.telnet.state != CONMAN_TELNET_UP) ) &&
                 ( ! (is_process_obj(obj) &&
                      obj->aux.process.state != CONMAN_PROCESS_UP) ) &&
#if WITH_FREEIPMI
                 ( ! (is_ipmi_obj(obj) &&
                      obj->aux.ipmi.state != CONMAN_IPMI_UP) ) &&
#endif /* WITH_FREEIPMI */
                 ( ! (is_unixsock_obj(obj) &&
                      obj->aux.unixsock.state != CONMAN_UNIXSOCK_UP) ) &&
                 ( ! (is_client_obj(obj) &&
                      obj->aux.client.gotSuspend) ) )
            {
                tpoll_set(conf->tp, obj->fd, POLLOUT);
            }
            if (is_telnet_obj(obj) &&
                obj->aux.telnet.state == CONMAN_TELNET_PENDING)
            {
                tpoll_set(conf->tp, obj->fd, POLLIN | POLLOUT);
            }
        }
        DPRINTF((25, "Calling tpoll\n"));
        while ((n = tpoll(conf->tp, 1000)) < 0) {
            if (errno != EINTR) {
                log_err(errno, "Unable to multiplex I/O");
            }
            else if (done || reconfig) {
                break;
            }
        }
        if (n <= 0) {
            continue;
        }
        if (tpoll_is_set(conf->tp, conf->ld, POLLIN)) {
            accept_client(conf);
        }
        if ((inevent_fd >= 0) && tpoll_is_set(conf->tp, inevent_fd, POLLIN)) {
            inevent_process();
        }
        /*  If read_from_obj() or write_to_obj() returns -1,
         *    the obj's buffer has been flushed.  If it is a telnet obj,
         *    retain it and attempt to re-establish the connection;
         *    o/w, give up and remove it from the master objs list.
         */
        list_iterator_reset(i);
        while ((obj = list_next(i))) {

            if (obj->fd < 0) {
                continue;
            }
            if (is_telnet_obj(obj)
              && tpoll_is_set(conf->tp, obj->fd, POLLIN | POLLOUT)
              && (obj->aux.telnet.state == CONMAN_TELNET_PENDING)) {
                open_telnet_obj(obj);
                continue;
            }
            if (tpoll_is_set(conf->tp, obj->fd, POLLIN | POLLHUP | POLLERR)) {
                if (read_from_obj(obj, conf->tp) < 0) {
                    list_delete(i);
                    continue;
                }
                if (obj->fd < 0) {
                    continue;
                }
            }
            if (tpoll_is_set(conf->tp, obj->fd, POLLOUT)) {
                if (write_to_obj(obj) < 0) {
                    list_delete(i);
                    continue;
                }
                if (obj->fd < 0) {
                    continue;
                }
            }
        }
    }
    log_msg(LOG_NOTICE, "Exiting on signal=%d", done);
    list_iterator_destroy(i);
    return;
}
int open_serial_obj(obj_t *serial)
{
/*  (Re)opens the specified 'serial' obj.
 *  Returns 0 if the serial console is successfully opened; o/w, returns -1.
 *
 *  FIXME: Check to see if "downed" serial consoles are ever resurrected.
 */
    int fd;
    int flags;
    struct termios tty;

    assert(serial != NULL);
    assert(is_serial_obj(serial));

    if (serial->fd >= 0) {
        write_notify_msg(serial, LOG_INFO,
            "Console [%s] disconnected from \"%s\"",
            serial->name, serial->aux.serial.dev);
        set_tty_mode(&serial->aux.serial.tty, serial->fd);
        if (close(serial->fd) < 0)      /* log err and continue */
            log_msg(LOG_WARNING, "Unable to close [%s] device \"%s\": %s",
                serial->name, serial->aux.serial.dev, strerror(errno));
        serial->fd = -1;
    }
    flags = O_RDWR | O_NONBLOCK | O_NOCTTY;
    if ((fd = open(serial->aux.serial.dev, flags)) < 0) {
        log_msg(LOG_WARNING, "Unable to open [%s] device \"%s\": %s",
            serial->name, serial->aux.serial.dev, strerror(errno));
        goto err;
    }
    if (get_write_lock(fd) < 0) {
        log_msg(LOG_WARNING, "Unable to lock [%s] device \"%s\"",
            serial->name, serial->aux.serial.dev);
        goto err;
    }
    if (!isatty(fd)) {
        log_msg(LOG_WARNING, "[%s] device \"%s\" not a terminal",
            serial->name, serial->aux.serial.dev);
        goto err;
    }
    /*  According to the UNIX Programming FAQ v1.37
     *    <http://www.faqs.org/faqs/unix-faq/programmer/faq/>
     *    (Section 3.6: How to Handle a Serial Port or Modem),
     *    systems seem to differ as to whether a nonblocking
     *    open on a tty will affect subsequent read()s.
     *    Play it safe and be explicit!
     */
    set_fd_nonblocking(fd);
    set_fd_closed_on_exec(fd);
    /*
     *  Note that while the initial state of the console dev's termios
     *    are saved, the 'opts' settings are not.  This is because the
     *    settings do not change until the obj is destroyed, at which time
     *    the termios is reverted back to its initial state.
     *
     *  FIXME: Re-evaluate this thinking since a SIGHUP should attempt
     *         to resurrect "downed" serial objs.
     */
    get_tty_mode(&serial->aux.serial.tty, fd);
    get_tty_raw(&tty, fd);
    set_serial_opts(&tty, serial, &serial->aux.serial.opts);
    set_tty_mode(&tty, fd);
    serial->fd = fd;
    serial->gotEOF = 0;
    /*
     *  Success!
     */
    write_notify_msg(serial, LOG_INFO, "Console [%s] connected to \"%s\"",
        serial->name, serial->aux.serial.dev);
    DPRINTF((9, "Opened [%s] serial: fd=%d dev=%s bps=%d.\n",
        serial->name, serial->fd, serial->aux.serial.dev,
        bps_to_int(serial->aux.serial.opts.bps)));
    return(0);

err:
    if (fd >= 0) {
        close(fd);                      /* ignore errors */
    }
    return(-1);
}
Exemple #7
0
static void check_console_state(obj_t *console, obj_t *client)
{
/*  Checks the state of the console and warns the client if needed.
 *  Informs the newly-connected client if strange things are afoot.
 *  Attempts an immediate reconnect if the console connection is down.
 */
    char buf[MAX_LINE];

    assert(is_console_obj(console));
    assert(is_client_obj(client));

    if (is_process_obj(console) && (console->fd < 0)) {
        snprintf(buf, sizeof(buf),
            "%sConsole [%s] is currently disconnected from \"%s\"%s",
            CONMAN_MSG_PREFIX, console->name, console->aux.process.prog,
            CONMAN_MSG_SUFFIX);
        strcpy(&buf[sizeof(buf) - 3], "\r\n");
        write_obj_data(client, buf, strlen(buf), 1);
        open_process_obj(console);
    }
    else if (is_serial_obj(console) && (console->fd < 0)) {
        snprintf(buf, sizeof(buf),
            "%sConsole [%s] is currently disconnected from \"%s\"%s",
            CONMAN_MSG_PREFIX, console->name, console->aux.serial.dev,
            CONMAN_MSG_SUFFIX);
        strcpy(&buf[sizeof(buf) - 3], "\r\n");
        write_obj_data(client, buf, strlen(buf), 1);
        open_serial_obj(console);
    }
    else if (is_telnet_obj(console)
            && (console->aux.telnet.state != CONMAN_TELNET_UP)) {
        snprintf(buf, sizeof(buf),
            "%sConsole [%s] is currently disconnected from <%s:%d>%s",
            CONMAN_MSG_PREFIX, console->name, console->aux.telnet.host,
            console->aux.telnet.port, CONMAN_MSG_SUFFIX);
        strcpy(&buf[sizeof(buf) - 3], "\r\n");
        write_obj_data(client, buf, strlen(buf), 1);
        console->aux.telnet.delay = TELNET_MIN_TIMEOUT;
        /*
         *  Do not call connect_telnet_obj() while in the PENDING state since
         *    it would be misinterpreted as the completion of the non-blocking
         *    connect().
         */
        if (console->aux.telnet.state == CONMAN_TELNET_DOWN) {
            open_telnet_obj(console);
        }
    }
    else if (is_unixsock_obj(console) && (console->fd < 0)) {
        assert(console->aux.unixsock.state == CONMAN_UNIXSOCK_DOWN);
        snprintf(buf, sizeof(buf),
            "%sConsole [%s] is currently disconnected from \"%s\"%s",
            CONMAN_MSG_PREFIX, console->name, console->aux.unixsock.dev,
            CONMAN_MSG_SUFFIX);
        strcpy(&buf[sizeof(buf) - 3], "\r\n");
        write_obj_data(client, buf, strlen(buf), 1);
        open_unixsock_obj(console);
    }
#if WITH_FREEIPMI
    else if (is_ipmi_obj(console)
            && (console->aux.ipmi.state != CONMAN_IPMI_UP)) {
        snprintf(buf, sizeof(buf),
            "%sConsole [%s] is currently disconnected from <%s>%s",
            CONMAN_MSG_PREFIX, console->name, console->aux.ipmi.host,
            CONMAN_MSG_SUFFIX);
        strcpy(&buf[sizeof(buf) - 3], "\r\n");
        write_obj_data(client, buf, strlen(buf), 1);
        if (console->aux.ipmi.state == CONMAN_IPMI_DOWN) {
            open_ipmi_obj(console);
        }
    }
#endif /* WITH_FREEIPMI */
    return;
}