Example #1
0
static void timestamp_logfiles(server_conf_t *conf)
{
/*  Writes a timestamp message into all of the console logfiles.
 */
    char *now;
    ListIterator i;
    obj_t *logfile;
    char buf[MAX_LINE];
    int gotLogs = 0;

    now = create_long_time_string(0);
    i = list_iterator_create(conf->objs);
    while ((logfile = list_next(i))) {
        if (!is_logfile_obj(logfile)) {
            continue;
        }
        snprintf(buf, sizeof(buf), "%sConsole [%s] log at %s%s",
            CONMAN_MSG_PREFIX, logfile->aux.logfile.console->name,
            now, CONMAN_MSG_SUFFIX);
        strcpy(&buf[sizeof(buf) - 3], "\r\n");
        write_obj_data(logfile, buf, strlen(buf), 1);
        gotLogs = 1;
    }
    list_iterator_destroy(i);
    free(now);

    /*  If any logfile objs exist, schedule a timer for the next timestamp.
     */
    if (gotLogs) {
        schedule_timestamp(conf);
    }
    return;
}
int write_log_data(obj_t *log, const void *src, int len)
{
/*  Writes a potentially modified version of the buffer (src) of length (len)
 *    into the logfile obj (log); the original (src) buffer is not modified.
 *  This routine is intended for writing data that was read from a console,
 *    not informational messages.
 *  If sanitized logs are enabled, data is stripped to 7-bit ASCII and
 *    control/binary characters are displayed as two-character printable
 *    sequences.
 *  If newline timestamping is enabled, the current timestamp is appended
 *    after each newline.
 *  Returns the number of bytes written into the logfile obj's buffer.
 */
    const int minbuf = 25;              /* cr/lf + timestamp + meta/char */
    unsigned char buf[MAX_BUF_SIZE - 1];
    const unsigned char *p;
    unsigned char *q;
    const unsigned char * const qLast = buf + sizeof(buf);
    int n = 0;

    assert(is_logfile_obj(log));
    assert(sizeof(buf) >= minbuf);

    /*  If no additional processing is needed, listen to Biff Tannen:
     *    "make like a tree and get outta here".
     */
    if (!log->aux.logfile.gotProcessing) {
        return(write_obj_data(log, src, len, 0));
    }
    DPRINTF((15, "Processing %d bytes for [%s] log \"%s\".\n",
        len, log->aux.logfile.console->name, log->name));

    for (p=src, q=buf; len>0; p++, len--) {
        /*
         *  A newline state machine is used to properly sanitize CR/LF line
         *    terminations.  This is responsible for coalescing multiple CRs,
         *    swapping LF/CR to CR/LF, transcribing CR/NUL to CR/LF,
         *    prepending a CR to a lonely LF, and appending a LF to a
         *    lonely CR to prevent characters from being overwritten.
         */
        if (*p == '\r') {
            if (log->aux.logfile.lineState == CONMAN_LOG_LINE_DATA) {
                log->aux.logfile.lineState = CONMAN_LOG_LINE_CR;
            }
            else if (log->aux.logfile.lineState == CONMAN_LOG_LINE_INIT) {
                if (log->aux.logfile.opts.enableTimestamp)
                    q += write_time_string(0, (char *) q, qLast - q);
                log->aux.logfile.lineState = CONMAN_LOG_LINE_CR;
            }
            else {
                ; /* ignore */
            }
        }
        else if (*p == '\n') {
            if (  (log->aux.logfile.lineState == CONMAN_LOG_LINE_INIT)
               || (log->aux.logfile.lineState == CONMAN_LOG_LINE_LF) ) {
                if (log->aux.logfile.opts.enableTimestamp)
                    q += write_time_string(0, (char *) q, qLast - q);
            }
            *q++ = '\r';
            *q++ = '\n';
            log->aux.logfile.lineState = CONMAN_LOG_LINE_LF;
        }
        else if (  (*p == '\0')
                && (  (log->aux.logfile.lineState == CONMAN_LOG_LINE_CR)
                   || (log->aux.logfile.lineState == CONMAN_LOG_LINE_LF) ) ) {
            ; /* ignore */
        }
        else {
            if (log->aux.logfile.lineState == CONMAN_LOG_LINE_CR) {
                *q++ = '\r';
                *q++ = '\n';
            }
            if (log->aux.logfile.lineState != CONMAN_LOG_LINE_DATA) {
                if (log->aux.logfile.opts.enableTimestamp)
                    q += write_time_string(0, (char *) q, qLast - q);
            }
            log->aux.logfile.lineState = CONMAN_LOG_LINE_DATA;

            if (log->aux.logfile.opts.enableSanitize) {

                int c = *p & 0x7F;      /* strip data to 7-bit ASCII */

                if (c < 0x20) {         /* ASCII ctrl-chars */
                    *q++ = (*p & 0x80) ? '~' : '^';
                    *q++ = c + '@';
                }
                else if (c == 0x7F) {   /* ASCII DEL char */
                    *q++ = (*p & 0x80) ? '~' : '^';
                    *q++ = '?';
                }
                else {
                    if (*p & 0x80)
                        *q++ = '`';
                    *q++ = c;
                }
            }
            else {
                *q++ = *p;
            }
        }
        /*  Flush internal buffer before it overruns.
         */
        if ((qLast - q) < minbuf) {
            assert((q >= buf) && (q <= qLast));
            n += write_obj_data(log, buf, q - buf, 0);
            q = buf;
        }
    }
    assert((q >= buf) && (q <= qLast));
    n += write_obj_data(log, buf, q - buf, 0);
    return(n);
}
int open_logfile_obj(obj_t *logfile)
{
/*  (Re)opens the specified 'logfile' obj.
 *  Since this logfile can be re-opened after the daemon has chdir()'d,
 *    it must be specified with an absolute pathname.
 *  Returns 0 if the logfile is successfully opened; o/w, returns -1.
 */
    char  dirname[PATH_MAX];
    int   flags;
    char *now;
    char *msg;

    assert(logfile != NULL);
    assert(is_logfile_obj(logfile));
    assert(logfile->name != NULL);
    assert(logfile->name[0] == '/');
    assert(logfile->aux.logfile.console != NULL);
    assert(logfile->aux.logfile.console->name != NULL);

    if (logfile->fd >= 0) {
        if (close(logfile->fd) < 0)     /* log err and continue */
            log_msg(LOG_WARNING, "Unable to close logfile \"%s\": %s",
                logfile->name, strerror(errno));
        logfile->fd = -1;
    }
    /*  Perform conversion specifier expansion.
     */
    if (logfile->aux.logfile.fmtName) {

        char buf[MAX_LINE];

        if (format_obj_string(buf, sizeof(buf),
          logfile->aux.logfile.console,
          logfile->aux.logfile.fmtName) < 0) {
            log_msg(LOG_WARNING,
                "Unable to open logfile for [%s]: filename exceeded buffer",
                logfile->aux.logfile.console->name);
            logfile->fd = -1;
            return(-1);
        }
        free(logfile->name);
        logfile->name = create_string(buf);
    }
    /*  Create intermediate directories.
     */
    if (get_dir_name(logfile->name, dirname, sizeof(dirname))) {
        (void) create_dirs(dirname);
    }
    /*  Only truncate on the initial open if ZeroLogs was enabled.
     */
    flags = O_WRONLY | O_CREAT | O_APPEND | O_NONBLOCK;
    if (logfile->aux.logfile.gotTruncate) {
        logfile->aux.logfile.gotTruncate = 0;
        flags |= O_TRUNC;
    }
    if ((logfile->fd = open(logfile->name, flags, S_IRUSR | S_IWUSR)) < 0) {
        log_msg(LOG_WARNING, "Unable to open logfile \"%s\": %s",
            logfile->name, strerror(errno));
        return(-1);
    }
    if (logfile->aux.logfile.opts.enableLock
            && (get_write_lock(logfile->fd) < 0)) {
        log_msg(LOG_WARNING, "Unable to lock \"%s\"", logfile->name);
        close(logfile->fd);             /* ignore err on close() */
        logfile->fd = -1;
        return(-1);
    }
    logfile->gotEOF = 0;
    set_fd_nonblocking(logfile->fd);    /* redundant, just playing it safe */
    set_fd_closed_on_exec(logfile->fd);

    now = create_long_time_string(0);
    msg = create_format_string("%sConsole [%s] log opened at %s%s",
        CONMAN_MSG_PREFIX, logfile->aux.logfile.console->name, now,
        CONMAN_MSG_SUFFIX);
    write_obj_data(logfile, msg, strlen(msg), 0);
    free(now);
    free(msg);
    /*
     *  Since the above console log message is not marked "informational",
     *    the test in write_obj_data() to re-init the line state will not
     *    be triggered.  Thusly, we re-initialize the line state here.
     */
    logfile->aux.logfile.lineState = CONMAN_LOG_LINE_INIT;

    DPRINTF((9, "Opened [%s] logfile: fd=%d file=%s.\n",
        logfile->aux.logfile.console->name, logfile->fd, logfile->name));
    return(0);
}
Example #4
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;
}