app_control.c
author viric@mandarina
Thu, 21 Aug 2008 23:21:22 +0200
changeset 93 7d9b7a6da507
parent 82 5cbe47923060
child 94 330324fc7c20
permissions -rw-r--r--
Removing direct references to /usr in the Makefile, for nix.

/*
    Terminal Mixer - multi-point multi-user access to terminal applications
    Copyright (C) 2007  LluĂ­s Batlle i Rossell

    Please find the license in the provided COPYING file.
*/
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <sys/select.h>

#include "main.h"
#include "filter.h"
#include "handlers.h"

/* it uses app_stdin, app_stdout, app_stderr.
 * They have value -1 if not opened. */

static struct FilterRules *fr;

void write_newline_cb(struct FilterRules *myfr,
        const struct FFilter *ff, char *obuf, int *olen,
        const char *ibuf, int pos)
{
    obuf[(*olen)++] = '\n';
}

void app_control_start()
{
    struct FFilter *ff;
    fr = new_filter_rules();

    /* We don't pay attention to any telnet command */
    ff = new_ftelnet();
    add_ffilter(fr, ff);

    /* The raw terminal will send \r,
     * telnet will send \r\0. If we filter all \0, we're fine.
     * I used to change \r to \n here... It works better if I don't. */

    /* We should trim the telnet's no-operation characters '\0'. */
    ff = new_fstring_len("\0", 1);
    add_ffilter(fr, ff);
}

void app_control_shutdown()
{
}

void app_control_prepare_read_fdset(fd_set *read_set, int *maxfd)
{
    if (app_stdout != -1)
    {
        debugmsg("Considering app_stdout %i", app_stdout);
        FD_SET(app_stdout, read_set);
        *maxfd = max(*maxfd, app_stdout);
    }

    if (app_stderr != -1 && app_stdout != app_stderr)
    {
        debugmsg("Considering app_stderr %i", app_stderr);
        FD_SET(app_stderr, read_set);
        *maxfd = max(*maxfd, app_stderr);
    }
}

/* Return -1, finished stdout/stderr. Else, return 0. */
int app_control_process_read_fdset(fd_set *read_set)
{
    if (app_stdout != -1 && FD_ISSET(app_stdout, read_set))
    {
        int res;
        debugmsg("Read app_stdout %i", app_stdout);
        res = read(app_stdout, stream_buffer, stream_buffer_size);
        if (res == -1 && errno == EIO)
        {
            /* I've noticed that when the app dies, read() returns EIO */
            app_stdout = -1;
            app_stdin = -1;
            return -1;
        }
        if (res == -1 )
            error("Error reading from app.");
        if (res == 0)
        {
            debugmsg("Closing app_stdout %i", app_stdout);
            close(app_stdout);
            if (!command_line.s_param.nohup)
                close(1);
            app_stdout = -1;
            if (app_stdout == app_stderr || app_stderr == -1)
            {
                app_stderr = -1;
                return -1;
            }
        } else
        {
            hex_dump("from app", stream_buffer, res);
            if (!command_line.s_param.nohup)
                write(1, stream_buffer, res);
            if (command_line.s_param.serve_unix)
                s_unix_send_to_connected(stream_buffer, res);
            if (command_line.s_param.serve_tcp)
                s_tcp_send_to_connected(stream_buffer, res);
#ifdef linux
            if (command_line.s_param.serve_eth)
                s_eth_send_to_connected(stream_buffer, res);
#endif /* linux */
        }
    }
    if (app_stderr != -1 && app_stdout != app_stderr &&
            FD_ISSET(app_stderr, read_set))
    {
        int res;
        debugmsg("Read app_stderr %i", app_stdout);
        res = read(app_stderr, stream_buffer, stream_buffer_size);
        if (res == 0)
        {
            debugmsg("Closing app_stderr %i", app_stderr);
            close(app_stderr);
            close(2); /* MOVE */
            app_stderr = -1;
            if (app_stdout == -1)
                return -1;
        } else
        {
            if (!command_line.s_param.nohup)
                write(2, stream_buffer, res);

            if (command_line.s_param.serve_unix)
                s_unix_send_to_connected(stream_buffer, res);
            if (command_line.s_param.serve_tcp)
                s_tcp_send_to_connected(stream_buffer, res);
#ifdef linux
            if (command_line.s_param.serve_eth)
                s_eth_send_to_connected(stream_buffer, res);
#endif /* linux */
        }
    }
    return 0;
}

void app_control_local_send_to_stdin(const char *buffer, size_t size)
{
    if (size == 0)
    {
        close(app_stdin);
        if (app_stdout == app_stdin)
          app_stdout = -1;
        if (app_stderr == app_stdin)
          app_stderr = -1;
    }
    else
    {
        hex_dump("from local to app", buffer, size);
        write(app_stdin, buffer, size);
    }
}

static void write_data_to_app_stdin(const char *buffer, size_t size)
{
    if (size > 0)
    {
        hex_dump("to app", buffer, size);
        if( !command_line.s_param.nohup &&
                command_line.s_param.echo_in_local_terminal)
            write(1, buffer, size);
        write(app_stdin, buffer, size);
    }
}

void app_control_remote_send_to_stdin(const char *buffer, size_t size)
{
    hex_dump("from client prefilter", buffer, size);
    if (command_line.s_param.client_can_write)
    {
        int osize;
        if (size > 0)
        {
            filter_stream(fr, ostream_buffer, &osize,
                    buffer, size);
            if (osize > 0)
                write_data_to_app_stdin(ostream_buffer, osize);
        } else if (size == 0)
        {
            filter_flush(fr, ostream_buffer, &osize);
            if (osize > 0)
                write_data_to_app_stdin(ostream_buffer, osize);
        }

        if (size == 0 && command_line.s_param.client_may_close_app_stdin)
        {
            close(app_stdin);
            if (app_stdout == app_stdin)
              app_stdout = -1;
            if (app_stderr == app_stdin)
              app_stderr = -1;
        }
    }
}

void app_control_avoid_sending(fd_set *read_set)
{
}