main.c
author Lluís Batlle <viric@viric.name>
Thu, 20 Mar 2014 16:33:27 +0100
changeset 97 eea77d5a624c
parent 94 330324fc7c20
permissions -rw-r--r--
Merging the savefile branch. I hope it works; I even don't remember it.

/*
    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 <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/select.h>

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

extern char *optarg;
extern int optind, opterr, optopt;

/* From main.h given external */
int app_stdin;
int app_stdout;
int app_stderr;

struct Command_line command_line;

static const char version[] = "0.4.1";

static int showhelp(const char *pname)
{
    printf("tm v%s - terminal mixer,  Copyright (C) 2007  "
            "Lluis Batlle i Rossell\n",
            version);
    printf("usage: %s [opts] [appcommand] [param1] [param2] ...\n", pname);
    printf(" If you give _appcommand_, it starts the application and\n");
    printf(" serves it through a Unix socket on $TM_SOCKET or "
            "/tmp/tm-socket.UID,\n");
    printf(" unless '-p' is used.\n");
    printf(" Without _appcomand_, starts a the Unix socket client.\n");
    printf("options: \n");
    printf(" -h     Show help.\n");
    printf(" -P     Run the child as connected to a pipe (default).\n");
    printf(" -t     Run the child as connected to a terminal (raw mode in "
        "client).\n");
    printf(" -n     Unlink the program from the terminal (as 'nohup').\n");
    printf(" -N MAX Serve at most MAX sockets for each transport (1 default)."
            "\n");
    printf(" -w     The remote clients can write to the application.\n");
    printf(" -C     The remote clients end will close app's stdin.\n");
    printf(" -p NUM Listen to tcp port NUM for 'telnet', and not listen to "
            "any\n"
           "        Unix socket.\n");
    printf(" -E     Echo remote input to the server terminal.\n");
#ifdef linux
    printf(" -e dev[:port] Also serve/connect using raw ethernet, device 'dev'.\n");
    printf(" -c adr Connect to address (MAC if eth).\n");
    printf(" -s fil Save the child stdin/stdout to the filename 'fil'.\n");
#endif /* linux */
    printf(" -x     Send xterm's resize control string to clients.\n");
    return 0;
}

static int my_getopt(int argc, char * argv[], const char *optstring)
{
    char *old_getopt_env;
    static char getopt_env[100] = "POSIXLY_CORRECT=YES";
    int res;

    old_getopt_env = getenv("POSIXLY_CORRECT");
    putenv(getopt_env);

    res = getopt(argc, argv, optstring);

    if (old_getopt_env == 0)
    {
        putenv("POSIXLY_CORRECT");
    }
    else
        snprintf(getopt_env, sizeof getopt_env, "POSIXLY_CORRECT=%s",
                old_getopt_env);

    return res;
}

static void parse_eth_device(char *str)
{
    char *p;
    for (p = str; *p != 0 && *p != ':'; ++p);
    if (*p == ':')
    {
        *p = '\0'; /* We want only the eth device name in str */
        ++p;
        command_line.eth_port = atoi(p);
    }

    /* The string will not have any colon or port. */
    command_line.eth_device = strdup(str);
}

static void default_command_line()
{
    command_line.is_server = 0;
    command_line.s_param.use_blocking_sockets = 1;
    command_line.s_param.run_in_subterminal = 0;
    command_line.s_param.max_served = 1;
    command_line.s_param.serve_unix = 1;
    command_line.s_param.serve_tcp = 0;
    command_line.s_param.send_xterm_resize = 1;
    command_line.s_param.client_may_close_app_stdin = 0;
    command_line.s_param.nohup = 0;
    command_line.s_param.client_can_write = 0;
    command_line.s_param.echo_in_local_terminal = 0;
    command_line.s_param.send_xterm_resize = 0;
    command_line.s_param.serve_eth = 0;
    command_line.s_param.savefile = 0;

    command_line.tcp_port = 40000; /* Arbitrary */
    command_line.eth_port = 100;   /* Arbitrary */
    command_line.buffer_size = 4096; /* Arbitrary */
    command_line.eth_device = 0;
    get_unix_path(); /* for command_line.unix_path */

    command_line.c_param.transport = UNIX;
    command_line.c_param.wait_until_char = -1;
    command_line.c_param.raw_mode = 0;
    command_line.c_param.server_address = 0; /* TODO: free it */
}

static int parse_opts(int argc, char * argv[])
{
    int c;
    extern char *optarg;
    extern int optind, opterr, optopt;

    while(1) {
        c = my_getopt(argc, argv, "s:tp:nBxdPN:hwCED"
#ifdef linux
                "c:e:"
#endif /* linux */
                );

        if (c == -1)
            break;

        switch(c)
        {
            case 't':
                command_line.s_param.run_in_subterminal = 1;
                command_line.c_param.raw_mode = 1;
                break;
            case 'P':
                command_line.s_param.run_in_subterminal = 0;
                break;
            case 'B':
                command_line.s_param.use_blocking_sockets = 0;
                break;
            case 'n':
                command_line.s_param.nohup = 1;
                break;
            case 'p':
                command_line.tcp_port = atoi(optarg);
                command_line.s_param.serve_tcp = 1;
                command_line.s_param.serve_unix = 0;
                break;
            case 'N':
                command_line.s_param.max_served = atoi(optarg);
                break;
            case 'c':
                command_line.c_param.server_address = strdup(optarg);
                break;
            case 'h':
                showhelp(argv[0]);
                exit(0);
                break;
            case 'w':
                command_line.s_param.client_can_write = 1;
                break;
            case 'C':
                command_line.s_param.client_may_close_app_stdin = 1;
                break;
            case 'x':
                command_line.s_param.send_xterm_resize = 1;
                break;
            case 'E':
                command_line.s_param.echo_in_local_terminal = 1;
                break;
            case 'D':
                should_dump = 1;
                break;
            case 'e':
                command_line.s_param.serve_eth = 1;
                command_line.s_param.serve_unix = 0;
                parse_eth_device(optarg);
                command_line.c_param.transport = ETHERNET;
                break;
            case 's':
                command_line.s_param.savefile = strdup(optarg);
                break;
            case '?':
                error("Wrong option %c.\n", optopt);
        }
    }

    if (command_line.s_param.use_blocking_sockets == 0)
        not_implemented("Not using blocking sockets.");

    if (optind < argc)
    {
        command_line.is_server = 1;
        command_line.s_param.command = &argv[optind];
    }
    return 0;
}

int main(int argc, char * argv[])
{
    int res;

    default_command_line();
    parse_opts(argc, argv);
    init_stream_buffers();

    if (command_line.is_server)
        res = server();
    else
        res = client();
    return res;
}