Пример #1
0
static int perform_monitor_cmd(req_t *req, server_conf_t *conf)
{
/*  Performs the MONITOR command, placing the client in a
 *    "read-only" session with a single console.
 *  Returns 0 if the command succeeds, or -1 on error.
 */
    obj_t *client;
    obj_t *console;

    assert(req->sd >= 0);
    assert(req->command == CONMAN_CMD_MONITOR);
    assert(list_count(req->consoles) == 1);

    log_msg(LOG_INFO, "Client <%s@%s:%d> issued monitor command",
        req->user, req->fqdn, req->port);

    if (send_rsp(req, CONMAN_ERR_NONE, NULL) < 0)
        return(-1);
    client = create_client_obj(conf, req);
    console = list_peek(req->consoles);
    assert(is_console_obj(console));
    link_objs(console, client);
    check_console_state(console, client);
    return(0);
}
Пример #2
0
static int query_consoles_via_regex(
    server_conf_t *conf, req_t *req, List matches)
{
/*  Match request patterns against console names using regular expressions.
 */
    char *p;
    ListIterator i;
    char buf[MAX_SOCK_LINE];
    int rc;
    regex_t rex;
    regmatch_t match;
    obj_t *obj;

    /*  An empty list for the QUERY command matches all consoles.
     */
    if (list_is_empty(req->consoles)) {
        p = create_string(".*");
        list_append(req->consoles, p);
    }

    /*  Combine console patterns via alternation to create single regex.
     */
    i = list_iterator_create(req->consoles);
    strlcpy(buf, list_next(i), sizeof(buf));
    while ((p = list_next(i))) {
        strlcat(buf, "|", sizeof(buf));
        strlcat(buf, p, sizeof(buf));
    }
    list_iterator_destroy(i);

    /*  Initialize 'rex' to silence "uninitialized use" warnings.
     */
    memset(&rex, 0, sizeof(rex));

    /*  Compile regex for searching server's console objs.
     */
    rc = regcomp(&rex, buf, REG_EXTENDED | REG_ICASE);
    if (rc != 0) {
        if (regerror(rc, &rex, buf, sizeof(buf)) > sizeof(buf))
            log_msg(LOG_WARNING, "Got regerror() buffer overrun");
        regfree(&rex);
        send_rsp(req, CONMAN_ERR_BAD_REGEX, buf);
        return(-1);
    }

    /*  Search objs for console names matching console patterns in the request.
     */
    i = list_iterator_create(conf->objs);
    while ((obj = list_next(i))) {
        if (!is_console_obj(obj))
            continue;
        if (!regexec(&rex, obj->name, 1, &match, 0)
          && (match.rm_so == 0) && (match.rm_eo == strlen(obj->name)))
            list_append(matches, obj);
    }
    list_iterator_destroy(i);
    regfree(&rex);
    return(0);
}
Пример #3
0
static int perform_connect_cmd(req_t *req, server_conf_t *conf)
{
/*  Performs the CONNECT command.  If a single console is specified,
 *    the client is placed in a "read-write" session with that console.
 *    Otherwise, the client is placed in a "write-only" broadcast session
 *    affecting multiple consoles.
 *  Returns 0 if the command succeeds, or -1 on error.
 */
    obj_t *client;
    obj_t *console;
    ListIterator i;

    assert(req->sd >= 0);
    assert(req->command == CONMAN_CMD_CONNECT);

    log_msg(LOG_INFO, "Client <%s@%s:%d> issued connect command",
        req->user, req->fqdn, req->port);

    if (send_rsp(req, CONMAN_ERR_NONE, NULL) < 0)
        return(-1);
    client = create_client_obj(conf, req);

    if (list_count(req->consoles) == 1) {
        /*
         *  Unicast connection (R/W).
         */
        console = list_peek(req->consoles);
        assert(is_console_obj(console));
        link_objs(client, console);
        link_objs(console, client);
        check_console_state(console, client);
    }
    else {
        /*
         *  Broadcast connection (W/O).
         */
        i = list_iterator_create(req->consoles);
        while ((console = list_next(i))) {
            assert(is_console_obj(console));
            link_objs(client, console);
            check_console_state(console, client);
        }
        list_iterator_destroy(i);
    }
    return(0);
}
Пример #4
0
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);
}
Пример #5
0
static int query_consoles_via_globbing(
    server_conf_t *conf, req_t *req, List matches)
{
/*  Match request patterns against console names using shell-style globbing.
 *  This is less efficient than matching via regular expressions
 *    since the console list must be traversed for each pattern, and the
 *    matches list must be traversed for each match to prevent duplicates.
 */
    char *p;
    ListIterator i, j;
    char *pat;
    obj_t *obj;

    /*  An empty list for the QUERY command matches all consoles.
     */
    if (list_is_empty(req->consoles)) {
        p = create_string("*");
        list_append(req->consoles, p);
    }

    /*  Search objs for console names matching console patterns in the request.
     */
    i = list_iterator_create(req->consoles);
    j = list_iterator_create(conf->objs);
    while ((pat = list_next(i))) {
        list_iterator_reset(j);
        while ((obj = list_next(j))) {
            if (!is_console_obj(obj))
                continue;
            if (!fnmatch(pat, obj->name, 0)
              && !list_find_first(matches, (ListFindF) find_obj, obj))
                list_append(matches, obj);
        }
    }
    list_iterator_destroy(i);
    list_iterator_destroy(j);
    return(0);
}
Пример #6
0
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);
}
Пример #7
0
static void display_configuration(server_conf_t *conf)
{
/*  Displays a summary of the server's configuration.
 */
    ListIterator i;
    obj_t *obj;
    int n = 0;
    int gotOptions = 0;

    i = list_iterator_create(conf->objs);
    while ((obj = list_next(i))) {
        if (is_console_obj(obj)) {
            n++;
        }
    }
    list_iterator_destroy(i);

    fprintf(stderr, "\nStarting ConMan daemon %s (pid %d)\n",
        VERSION, (int) getpid());
    fprintf(stderr, "Configuration: %s\n", conf->confFileName);
    fprintf(stderr, "Options:");

    if (conf->enableCoreDump) {
        fprintf(stderr, " CoreDump");
        gotOptions++;
    }
    if (conf->enableKeepAlive) {
        fprintf(stderr, " KeepAlive");
        gotOptions++;
    }
    if (conf->logFileName) {
        fprintf(stderr, " LogFile");
        gotOptions++;
    }
    if (conf->enableLoopBack) {
        fprintf(stderr, " LoopBack");
        gotOptions++;
    }
    if (conf->resetCmd) {
        fprintf(stderr, " ResetCmd");
        gotOptions++;
    }
    if (conf->syslogFacility >= 0) {
        fprintf(stderr, " SysLog");
        gotOptions++;
    }
    if (conf->enableTCPWrap) {
        fprintf(stderr, " TCP-Wrappers");
        gotOptions++;
    }
    if (conf->tStampMinutes > 0) {
        fprintf(stderr, " TimeStamp=%dm", conf->tStampMinutes);
        gotOptions++;
    }
    if (conf->enableZeroLogs) {
        fprintf(stderr, " ZeroLogs");
        gotOptions++;
    }
    if (!gotOptions) {
        fprintf(stderr, " None");
    }
    fprintf(stderr, "\n");
    fprintf(stderr, "Listening on port %d\n", conf->port);
    fprintf(stderr, "Monitoring %d console%s\n", n, ((n == 1) ? "" : "s"));
    fprintf(stderr, "\n");
    return;
}
Пример #8
0
static void reset_console(obj_t *console, const char *cmd)
{
/*  Resets the 'console' obj by performing the reset 'cmd' in a subshell.
 */
    char buf[MAX_LINE];
    pid_t pid;
    pid_t *arg;

    assert(is_console_obj(console));
    assert(console->gotReset);
    assert(cmd != NULL);

    console->gotReset = 0;

    if (format_obj_string(buf, sizeof(buf), console, cmd) < 0) {
        log_msg(LOG_NOTICE, "Unable to reset console [%s]: command too long",
            console->name);
        return;
    }
    if ((pid = fork()) < 0) {
        log_msg(LOG_NOTICE, "Unable to reset console [%s]: %s",
            console->name, strerror(errno));
        return;
    }
    else if (pid == 0) {
        setpgid(pid, 0);
        close(STDIN_FILENO);            /* ignore errors on close() */
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        execl("/bin/sh", "sh", "-c", buf, (char *) NULL);
        _exit(127);                     /* execl() error */
    }
    /*  Both parent and child call setpgid() to make the child a process
     *    group leader.  One of these calls is redundant, but by doing
     *    both we avoid a race condition.  (cf. APUE 9.4 p244)
     */
    setpgid(pid, 0);

    /*  FIXME: Have perform_reset() store the client info instead of a bool
     *    for gotReset.  Then remove the notify_objs msg from perform_reset()
     *    and replace it with the following:
     */
    log_msg(LOG_INFO, "Reset console [%s] (pid %d)", console->name, pid);
#if 0
    snprintf(buf, sizeof(buf), "%sConsole [%s] reset (pid %d)%s",
        CONMAN_MSG_PREFIX, console->name, (int) pid, CONMAN_MSG_SUFFIX);
    strcpy(&buf[sizeof(buf) - 3], "\r\n");
    notify_console_objs(console, buf);
#endif

    /*  Set a timer to ensure the reset cmd does not exceed its time limit.
     *  The callback function's arg must be allocated on the heap since
     *    local vars on the stack will be lost once this routine returns.
     */
    if (!(arg = malloc(sizeof *arg))) {
        out_of_memory();
    }
    *arg = pid;

    if (tpoll_timeout_relative (tp_global,
            (callback_f) kill_console_reset, arg,
            RESET_CMD_TIMEOUT * 1000) < 0) {
        log_msg(LOG_ERR,
            "Unable to create timer for resetting console [%s]",
            console->name);
    }
    return;
}
Пример #9
0
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);
}
Пример #10
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;
}
Пример #11
0
static int check_busy_consoles(req_t *req)
{
/*  Checks to see if a "writable" request affects any consoles
 *    that are currently busy (unless the force or join option is enabled).
 *  Returns 0 if the request is valid, or -1 on error.
 */
    List busy;
    ListIterator i;
    obj_t *console;
    obj_t *writer;
    int gotBcast;
    char *tty;
    time_t t;
    char *delta;
    char buf[MAX_LINE];

    assert(!list_is_empty(req->consoles));

    if ((req->command == CONMAN_CMD_QUERY)
      || (req->command == CONMAN_CMD_MONITOR))
        return(0);
    if (req->enableForce || req->enableJoin)
        return(0);

    busy = list_create(NULL);
    i = list_iterator_create(req->consoles);
    while ((console = list_next(i))) {
        assert(is_console_obj(console));
        if (!list_is_empty(console->writers))
            list_append(busy, console);
    }
    list_iterator_destroy(i);

    if (list_is_empty(busy)) {
        list_destroy(busy);
        return(0);
    }

    if (list_count(busy) == 1) {
        snprintf(buf, sizeof(buf), "Found console already in use");
    }
    else {
        snprintf(buf, sizeof(buf), "Found %d consoles already in use",
            list_count(busy));
    }
    send_rsp(req, CONMAN_ERR_BUSY_CONSOLES, buf);

    /*  Note: the "busy" list contains object references,
     *    so they DO NOT get destroyed here when removed from the list.
     */
    while ((console = list_pop(busy))) {

        i = list_iterator_create(console->writers);
        while ((writer = list_next(i))) {

            assert(is_client_obj(writer));
            x_pthread_mutex_lock(&writer->bufLock);
            t = writer->aux.client.timeLastRead;
            gotBcast = list_is_empty(writer->writers);
            tty = writer->aux.client.req->tty;
            x_pthread_mutex_unlock(&writer->bufLock);
            delta = create_time_delta_string(t, -1);

            snprintf(buf, sizeof(buf),
                "Console [%s] open %s by <%s@%s>%s%s (idle %s).\n",
                console->name, (gotBcast ? "B/C" : "R/W"),
                writer->aux.client.req->user, writer->aux.client.req->host,
                (tty ? " on " : ""), (tty ? tty : ""),
                (delta ? delta : "???"));
            buf[sizeof(buf) - 2] = '\n';
            buf[sizeof(buf) - 1] = '\0';
            if (delta)
                free(delta);
            if (write_n(req->sd, buf, strlen(buf)) < 0) {
                log_msg(LOG_NOTICE, "Unable to write to <%s:%d>: %s",
                    req->fqdn, req->port, strerror(errno));
                break;
            }
        }
        list_iterator_destroy(i);
    }
    list_destroy(busy);
    return(-1);
}