Logo Search packages:      
Sourcecode: ldm version File versions  Download package

sshutils.c

/*
 * sshutils.c
 * LTSP display manager.
 * Manages spawning a session to a server.
 *
 * (c) Scott Balneaves, sbalneav@ltsp.org
 *
 * This software is licensed under the GPL v2 or later.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <pty.h>
#include <utmp.h>
#include <glib.h>

#include "ldm.h"

#include <config.h>
#include <libintl.h>
#include <locale.h>
#define _(text) gettext(text)

#define ERROR -1
#define TIMED_OUT -2

int
expect(int fd, char *p, int seconds, ...)
{
    fd_set set;
    struct timeval timeout;
    int i, st;
    ssize_t size = 0;
    size_t total = 0;
    va_list ap;
    char buffer[BUFSIZ];
    gchar *arg;
    GPtrArray *expects;
    int loopcount = seconds;
    int loopend = 0;

    bzero(p, MAXEXP);

    expects = g_ptr_array_new();

    va_start(ap, seconds);

    while ((arg = va_arg(ap, char *)) != NULL) {
        g_ptr_array_add(expects, (gpointer) arg);
    }

    va_end(ap);

    /*
     * Set our file descriptor to be watched.
     */

    FD_ZERO(&set);
    FD_SET(fd, &set);

    timeout.tv_sec = (long)seconds;             /* one second timeout */
    timeout.tv_usec = 0;

    /*
     * Main loop.
     */

    while(1) {
        st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);

        if (st < 0) {                 /* bad thing */
            break;
        }
        if (!st) {                  /* timeout */
            break;              /* We've not seen the data we want */
        }

        size = read(fd, buffer, sizeof buffer);
        if (size <= 0) {
            break;
        }

        if ((total + size) < MAXEXP) {
            strncpy(p + total, buffer, size);
            total += size;
        }

        for (i = 0; i < expects->len; i++) {
            if (strstr(p, g_ptr_array_index(expects, i))) {
                loopend = TRUE;
                break;
            }
        }

        if (loopend) {
            break;
        }

        if (timeout.tv_sec == 0) {
            break;
        }
    }

    fprintf(ldmlog, "expect saw: %s\n", p);

    if (size < 0 || st < 0) {
        return ERROR;               /* error occured */
    }
    if (st == 0) {
        return TIMED_OUT;           /* timed out */
    } else {
        return i;                   /* which expect did we see? */
    }
}

void
ssh_chat(gint fd)
{
    int seen;
    gchar lastseen[MAXEXP];
    int first_time = 1;

    /* We've already got the password here from the mainline,  so there's
     * no delay between asking for the userid, and the ssh session asking for a
     * password.  That's why we need the "first_time" variable.  If a
     * password expiry is in the works, then subsequent password prompts
     * will cause us to go back to the greeter. */

    while (TRUE) {
        /* ASSUMPTION: ssh will send out a string that ends in ": " for an expiry */
        seen = expect(fd, lastseen, 30, SENTINEL, ": ", NULL);

        /* We might have a : in the data, we're looking for :'s at the
           end of the line */
        if (seen == 0) {
            fprintf(ldmlog, _("Logged in successfully.\n"));
            g_free(ldm.password);
            ldm.password = NULL;
            return;
        } else if (seen == 1) {
            int i;
            g_strdelimit(lastseen, "\r\n\t", ' ');
            g_strchomp(lastseen);
            i = strlen(lastseen);
            /* If it's not the first time through, or the :'s not at the
             * end of a line (password expiry or error), set the message */
            if ((!first_time) || (lastseen[i - 1] != ':')) {
                set_message(lastseen);
            }
            /* If ':' *IS* the last character on the line, we'll assume a
             * password prompt is presented, and get a password */
            if (lastseen[i - 1] == ':') {
                if (!first_time) {    /* first time, we already prompted */
                    get_passwd();
                }
                write(fd, ldm.password, strlen(ldm.password));
                write(fd, "\n", 1);
                g_free(ldm.password);
                ldm.password = NULL;
            }
            first_time = 0;
        } else if (seen < 0) {
            set_message(_("No response from server, restarting..."));
            sleep(5);
            die(_("No response, restarting"));
        }
    }
}

/*
 * ssh_session()
 * Start an ssh login to the server.
 */

void
ssh_session()
{
    gint fd_master, fd_slave;
    gchar *userathost;
    gchar *port = NULL;
    GPid pid;

    if (ldm.override_port) {
        port = g_strconcat(" -p ", ldm.override_port, " ",  NULL);
    }

    userathost = g_strconcat(ldm.username, "@", ldm.server, NULL);

    openpty(&fd_master, &fd_slave, NULL, NULL, NULL);
    ldm.sshfd = fd_master;

    /*
     * Why is g_spawn_async misbehaving?  Back to the old ways.
     */

    pid = fork();

    if (pid > 0) {
        /* In the parent */
        ldm.sshpid = pid;
        ssh_chat(fd_master);
    } else {
        /* child */
        int i = 0;
        int j;
        char *argv[12];

        close(fd_master);
        argv[i++] = "ssh";
        argv[i++] = "-Y";
        argv[i++] = "-t";
        argv[i++] = "-M";
        argv[i++] = "-S";
        argv[i++] = ldm.control_socket;
        if (port) {
            argv[i++] = "-p";
            argv[i++] = ldm.override_port;
        }
        argv[i++] = userathost;
        argv[i++] = "echo " SENTINEL "; /bin/sh -";
        argv[i++] = NULL;

        fprintf(ldmlog, "ssh_session: ");
        for (j = 0; argv[j]; j++) {
            fprintf(ldmlog, "%s ", argv[j]);
        }
        fprintf(ldmlog, "\n");
        (void) setsid();
        if (login_tty(fd_slave) < 0 ) {
            fprintf(ldmlog, "login_tty failed\n");
        }
        execvp(*argv, argv);
    }

    g_free(port);
    g_free(userathost);
}

int
ssh_endsession()
{
    char lastseen[MAXEXP];
    int status;

    write(ldm.sshfd, "exit\r\n", 6);
    expect(ldm.sshfd, lastseen, 15.0, ldm.server, NULL);
    status = ldm_wait(ldm.sshpid);
    close (ldm.sshfd);
    return status;
}

Generated by  Doxygen 1.6.0   Back to index