Exemple #1
0
int
main(int argc, char **argv)
{
    const int exit_sig[] = {SIGINT, SIGTERM, SIGHUP};
    char *fqdn = NULL;
    struct passwd *passwd;
    struct tlog_writer *writer = NULL;
    clockid_t clock_id;
    struct timespec timestamp;
    struct tlog_sink *sink = NULL;
    tlog_grc grc;
    ssize_t rc;
    int master_fd;
    pid_t child_pid;
    unsigned int session_id;
    sig_atomic_t last_sigwinch_caught = 0;
    sig_atomic_t new_sigwinch_caught;
    sig_atomic_t last_alarm_caught = 0;
    sig_atomic_t new_alarm_caught;
    bool alarm_set = false;
    bool io_pending = false;
    bool term_attrs_set = false;
    struct termios orig_termios;
    struct termios raw_termios;
    struct winsize winsize;
    struct winsize new_winsize;
    size_t i, j;
    struct sigaction sa;
    struct pollfd fds[FD_IDX_NUM];
    uint8_t input_buf[BUF_SIZE];
    size_t input_pos = 0;
    size_t input_len = 0;
    uint8_t output_buf[BUF_SIZE];
    size_t output_pos = 0;
    size_t output_len = 0;
    struct tlog_pkt pkt = TLOG_PKT_VOID;
    int status = 1;

    (void)argv;
    if (argc > 1) {
        fprintf(stderr, "Arguments are not accepted\n");
        goto cleanup;
    }

    /* Get host FQDN */
    grc = get_fqdn(&fqdn);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed retrieving host FQDN: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Get session ID */
    grc = get_session_id(&session_id);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed retrieving session ID: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Open syslog */
    openlog("tlog", LOG_NDELAY, LOG_LOCAL0);
    /* Create the syslog writer */
    grc = tlog_syslog_writer_create(&writer, LOG_INFO);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed creating syslog writer: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Get effective user entry */
    errno = 0;
    passwd = getpwuid(geteuid());
    if (passwd == NULL) {
        if (errno == 0)
            fprintf(stderr, "User entry not found\n");
        else
            fprintf(stderr, "Failed retrieving user entry: %s\n",
                    strerror(errno));
        goto cleanup;
    }

    /*
     * Choose the clock: try to use coarse monotonic clock (which is faster),
     * if it provides resolution of at least one millisecond.
     */
    if (clock_getres(CLOCK_MONOTONIC_COARSE, &timestamp) == 0 &&
        timestamp.tv_sec == 0 && timestamp.tv_nsec < 1000000) {
        clock_id = CLOCK_MONOTONIC_COARSE;
    } else if (clock_getres(CLOCK_MONOTONIC, NULL) == 0) {
        clock_id = CLOCK_MONOTONIC;
    } else {
        fprintf(stderr, "No clock to use\n");
        goto cleanup;
    }

    /* Create the log sink */
    grc = tlog_sink_create(&sink, writer, fqdn, passwd->pw_name,
                           session_id, BUF_SIZE);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed creating log sink: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Get terminal attributes */
    rc = tcgetattr(STDOUT_FILENO, &orig_termios);
    if (rc < 0) {
        fprintf(stderr, "Failed retrieving tty attributes: %s\n",
                strerror(errno));
        goto cleanup;
    }

    /* Get terminal window size */
    rc = ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize);
    if (rc < 0) {
        fprintf(stderr, "Failed retrieving tty window size: %s\n",
                strerror(errno));
        goto cleanup;
    }

    /* Fork a child under a slave pty */
    child_pid = forkpty(&master_fd, NULL, &orig_termios, &winsize);
    if (child_pid < 0) {
        fprintf(stderr, "Failed forking a pty: %s\n", strerror(errno));
        goto cleanup;
    } else if (child_pid == 0) {
        /*
         * Child
         */
        execl("/bin/bash", "/bin/bash", NULL);
        fprintf(stderr, "Failed to execute the shell: %s",
                strerror(errno));
        goto cleanup;
    }

    /*
     * Parent
     */
    /* Log initial window size */
    clock_gettime(clock_id, &timestamp);
    tlog_pkt_init_window(&pkt, &timestamp,
                         winsize.ws_col, winsize.ws_row);
    grc = tlog_sink_write(sink, &pkt);
    tlog_pkt_cleanup(&pkt);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed logging window size: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Setup signal handlers to terminate gracefully */
    for (i = 0; i < ARRAY_SIZE(exit_sig); i++) {
        sigaction(exit_sig[i], NULL, &sa);
        if (sa.sa_handler != SIG_IGN)
        {
            sa.sa_handler = exit_sighandler;
            sigemptyset(&sa.sa_mask);
            for (j = 0; j < ARRAY_SIZE(exit_sig); j++)
                sigaddset(&sa.sa_mask, exit_sig[j]);
            /* NOTE: no SA_RESTART on purpose */
            sa.sa_flags = 0;
            sigaction(exit_sig[i], &sa, NULL);
        }
    }

    /* Setup WINCH signal handler */
    sa.sa_handler = winch_sighandler;
    sigemptyset(&sa.sa_mask);
    /* NOTE: no SA_RESTART on purpose */
    sa.sa_flags = 0;
    sigaction(SIGWINCH, &sa, NULL);

    /* Setup ALRM signal handler */
    sa.sa_handler = alarm_sighandler;
    sigemptyset(&sa.sa_mask);
    /* NOTE: no SA_RESTART on purpose */
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    /* Switch the terminal to raw mode */
    raw_termios = orig_termios;
    raw_termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
    raw_termios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
                             INPCK | ISTRIP | IXON | PARMRK);
    raw_termios.c_oflag &= ~OPOST;
    raw_termios.c_cc[VMIN] = 1;
    raw_termios.c_cc[VTIME] = 0;
    rc = tcsetattr(STDOUT_FILENO, TCSAFLUSH, &raw_termios);
    if (rc < 0) {
        fprintf(stderr, "Failed setting tty attributes: %s\n",
                strerror(errno));
        goto cleanup;
    }
    term_attrs_set = true;

    /*
     * Transfer I/O and window changes
     */
    fds[FD_IDX_STDIN].fd        = STDIN_FILENO;
    fds[FD_IDX_STDIN].events    = POLLIN;
    fds[FD_IDX_MASTER].fd       = master_fd;
    fds[FD_IDX_MASTER].events   = POLLIN;

    while (exit_signum == 0) {
        /*
         * Handle SIGWINCH
         */
        new_sigwinch_caught = sigwinch_caught;
        if (new_sigwinch_caught != last_sigwinch_caught) {
            /* Retrieve window size */
            rc = ioctl(STDOUT_FILENO, TIOCGWINSZ, &new_winsize);
            if (rc < 0) {
                if (errno == EBADF)
                    status = 0;
                else
                    fprintf(stderr, "Failed retrieving window size: %s\n",
                            strerror(errno));
                break;
            }
            /* If window size has changed */
            if (new_winsize.ws_row != winsize.ws_row ||
                new_winsize.ws_col != winsize.ws_col) {
                /* Log window size */
                clock_gettime(clock_id, &timestamp);
                tlog_pkt_init_window(&pkt, &timestamp,
                                     new_winsize.ws_col,
                                     new_winsize.ws_row);
                grc = tlog_sink_write(sink, &pkt);
                tlog_pkt_cleanup(&pkt);
                if (grc != TLOG_RC_OK) {
                    fprintf(stderr, "Failed logging window size: %s\n",
                            tlog_grc_strerror(grc));
                    break;
                }
                /* Propagate window size */
                rc = ioctl(master_fd, TIOCSWINSZ, &new_winsize);
                if (rc < 0) {
                    if (errno == EBADF)
                        status = 0;
                    else
                        fprintf(stderr, "Failed setting window size: %s\n",
                                strerror(errno));
                    break;
                }
                winsize = new_winsize;
            }
            /* Mark SIGWINCH processed */
            last_sigwinch_caught = new_sigwinch_caught;
        }

        /*
         * Deliver I/O
         */
        clock_gettime(clock_id, &timestamp);

        if (input_pos < input_len) {
            rc = write(master_fd, input_buf + input_pos,
                       input_len - input_pos);
            if (rc >= 0) {
                if (rc > 0) {
                    /* Log delivered input */
                    tlog_pkt_init_io(&pkt, &timestamp,
                                     false, input_buf + input_pos,
                                     false, (size_t)rc);
                    grc = tlog_sink_write(sink, &pkt);
                    tlog_pkt_cleanup(&pkt);
                    if (grc != TLOG_RC_OK) {
                        fprintf(stderr, "Failed logging input: %s\n",
                                tlog_grc_strerror(grc));
                        break;
                    }
                    io_pending = true;
                    input_pos += rc;
                }
                /* If interrupted by a signal handler */
                if (input_pos < input_len)
                    continue;
                /* Exhausted */
                input_pos = input_len = 0;
            } else {
                if (errno == EINTR)
                    continue;
                else if (errno == EBADF || errno == EINVAL)
                    status = 0;
                else
                    fprintf(stderr, "Failed to write to master: %s\n",
                            strerror(errno));
                break;
            };
        }
        if (output_pos < output_len) {
            rc = write(STDOUT_FILENO, output_buf + output_pos,
                       output_len - output_pos);
            if (rc >= 0) {
                if (rc > 0) {
                    /* Log delivered output */
                    tlog_pkt_init_io(&pkt, &timestamp,
                                     true, output_buf + output_pos,
                                     false, (size_t)rc);
                    grc = tlog_sink_write(sink, &pkt);
                    tlog_pkt_cleanup(&pkt);
                    if (grc != TLOG_RC_OK) {
                        fprintf(stderr, "Failed logging output: %s\n",
                                tlog_grc_strerror(grc));
                        break;
                    }
                    io_pending = true;
                    output_pos += rc;
                }
                /* If interrupted by a signal handler */
                if (output_pos < output_len)
                    continue;
                /* Exhausted */
                output_pos = output_len = 0;
            } else {
                if (errno == EINTR)
                    continue;
                else if (errno == EBADF || errno == EINVAL)
                    status = 0;
                else
                    fprintf(stderr, "Failed to write to stdout: %s\n",
                            strerror(errno));
                break;
            };
        }

        /*
         * Handle I/O latency limit
         */
        new_alarm_caught = alarm_caught;
        if (new_alarm_caught != last_alarm_caught) {
            alarm_set = false;
            grc = tlog_sink_flush(sink);
            if (grc != TLOG_RC_OK) {
                fprintf(stderr, "Failed flushing I/O log: %s\n",
                        tlog_grc_strerror(grc));
                goto cleanup;
            }
            last_alarm_caught = new_alarm_caught;
            io_pending = false;
        } else if (io_pending && !alarm_set) {
            alarm(IO_LATENCY);
            alarm_set = true;
        }

        /*
         * Wait for I/O
         */
        rc = poll(fds, sizeof(fds) / sizeof(fds[0]), -1);
        if (rc < 0) {
            if (errno == EINTR)
                continue;
            else
                fprintf(stderr, "Failed waiting for I/O: %s\n",
                        strerror(errno));
            break;
        }

        /*
         * Retrieve I/O
         *
         * NOTE: Reading master first in case child went away.
         *       Otherwise writing to it can block
         */
        if (fds[FD_IDX_MASTER].revents & (POLLIN | POLLHUP | POLLERR)) {
            rc = read(master_fd, output_buf, sizeof(output_buf));
            if (rc <= 0) {
                if (rc < 0 && errno == EINTR)
                    continue;
                status = 0;
                break;
            }
            output_len = rc;
        }
        if (fds[FD_IDX_STDIN].revents & (POLLIN | POLLHUP | POLLERR)) {
            rc = read(STDIN_FILENO, input_buf, sizeof(input_buf));
            if (rc <= 0) {
                if (rc < 0 && errno == EINTR)
                    continue;
                status = 0;
                break;
            }
            input_len = rc;
        }
    }

    /* Cut I/O log (write incomplete characters as binary) */
    grc = tlog_sink_cut(sink);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed cutting-off I/O log: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Flush I/O log */
    grc = tlog_sink_flush(sink);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed flushing I/O log: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

