int open_unixsock_obj(obj_t *unixsock)
{
/*  (Re)opens the specified 'unixsock' obj.
 *  Returns 0 if the console is successfully opened; o/w, returns -1.
 */
    int rc = 0;

    assert(unixsock != NULL);
    assert(is_unixsock_obj(unixsock));

    if (unixsock->aux.unixsock.state == CONMAN_UNIXSOCK_UP) {
        rc = disconnect_unixsock_obj(unixsock);
    }
    else {
        rc = connect_unixsock_obj(unixsock);
    }
    return(rc);
}
static int disconnect_unixsock_obj(obj_t *unixsock)
{
/*  Closes the existing connection with the specified (unixsock) obj
 *    and sets a timer for establishing a new connection.
 *  Always returns -1.
 */
    unixsock_obj_t *auxp;

    assert(unixsock != NULL);
    assert(is_unixsock_obj(unixsock));

    auxp = &(unixsock->aux.unixsock);

    if (auxp->timer >= 0) {
        (void) tpoll_timeout_cancel(tp_global, auxp->timer);
        auxp->timer = -1;
    }
    if (unixsock->fd >= 0) {
        if (close(unixsock->fd) < 0) {
            log_msg(LOG_ERR,
                "Unable to close console [%s] socket \"%s\": %s",
                unixsock->name, auxp->dev, strerror(errno));
        }
        unixsock->fd = -1;
    }
    /*  Notify linked objs when transitioning from an UP state.
     */
    if (auxp->state == CONMAN_UNIXSOCK_UP) {
        auxp->state = CONMAN_UNIXSOCK_DOWN;
        write_notify_msg(unixsock, LOG_NOTICE,
            "Console [%s] disconnected from \"%s\"",
            unixsock->name, auxp->dev);
    }
    /*  Set timer for establishing new connection.
     */
    auxp->timer = tpoll_timeout_relative(tp_global,
        (callback_f) connect_unixsock_obj, unixsock,
        auxp->delay * 1000);

    if (auxp->delay < UNIXSOCK_MAX_TIMEOUT) {
        auxp->delay = MIN(auxp->delay * 2, UNIXSOCK_MAX_TIMEOUT);
    }
    return(-1);
}
static void reset_unixsock_delay(obj_t *unixsock)
{
/*  Resets the unixsock obj's reconnect-delay after the connection has been up
 *    for the minimum length of time.  This protects against spinning on
 *    reconnects when the connection immediately terminates.
 */
    unixsock_obj_t *auxp;

    assert(unixsock != NULL);
    assert(is_unixsock_obj(unixsock));

    auxp = &(unixsock->aux.unixsock);

    /*  Reset the timer ID since this routine is only invoked by a timer.
     */
    auxp->timer = -1;

    DPRINTF((15, "Reset [%s] reconnect delay\n", unixsock->name));
    auxp->delay = UNIXSOCK_MIN_TIMEOUT;
    return;
}
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 #6
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;
}
obj_t * create_unixsock_obj(server_conf_t *conf, char *name, char *dev,
    char *errbuf, int errlen)
{
/*  Creates a new unix domain object and adds it to the master objs list.
 *  Returns the new objects, or NULL on error.
 */
    int           n;
    ListIterator  i;
    obj_t        *unixsock;
    int           rv;

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

    /*  Check length of device string.
     */
    n = max_unixsock_dev_strlen();
    if (strlen(dev) > n) {
        snprintf(errbuf, errlen,
            "console [%s] exceeds maximum device length of %d bytes", name, n);
        return(NULL);
    }
    /*  Check for duplicate console and device names.
     */
    i = list_iterator_create(conf->objs);
    while ((unixsock = list_next(i))) {
        if (is_console_obj(unixsock) && !strcmp(unixsock->name, name)) {
            snprintf(errbuf, errlen,
                "console [%s] specifies duplicate console name", name);
            break;
        }
        if (is_unixsock_obj(unixsock)
                && !strcmp(unixsock->aux.unixsock.dev, dev)) {
            snprintf(errbuf, errlen,
                "console [%s] specifies duplicate device \"%s\"", name, dev);
            break;
        }
    }
    list_iterator_destroy(i);
    if (unixsock != NULL) {
        return(NULL);
    }
    unixsock = create_obj(conf, name, -1, CONMAN_OBJ_UNIXSOCK);
    unixsock->aux.unixsock.dev = create_string(dev);
    unixsock->aux.unixsock.logfile = NULL;
    unixsock->aux.unixsock.timer = -1;
    unixsock->aux.unixsock.state = CONMAN_UNIXSOCK_DOWN;
    /*
     *  Add obj to the master conf->objs list.
     */
    list_append(conf->objs, unixsock);

    rv = inevent_add(unixsock->aux.unixsock.dev,
        (inevent_cb_f) open_unixsock_obj, unixsock);
    if (rv < 0) {
        log_msg(LOG_INFO,
            "Console [%s] unable to register device \"%s\" for inotify events",
            unixsock->name, unixsock->aux.unixsock.dev);
    }
    return(unixsock);
}
static int connect_unixsock_obj(obj_t *unixsock)
{
/*  Opens a connection to the specified (unixsock) obj.
 *  Returns 0 if the connection is successfully completed; o/w, returns -1.
 */
    unixsock_obj_t     *auxp;
    struct stat         st;
    struct sockaddr_un  saddr;
    int                 rc;

    assert(unixsock != NULL);
    assert(is_unixsock_obj(unixsock));
    assert(unixsock->aux.unixsock.state != CONMAN_UNIXSOCK_UP);
    assert(strlen(unixsock->aux.unixsock.dev) <= max_unixsock_dev_strlen());

    auxp = &(unixsock->aux.unixsock);

    if (auxp->timer >= 0) {
        (void) tpoll_timeout_cancel(tp_global, auxp->timer);
        auxp->timer = -1;
    }

    if (stat(auxp->dev, &st) < 0) {
        log_msg(LOG_DEBUG, "Console [%s] cannot stat device \"%s\": %s",
            unixsock->name, auxp->dev, strerror(errno));
        return(disconnect_unixsock_obj(unixsock));
    }
#ifdef S_ISSOCK
    /*  Danger, Will Robinson!  S_ISSOCK not in POSIX.1-1996.
     *
     *  If this is not defined, connect() will detect the error of the device
     *    not being a socket.
     */
    if (!S_ISSOCK(st.st_mode)) {
        log_msg(LOG_INFO, "Console [%s] device \"%s\" is not a socket",
            unixsock->name, auxp->dev, strerror(errno));
        return(disconnect_unixsock_obj(unixsock));
    }
#endif /* S_ISSOCK */

    memset(&saddr, 0, sizeof(saddr));
    saddr.sun_family = AF_UNIX;
    rc = strlcpy(saddr.sun_path, auxp->dev, sizeof(saddr.sun_path));
    assert(rc < sizeof(saddr.sun_path));

    if ((unixsock->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        /*
         *  This error should probably not be considered fatal,
         *    but since it is elsewhere in the code, it is here as well.
         */
        log_err(errno, "Unable to create console [%s] socket", unixsock->name);
    }
    set_fd_nonblocking(unixsock->fd);
    set_fd_closed_on_exec(unixsock->fd);

    /*  FIXME: Check to see if connect() on a nonblocking unix domain socket
     *    can return EINPROGRESS.  I don't think it can.
     */
    if (connect(unixsock->fd,
            (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
        log_msg(LOG_INFO,
            "Console [%s] cannot connect to device \"%s\": %s",
            unixsock->name, auxp->dev, strerror(errno));
        return(disconnect_unixsock_obj(unixsock));
    }
    /*  Write-locking the unix domain socket appears ineffective.  But since
     *    create_unixsock_obj() already checks for duplicate devices, this is
     *    only an issue if two daemons are trying to simultaneously use the
     *    same local socket.
     */
    unixsock->gotEOF = 0;
    auxp->state = CONMAN_UNIXSOCK_UP;

    /*  Notify linked objs when transitioning into an UP state.
     */
    write_notify_msg(unixsock, LOG_NOTICE, "Console [%s] connected to \"%s\"",
        unixsock->name, auxp->dev);
    DPRINTF((9, "Opened [%s] unixsock: fd=%d dev=%s.\n",
            unixsock->name, unixsock->fd, auxp->dev));

    return(0);
}
Exemple #9
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;
}