main.c
author viric@llimona
Fri, 14 Sep 2007 22:27:01 +0200
changeset 13 aec966cdbaa2
parent 12 6a372f8b4b8a
child 14 286b248e402a
permissions -rw-r--r--
Added tag v0.9 for changeset 6a372f8b4b8a

/*
    stdin mix - a mixer/multiplexer for stdin to processes
    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 "main.h"

static const char version[] = "0.9";

static int max(int a, int b)
{
    if (a > b)
        return a;
    return b;
}

static int fork_app(int *opipe, char * const command[])
{
    int p_input[2]; /* from mpg321 to us */
    int p_output[2]; /* from us to mpg321 */
    int pid;
    int res;

    pipe(p_input);
    pipe(p_output);
    opipe[0] = p_input[0]; /* For us to read */
    opipe[1] = p_output[1]; /* For us to write */

    pid = fork();

    switch(pid)
    {
        case 0: /* child */
            close(p_input[0]);
            res = dup2(p_input[1], 1);
            if (res == -1) perror("Dup2 1");
            res = dup2(p_input[1], 2);
            if (res == -1) perror("Dup2 2");
            close(p_input[1]);
            close(p_output[1]);
            res = dup2(p_output[0], 0);
            if (res == -1) perror("Dup2 3");
            close(p_output[0]);

            execvp(command[0], command);

            perror("Cannot execlp mpg321");
            exit(-1);
        case -1:
            perror("Failed fork");
            exit(-1);
        default: /* parent */
            close(p_input[1]);
            close(p_output[0]);
    }

    return pid;
}

static int forward_app_data(int in, int out)
{
    char buf[100];
    int res;

    res = read(in, buf, sizeof(buf));
    if (res > 0)
        write(out, buf, res);

    return res;
}

static void loop(const int *child_pipe, int lsocket)
{
    char buf[100];
    fd_set read_set;
    int child_read, child_write;
    int maxfd;
    int opened_socket;
    int stdin_opened;
    int res;

    child_read = child_pipe[0];
    child_write = child_pipe[1];

    stdin_opened = 1;
    opened_socket = -1; /* no socket opened */
    do
    {
        FD_ZERO(&read_set);

        if (stdin_opened)
            FD_SET(0, &read_set);
        FD_SET(child_read, &read_set);
        maxfd = child_read;
        if (opened_socket >= 0)
        {
            FD_SET(opened_socket, &read_set);
            maxfd = max(maxfd, opened_socket);
        }
        else
        {
            /* We only accept if we don't have any
             * connetion opened. */
            FD_SET(lsocket, &read_set);
            maxfd = max(maxfd, lsocket);
        }

        /* Will block */
        res = select(maxfd + 1, &read_set, 0, 0, 0);
        if (res == -1)
        {
            if (errno == EINTR)
                continue;
            else
                error("Error in select()");
        }

        if (FD_ISSET(child_read, &read_set))
        {
            res = forward_app_data(child_read, 1);
            if (res == 0)
                break;
        }
        if (FD_ISSET(0, &read_set))
        {
            res = forward_app_data(0, child_write);
            if (res == 0)
            {
                close(child_write);
                stdin_opened = 0;
            }
        }
        if (opened_socket >= 0 && FD_ISSET(opened_socket, &read_set))
        {
            res = forward_app_data(opened_socket, child_write);
            if (res == 0)
            {
                close(opened_socket);
                opened_socket = -1; /* no socket open */
            }
        }
        if (opened_socket == -1 && FD_ISSET(lsocket, &read_set))
        {
            opened_socket = accept_connection(lsocket);
        }
    } while(1);
}

static int server(int argn, char * const argv[])
{
    int p[2];
    int lsocket;
    int child;

    child = fork_app(p, &argv[1]);

    install_signal_forwarders(child);

    lsocket = serve_socket();

    loop(p, lsocket);

    remove_socket(lsocket);

    return 0;
}

static int client()
{
    int cs;
    int res;
    fd_set read_set;
    int maxfd;

    cs = connect_socket();

    do
    {
        FD_ZERO(&read_set);

        FD_SET(cs, &read_set); /* For reading other side's close() */
        maxfd = cs;
        FD_SET(0, &read_set); /* stdin */
        maxfd = max(maxfd, cs);

        res = select(maxfd + 1, &read_set, 0, 0, 0);
        if (res == -1)
        {
            if (errno == EINTR)
                continue;
            else
                error("Error in select()");
        }

        if (FD_ISSET(cs, &read_set))
        {
            /* assuming close() on the other side, even
             * without read(). */
            break;
        }
        if (FD_ISSET(0, &read_set))
        {
            res = forward_app_data(0, cs);
            if (res == 0) /* EOF */
                break;
        }
    } while (1);

    close(cs);
    return 0;
}

static int showhelp(const char *pname)
{
    printf("sdtdin-mix v%s - Copyright (C) 2007  Lluis Batlle i Rossell\n",
            version);
    printf("usage: %s [appcommand] [param1] [param2] ...\n", pname);
    printf(" If you give _appcommand_, it starts the application and\n");
    printf(" a stdin server on $SM_SOCKET or /tmp/socket-sm.UID.\n");
    printf(" If not given, it starts a stdin client for the same socket.\n");
    return 0;
}

int main(int argn, char * const * argv)
{
    int res;
    if (argn > 1 && strcmp(argv[1], "-h") == 0)
        res = showhelp(argv[0]);
    else if (argn > 1)
        res = server(argn, argv);
    else
        res = client();
    return res;
}