cleanup:

    tlog_sink_destroy(sink);
    tlog_writer_destroy(writer);
    free(fqdn);

    /* Restore signal handlers */
    for (i = 0; i < ARRAY_SIZE(exit_sig); i++) {
        sigaction(exit_sig[i], NULL, &sa);
        if (sa.sa_handler != SIG_IGN)
            signal(exit_sig[i], SIG_DFL);
    }

    /* Restore terminal attributes */
    if (term_attrs_set) {
        rc = tcsetattr(STDOUT_FILENO, TCSAFLUSH, &orig_termios);
        if (rc < 0 && errno != EBADF) {
            fprintf(stderr, "Failed restoring tty attributes: %s\n",
                    strerror(errno));
            return 1;
        }
    }

    /* Reproduce the exit signal to get proper exit status */
    if (exit_signum != 0)
        raise(exit_signum);

    return status;
}
Exemple #2
0
static tlog_grc
run(const char *progname, struct json_object *conf)
{
    tlog_grc grc;
    struct json_object *obj;
    int64_t num;
    unsigned int latency;
    unsigned int log_mask;
    const char *shell_path;
    char **shell_argv = NULL;
    const struct timespec delay_min = TLOG_DELAY_MIN_TIMESPEC;
    clockid_t clock_id;
    struct timespec timestamp;
    struct tlog_sink *log_sink = NULL;
    struct tlog_sink *tty_sink = NULL;
    struct tlog_source *tty_source = NULL;
    ssize_t rc;
    int master_fd;
    pid_t child_pid;
    bool term_attrs_set = false;
    struct termios orig_termios;
    struct termios raw_termios;
    struct winsize winsize;

    assert(progname != NULL);

    /* Check for the help flag */
    if (json_object_object_get_ex(conf, "help", &obj)) {
        if (json_object_get_boolean(obj)) {
            tlog_rec_conf_cmd_help(stdout, progname);
            grc = TLOG_RC_OK;
            goto cleanup;
        }
    }

    /* Prepare shell command line */
    grc = get_shell(conf, &shell_path, &shell_argv);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed building shell command line: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Read the log latency */
    if (!json_object_object_get_ex(conf, "latency", &obj)) {
        fprintf(stderr, "Log latency is not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }
    num = json_object_get_int64(obj);
    latency = (unsigned int)num;

    /* Read log mask */
    if (!json_object_object_get_ex(conf, "log", &obj)) {
        fprintf(stderr, "Logged data set parameters are not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }
    grc = get_log_mask(obj, &log_mask);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed reading log mask: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Create the log sink */
    grc = create_log_sink(&log_sink, conf);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed creating log sink: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /*
     * Choose the clock: try to use coarse monotonic clock (which is faster),
     * if it provides the required resolution.
     */
    if (clock_getres(CLOCK_MONOTONIC_COARSE, &timestamp) == 0 &&
        tlog_timespec_cmp(&timestamp, &delay_min) <= 0) {
        clock_id = CLOCK_MONOTONIC_COARSE;
    } else if (clock_getres(CLOCK_MONOTONIC, NULL) == 0) {
        clock_id = CLOCK_MONOTONIC;
    } else {
        fprintf(stderr, "No clock to use\n");
        goto cleanup;
    }

    /* Get terminal attributes */
    rc = tcgetattr(STDOUT_FILENO, &orig_termios);
    if (rc < 0) {
        fprintf(stderr, "Failed retrieving tty attributes: %s\n",
                strerror(errno));
        goto cleanup;
    }

    /* Get terminal window size */
    rc = ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize);
    if (rc < 0) {
        fprintf(stderr, "Failed retrieving tty window size: %s\n",
                strerror(errno));
        goto cleanup;
    }

    /* Fork a child under a slave pty */
    child_pid = forkpty(&master_fd, NULL, &orig_termios, &winsize);
    if (child_pid < 0) {
        grc = TLOG_GRC_ERRNO;
        fprintf(stderr, "Failed forking a pty: %s\n", tlog_grc_strerror(grc));
        goto cleanup;
    } else if (child_pid == 0) {
        /*
         * Child
         */
        execv(shell_path, shell_argv);
        grc = TLOG_GRC_ERRNO;
        fprintf(stderr, "Failed executing the shell: %s",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /*
     * Parent
     */
    /* Read and output the notice */
    if (json_object_object_get_ex(conf, "notice", &obj)) {
        fprintf(stderr, "%s", json_object_get_string(obj));
    }

    /* Switch the terminal to raw mode */
    raw_termios = orig_termios;
    raw_termios.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
    raw_termios.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR |
                             INPCK | ISTRIP | IXON | PARMRK);
    raw_termios.c_oflag &= ~OPOST;
    raw_termios.c_cc[VMIN] = 1;
    raw_termios.c_cc[VTIME] = 0;
    rc = tcsetattr(STDOUT_FILENO, TCSAFLUSH, &raw_termios);
    if (rc < 0) {
        grc = TLOG_GRC_ERRNO;
        fprintf(stderr, "Failed setting tty attributes: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }
    term_attrs_set = true;

    /* Create the TTY source */
    grc = tlog_tty_source_create(&tty_source, STDIN_FILENO,
                                 master_fd, STDOUT_FILENO,
                                 4096, clock_id);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed creating TTY source: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Create the TTY sink */
    grc = tlog_tty_sink_create(&tty_sink, master_fd,
                               STDOUT_FILENO, master_fd);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed creating TTY sink: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Transfer and log the data until interrupted or either end is closed */
    grc = transfer(tty_source, log_sink, tty_sink, latency, log_mask);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed transferring TTY data: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    grc = TLOG_RC_OK;

cleanup:

    tlog_sink_destroy(log_sink);
    tlog_sink_destroy(tty_sink);
    tlog_source_destroy(tty_source);

    /* Free shell argv list */
    if (shell_argv != NULL) {
        for (size_t i = 0; shell_argv[i] != NULL; i++) {
            free(shell_argv[i]);
        }
        free(shell_argv);
    }

    /* Restore terminal attributes */
    if (term_attrs_set) {
        rc = tcsetattr(STDOUT_FILENO, TCSAFLUSH, &orig_termios);
        if (rc < 0 && errno != EBADF) {
            fprintf(stderr, "Failed restoring tty attributes: %s\n",
                    strerror(errno));
            return 1;
        }
    }

    return grc;
}
Exemple #3
0
/**
 * Get shell parameters from a configuration JSON object.
 *
 * @param conf  Configuration JSON object.
 * @param ppath Location for the shell path pointer, references the
 *              configuration object.
 * @param pargv Location for the NULL-terminated shell argv array, to be
 *              freed, both the array and the elements.
 *
 * @return Global return code.
 */
static tlog_grc
get_shell(struct json_object *conf, const char **ppath, char ***pargv)
{
    tlog_grc grc;
    struct json_object *obj;
    struct json_object *args;
    bool login;
    bool command;
    const char *path;
    char *name = NULL;
    char *buf = NULL;
    char **argv = NULL;
    size_t argi;
    char *arg = NULL;
    int i;

    /* Read the login flag */
    login = json_object_object_get_ex(conf, "login", &obj) &&
            json_object_get_boolean(obj);

    /* Read the shell path */
    if (!json_object_object_get_ex(conf, "shell", &obj)) {
        fprintf(stderr, "Shell is not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }
    path = json_object_get_string(obj);

    /* Format shell name */
    buf = strdup(path);
    if (buf == NULL) {
        grc = TLOG_GRC_ERRNO;
        fprintf(stderr, "Failed duplicating shell path: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }
    if (login) {
        const char *str = basename(buf);
        name = malloc(strlen(str) + 2);
        if (name == NULL) {
            grc = TLOG_GRC_ERRNO;
            fprintf(stderr, "Failed allocating shell name: %s\n",
                    tlog_grc_strerror(grc));
            goto cleanup;
        }
        name[0] = '-';
        strcpy(name + 1, str);
        free(buf);
    } else {
        name = buf;
    }
    buf = NULL;

    /* Read the command flag */
    command = json_object_object_get_ex(conf, "command", &obj) &&
              json_object_get_boolean(obj);

    /* Read and check the positional arguments */
    if (!json_object_object_get_ex(conf, "args", &args)) {
        fprintf(stderr, "Positional arguments are not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }
    if (command && json_object_array_length(args) == 0) {
        fprintf(stderr, "Command string is not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }

    /* Create and fill argv list */
    argv = calloc(1 + (command ? 1 : 0) + json_object_array_length(args) + 1,
                  sizeof(*argv));
    if (argv == NULL) {
        grc = TLOG_GRC_ERRNO;
        fprintf(stderr, "Failed allocating argv list: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }
    argi = 0;
    argv[argi++] = name;
    name = NULL;
    if (command) {
        arg = strdup("-c");
        if (arg == NULL) {
            grc = TLOG_GRC_ERRNO;
            fprintf(stderr, "Failed allocating shell argv[#%zu]: %s\n",
                    argi, tlog_grc_strerror(grc));
            goto cleanup;
        }
        argv[argi++] = arg;
        arg = NULL;
    }
    for (i = 0; i < json_object_array_length(args); i++, argi++) {
        obj = json_object_array_get_idx(args, i);
        arg = strdup(json_object_get_string(obj));
        if (arg == NULL) {
            grc = TLOG_GRC_ERRNO;
            fprintf(stderr, "Failed allocating shell argv[#%zu]: %s\n",
                    argi, tlog_grc_strerror(grc));
            goto cleanup;
        }
        argv[argi] = arg;
        arg = NULL;
    }

    /* Output results */
    *ppath = path;
    *pargv = argv;
    argv = NULL;
    grc = TLOG_RC_OK;
cleanup:

    free(arg);
    if (argv != NULL) {
        for (argi = 0; argv[argi] != NULL; argi++) {
            free(argv[argi]);
        }
        free(argv);
    }
    free(name);
    free(buf);
    return grc;
}
Exemple #4
0
/**
 * Transfer and log terminal data until interrupted or either end closes.
 *
 * @param tty_source    TTY data source.
 * @param log_sink      Log sink.
 * @param tty_sink      TTY data sink.
 * @param latency       Number of seconds to wait before flushing logged data.
 * @param log_mask      Logging mask with bits indexed by enum tlog_log_item.
 *
 * @return Global return code.
 */
static tlog_grc
transfer(struct tlog_source    *tty_source,
         struct tlog_sink      *log_sink,
         struct tlog_sink      *tty_sink,
         unsigned int           latency,
         unsigned int           log_mask)
{
    const int exit_sig[] = {SIGINT, SIGTERM, SIGHUP};
    tlog_grc return_grc = TLOG_RC_OK;
    tlog_grc grc;
    size_t i, j;
    struct sigaction sa;
    bool alarm_set = false;
    bool log_pending = false;
    sig_atomic_t last_alarm_caught = 0;
    sig_atomic_t new_alarm_caught;
    struct tlog_pkt pkt = TLOG_PKT_VOID;
    struct tlog_pkt_pos tty_pos = TLOG_PKT_POS_VOID;
    struct tlog_pkt_pos log_pos = TLOG_PKT_POS_VOID;

    /* Setup signal handlers to terminate gracefully */
    for (i = 0; i < TLOG_ARRAY_SIZE(exit_sig); i++) {
        sigaction(exit_sig[i], NULL, &sa);
        if (sa.sa_handler != SIG_IGN)
        {
            sa.sa_handler = exit_sighandler;
            sigemptyset(&sa.sa_mask);
            for (j = 0; j < TLOG_ARRAY_SIZE(exit_sig); j++)
                sigaddset(&sa.sa_mask, exit_sig[j]);
            /* NOTE: no SA_RESTART on purpose */
            sa.sa_flags = 0;
            sigaction(exit_sig[i], &sa, NULL);
        }
    }

    /* Setup ALRM signal handler */
    sa.sa_handler = alarm_sighandler;
    sigemptyset(&sa.sa_mask);
    /* NOTE: no SA_RESTART on purpose */
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    /*
     * Transfer I/O and window changes
     */
    while (exit_signum == 0) {
        /* Handle latency limit */
        new_alarm_caught = alarm_caught;
        if (new_alarm_caught != last_alarm_caught) {
            alarm_set = false;
            grc = tlog_sink_flush(log_sink);
            if (grc != TLOG_RC_OK) {
                fprintf(stderr, "Failed flushing log: %s\n",
                        tlog_grc_strerror(grc));
                return_grc = grc;
                goto cleanup;
            }
            last_alarm_caught = new_alarm_caught;
            log_pending = false;
        } else if (log_pending && !alarm_set) {
            alarm(latency);
            alarm_set = true;
        }

        /* Deliver logged data if any */
        if (tlog_pkt_pos_cmp(&tty_pos, &log_pos) < 0) {
            grc = tlog_sink_write(tty_sink, &pkt, &tty_pos, &log_pos);
            if (grc != TLOG_RC_OK) {
                if (grc == TLOG_GRC_FROM(errno, EINTR)) {
                    continue;
                } else if (grc != TLOG_GRC_FROM(errno, EBADF) &&
                           grc != TLOG_GRC_FROM(errno, EINVAL)) {
                    fprintf(stderr, "Failed writing terminal data: %s\n",
                            tlog_grc_strerror(grc));
                    return_grc = grc;
                }
                break;
            }
            continue;
        }

        /* Log the received data, if any */
        if (tlog_pkt_pos_is_in(&log_pos, &pkt)) {
            /* If asked to log this type of packet */
            if (log_mask & (1 << tlog_log_item_from_pkt(&pkt))) {
                grc = tlog_sink_write(log_sink, &pkt, &log_pos, NULL);
                if (grc != TLOG_RC_OK &&
                    grc != TLOG_GRC_FROM(errno, EINTR)) {
                    fprintf(stderr, "Failed logging terminal data: %s\n",
                            tlog_grc_strerror(grc));
                    return_grc = grc;
                    goto cleanup;
                }
                log_pending = true;
            } else {
                tlog_pkt_pos_move_past(&log_pos, &pkt);
            }
            continue;
        }

        /* Read new data */
        tlog_pkt_cleanup(&pkt);
        log_pos = TLOG_PKT_POS_VOID;
        tty_pos = TLOG_PKT_POS_VOID;
        grc = tlog_source_read(tty_source, &pkt);
        if (grc != TLOG_RC_OK) {
            if (grc == TLOG_GRC_FROM(errno, EINTR)) {
                continue;
            } else if (grc != TLOG_GRC_FROM(errno, EBADF) &&
                       grc != TLOG_GRC_FROM(errno, EIO)) {
                fprintf(stderr, "Failed reading terminal data: %s\n",
                        tlog_grc_strerror(grc));
                return_grc = grc;
            }
            break;
        } else if (tlog_pkt_is_void(&pkt)) {
            break;
        }
    }

    /* Cut the log (write incomplete characters as binary) */
    grc = tlog_sink_cut(log_sink);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed cutting-off the log: %s\n",
                tlog_grc_strerror(grc));
        if (return_grc == TLOG_RC_OK) {
            return_grc = grc;
        }
        goto cleanup;
    }

    /* Flush the log */
    grc = tlog_sink_flush(log_sink);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed flushing the log: %s\n",
                tlog_grc_strerror(grc));
        if (return_grc == TLOG_RC_OK) {
            return_grc = grc;
        }
        goto cleanup;
    }

cleanup:

    tlog_pkt_cleanup(&pkt);
    /* Stop the timer */
    alarm(0);
    /* Restore signal handlers */
    signal(SIGALRM, SIG_DFL);
    for (i = 0; i < TLOG_ARRAY_SIZE(exit_sig); i++) {
        sigaction(exit_sig[i], NULL, &sa);
        if (sa.sa_handler != SIG_IGN)
            signal(exit_sig[i], SIG_DFL);
    }

    return return_grc;
}
Exemple #5
0
/**
 * Create the log sink according to configuration.
 *
 * @param psink Location for the created sink pointer.
 * @param conf  Configuration JSON object.
 *
 * @return Global return code.
 */
static tlog_grc
create_log_sink(struct tlog_sink **psink, struct json_object *conf)
{
    tlog_grc grc;
    int64_t num;
    const char *str;
    struct json_object *obj;
    struct tlog_sink *sink = NULL;
    struct tlog_json_writer *writer = NULL;
    int fd = -1;
    char *fqdn = NULL;
    struct passwd *passwd;
    unsigned int session_id;

    /*
     * Create the writer
     */
    if (!json_object_object_get_ex(conf, "writer", &obj)) {
        fprintf(stderr, "Writer type is not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }
    str = json_object_get_string(obj);
    if (strcmp(str, "file") == 0) {
        struct json_object *conf_file;

        /* Get file writer conf container */
        if (!json_object_object_get_ex(conf, "file", &conf_file)) {
            fprintf(stderr, "File writer parameters are not specified\n");
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }

        /* Get the file path */
        if (!json_object_object_get_ex(conf_file, "path", &obj)) {
            fprintf(stderr, "Log file path is not specified\n");
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }
        str = json_object_get_string(obj);

        /* Open the file */
        fd = open(str, O_WRONLY | O_CREAT | O_APPEND, S_IRWXU | S_IRWXG);
        if (fd < 0) {
            grc = TLOG_GRC_ERRNO;
            fprintf(stderr, "Failed opening log file \"%s\": %s\n",
                    str, tlog_grc_strerror(grc));
            goto cleanup;
        }

        /* Create the writer, letting it take over the FD */
        grc = tlog_fd_json_writer_create(&writer, fd, true);
        if (grc != TLOG_RC_OK) {
            fprintf(stderr, "Failed creating file writer: %s\n",
                    tlog_grc_strerror(grc));
            goto cleanup;
        }
        fd = -1;
    } else if (strcmp(str, "syslog") == 0) {
        struct json_object *conf_syslog;
        int facility;
        int priority;

        /* Get syslog writer conf container */
        if (!json_object_object_get_ex(conf, "syslog", &conf_syslog)) {
            fprintf(stderr, "Syslog writer parameters are not specified\n");
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }

        /* Get facility */
        if (!json_object_object_get_ex(conf_syslog, "facility", &obj)) {
            fprintf(stderr, "Syslog facility is not specified\n");
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }
        str = json_object_get_string(obj);
        facility = tlog_syslog_facility_from_str(str);
        if (facility < 0) {
            fprintf(stderr, "Unknown syslog facility: %s\n", str);
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }

        /* Get priority */
        if (!json_object_object_get_ex(conf_syslog, "priority", &obj)) {
            fprintf(stderr, "Syslog priority is not specified\n");
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }
        str = json_object_get_string(obj);
        priority = tlog_syslog_priority_from_str(str);
        if (priority < 0) {
            fprintf(stderr, "Unknown syslog priority: %s\n", str);
            grc = TLOG_RC_FAILURE;
            goto cleanup;
        }

        /* Create the writer */
        openlog("tlog", LOG_NDELAY, facility);
        grc = tlog_syslog_json_writer_create(&writer, priority);
        if (grc != TLOG_RC_OK) {
            fprintf(stderr, "Failed creating syslog writer: %s\n",
                    tlog_grc_strerror(grc));
            goto cleanup;
        }
    } else {
        fprintf(stderr, "Unknown writer type: %s\n", str);
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }

    /*
     * Create the sink
     */
    /* Get host FQDN */
    grc = get_fqdn(&fqdn);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed retrieving host FQDN: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Get session ID */
    grc = get_session_id(&session_id);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed retrieving session ID: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }

    /* Get effective user entry */
    errno = 0;
    passwd = getpwuid(geteuid());
    if (passwd == NULL) {
        if (errno == 0) {
            grc = TLOG_RC_FAILURE;
            fprintf(stderr, "User entry not found\n");
        } else {
            grc = TLOG_GRC_ERRNO;
            fprintf(stderr, "Failed retrieving user entry: %s\n",
                    tlog_grc_strerror(grc));
        }
        goto cleanup;
    }

    /* Get the maximum payload size */
    if (!json_object_object_get_ex(conf, "payload", &obj)) {
        fprintf(stderr, "Maximum payload size is not specified\n");
        grc = TLOG_RC_FAILURE;
        goto cleanup;
    }
    num = json_object_get_int64(obj);

    /* Create the sink, letting it take over the writer */
    grc = tlog_json_sink_create(&sink, writer, true,
                                fqdn, passwd->pw_name,
                                session_id, (size_t)num);
    if (grc != TLOG_RC_OK) {
        fprintf(stderr, "Failed creating log sink: %s\n",
                tlog_grc_strerror(grc));
        goto cleanup;
    }
    writer = NULL;

    *psink = sink;
    sink = NULL;
    grc = TLOG_RC_OK;
cleanup:

    if (fd >= 0) {
        close(fd);
    }
    tlog_json_writer_destroy(writer);
    free(fqdn);
    tlog_sink_destroy(sink);
    return grc;
}