Logo Search packages:      
Sourcecode: ldm version File versions

ldm.c

/*
 * ldm.c
 * LTSP display manager.
 * Manages spawning a session to a server.
 *
 * (c) Scott Balneaves, sbalneav@ltsp.org,      2007, 2008
 *     Oliver Grawert, ogra@ubuntu.com,         2005, 2006, 2007
 *     Vagrant Cascadian, vagrant@freegeek.org,
 *     Gideon Romm, gadi@ltsp.org,              2007, 2008
 *     Warren Togami
 *     Ryan Niebur, RyanRyan52@gmail.com,       2008
 *
 *
 * This software is licensed under the GPL v2 or later.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <glib.h>
#include <glib-object.h>

#include "ldm.h"

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

#define LOGFILE "/var/log/ldm.log"

GHashTable *ldminfo_hash = NULL;
GList *host_list = NULL;
volatile sig_atomic_t unexpected_child = 0;
volatile sig_atomic_t child_exited = 0;
FILE *logfile = NULL;

struct ldm_info ldm;

/*
 * loginfo: logs info messages to either syslog or the log file.
 */

void
loginfo(const char *format, ...)
{
    va_list ap;

    va_start(ap, format);

    if (logfile) {
        vfprintf(logfile, format, ap);
        fprintf(logfile, "\n");
    } else {
        vsyslog(LOG_INFO, format, ap);
    }

    va_end(ap);
}

/*
 * logerr: logs error messages to either syslog or the log file.
 */

void
logerr(const char *format, ...)
{
    va_list ap;

    va_start(ap, format);

    if (logfile) {
        vfprintf(logfile, format, ap);
        fprintf(logfile, "\n");
    } else {
        vsyslog(LOG_ERR, format, ap);
    }

    va_end(ap);
}

/*
 * die()
 *
 * Close display manager down with an error message.
 */

void
die(char *msg)
{
    logerr("%s", msg);

    /*
     * Shut things down gracefully if we can
     */

    if (ldm.greeterpid) {
        close_greeter();
    }

    ssh_endsession();

    /* Stop logging */
    if (logfile) {
        fclose(logfile);
    } else {
        closelog();
    }

    exit(1);
}

/*
 * ldm_spawn:
 *
 * Execute commands.  Prints nice error message if failure.
 */

GPid
ldm_spawn (gchar *command, gint *rfd, gint *wfd,  GSpawnChildSetupFunc setup)
{
    GPid pid;
    GError *error = NULL;
    GSpawnFlags flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD;
    gint argc;
    gchar **argv = NULL;

    g_shell_parse_argv (command, &argc, &argv, NULL);

    if (!wfd) {
        flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
    }

    g_spawn_async_with_pipes (
                  NULL,             /* Working directory: inherit */
                  argv,             /* Arguments, null term */
                  NULL,             /* Environment, inherit from parent */
                  flags,            /* Flags, set above */
                  setup,            /* Child setup function: passed to us */
                  NULL,             /* No user data */
                  &pid,             /* child pid */
                  wfd,              /* Pointer to in file descriptor */
                  rfd,              /* Pointer to out file descriptor */
                  NULL,             /* No stderr */
                  &error);          /* GError handler */

    g_strfreev(argv);

    if (error) {
        logerr(_("ldm_spawn failed to execute: %s"), error->message);
        die(_("Exiting ldm"));
    } else {
        loginfo(_("ldm_spawn: pid = %d"), pid);
    }

    return pid;
}

/*
 * handle_sigchld
 *
 * Handle sigchld's for ldm processes.  Empty function,
 * since we wait for things to happen in order via
 * ldm_wait
 */

void
handle_sigchld(int signo)
{
    /* do nothing */
    child_exited = TRUE;
}

/*
 * ldm_wait
 *
 * wait for child process
 */

void
ldm_wait(GPid pid)
{
    siginfo_t info;
    do {
        int res;
        res = waitid (P_ALL, 0, &info, WEXITED | WSTOPPED);
        if (res == -1) {
            logerr(_("sigwaitinfo returned an error"));
        } else {
            if (info.si_pid == pid) {
                /*
                 * The process we were waiting for exited,
                 * so break out of the loop.
                 */
                break;
            } else {
                logerr(_("Unexpected terminated process: pid = %d"),
                            info.si_pid);
                unexpected_child = TRUE;
            }
        }
    } while (TRUE);

    if (info.si_code == CLD_EXITED) {
        loginfo(_("Process %d exited with status %d"), info.si_pid,
                WEXITSTATUS(info.si_status));
    } else if (info.si_code == CLD_KILLED) {
        logerr(_("Process %d killed by signal %d"), info.si_pid,
                info.si_status);
    }
}

/*
 * rc_files
 *
 * Run startup commands.
 */

void
rc_files(char *action)
{
    GPid rcpid;
    gchar *command;

    command = g_strjoin(" ", "/bin/sh",  RC_DIR "/ldm-script", action, NULL);

    loginfo(_("rc_files: %s"), command);
    rcpid = ldm_spawn(command, NULL, NULL, NULL);

    ldm_wait(rcpid);
    g_free(command);
}

void
get_Xsession(void)
{
    ldminfo *curr_host = NULL;
    ldm.xsession = NULL;
    ldm.xsession = g_strdup(getenv("LDM_XSESSION"));
    if (!ldm.xsession || strlen(ldm.xsession) == 0) {
        curr_host = g_hash_table_lookup(ldminfo_hash, ldm.server);
        ldm.xsession = curr_host->xsession;
    }
    if (!ldm.xsession || strlen(ldm.xsession) == 0) {
       // stupid fallback
       ldm.xsession = g_strdup(getenv("LDM_DEFAULT_XSESSION"));
    }
    if (!ldm.xsession || strlen(ldm.xsession) == 0) {
        die (g_strconcat(_("ERROR: no Xsession"), "\n", NULL ));
    }
}

/*
 * x_session
 *
 * Start a login session with the server
 */

void
x_session(void)
{
    gchar *command;
    gchar *display = NULL;
    gchar *sound = NULL;
    gchar *lang = NULL;
    gchar *localdev = NULL;
    gchar *client_env = NULL;
    gchar *esdcmd = "esd -nobeeps -public -tcp";
    GPid xsessionpid;
    GPid esdpid = 0;

    /*
     * Set up the LTSP_CLIENT environment variable.
     * We may not want to force set this:
     * FIXME:  Revisit this at some point.
     */

    client_env = g_strconcat("LTSP_CLIENT=", ldm.ipaddr, NULL);

    /*
     * Set the DISPLAY env, if not running over encrypted ssh
     */

    if (ldm.directx) {
        display = g_strconcat("DISPLAY=", ldm.ipaddr, ldm.display, NULL);
    }

    /*
     * Set our language, if a different one is picked.
     */

    if (ldm.lang) {
        lang = g_strconcat("LC_ALL=", ldm.lang, " LANGUAGE=", ldm.lang,
                           " LANG=", ldm.lang, NULL);
    }

    /*
     * Handle sound
     */

    if (ldm.sound) {
        if (!ldm.sound_daemon || !strncmp(ldm.sound_daemon, "pulse", 5)) {
            sound = g_strconcat("PULSE_SERVER=tcp:", ldm.ipaddr,
                    ":4713 ESPEAKER=", ldm.ipaddr, ":16001", NULL);
        } else if (!strncmp(ldm.sound_daemon, "esd", 3)) {
            sound = g_strconcat("ESPEAKER=", ldm.ipaddr, ":16001", NULL);
            esdpid = ldm_spawn(esdcmd, NULL, NULL, NULL); /* launch ESD */
        } else if (!strncmp(ldm.sound_daemon, "nasd", 4)) {
            sound = g_strconcat("AUDIOSERVER=", ldm.ipaddr, ":0", NULL);
        }
    }

    /*
     * Handle localdev
     */

    if (ldm.localdev) {
        localdev = "; /usr/sbin/ltspfsmounter all cleanup";
    }

    get_Xsession();

    command = g_strjoin(" ", "ssh", "-Y", "-t", "-S", ldm.control_socket,
            "-l", ldm.username, ldm.server, client_env,
            lang ? lang : "", display ? display : "", sound ? sound : "",
            ldm.xsession, ldm.session, "< /dev/null > /dev/null", localdev ? localdev : "",
            NULL);

    loginfo(_("x_session: %s"), command);
    xsessionpid = ldm_spawn(command, NULL, NULL, NULL);
    ldm_wait(xsessionpid);
    if (esdpid) {
        kill(esdpid, SIGTERM);
        ldm_wait(esdpid);
        esdpid = 0;
    }
    g_free(lang);
    g_free(display);
    g_free(sound);
    g_free(client_env);
    g_free(command);

}

/*
 * Load guest info
 */

void
load_guestinfo(void)
{
       char **hosts_char = NULL;
       gchar *autoservers = NULL;
       gboolean good;
       int i;

       /* Get all info for autologin, without greeter */
       loginfo(_("Logging in as guest"));
       ldm.username = g_strdup(getenv("LDM_USERNAME"));
       ldm.password = g_strdup(getenv("LDM_PASSWORD"));
       if (!ldm.username) {
           gchar hostname[HOST_NAME_MAX + 1];      /* +1 for \0 terminator */
           gethostname(hostname, sizeof hostname);
           ldm.username = g_strdup(hostname);
       }
       if (!ldm.password) {
           ldm.password = g_strdup(ldm.username);
       }

       autoservers = g_strdup(getenv("LDM_GUEST_SERVER"));
       if (!autoservers) {
           autoservers = g_strdup(getenv("LDM_AUTOLOGIN_SERVER"));
       }
       if (!autoservers) {
           autoservers = g_strdup(getenv("LDM_SERVER"));
       }

       hosts_char = g_strsplit(autoservers, " ", -1);

       good = FALSE;
       if (ldm.server) {
           i = 0;
           while(1) {
               if(hosts_char[i] == NULL) {
                   break;
               }
               if(!g_strcmp0(hosts_char[i], ldm.server)) {
                   good = TRUE;
                   break;
               }
               i++;
           }
       }

       if (good == FALSE) {
           ldm.server = g_strdup(hosts_char[0]);
       }
       g_strfreev(hosts_char);
       g_free(autoservers);
       return;
}


/*
 * mainline
 */

int
main(int argc, char *argv[])
{
    /* decls */
    gchar *display_env, *server_env, *socket_env, *user_env;
    gchar *err_msg = NULL;
    struct sigaction action;

    /*
     * Open log.  Will log locally or back to the server based upon
     * the boolean LDM_SYSLOG.
     */

    if (ldm_getenv_bool("LDM_SYSLOG")) {
        openlog("ldm", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
    } else {
        logfile = fopen(LOGFILE, "a");
        if (!logfile) {
            fprintf(stderr, "Couldn't open logfile " LOGFILE "\n");
            exit(1);
        }
        setbuf(logfile, NULL);  /* unbuffered writes to the log file */
    }

#ifdef ENABLE_NLS
    setlocale (LC_ALL, "");
    bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
    textdomain (GETTEXT_PACKAGE);
#endif

    g_type_init();

    /* set an empty handler for SIGCHLD so they're not ignored */
    action.sa_handler = handle_sigchld;
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO;
    sigaction (SIGCHLD, &action, NULL);

    /*
     * Zero out our info struct.
     */

    bzero(&ldm, sizeof ldm);

    ldm.pid = getpid();  /* Get our pid, to use in the command_socket */
    ldm.display = g_strdup(getenv("DISPLAY"));

    /*
     * Get our IP address.
     */

    get_ipaddr();
    loginfo(_("LDM2 running on ip address %s"), ldm.ipaddr);

    /*
     *  Put ip address in environment so that it is available to to the greeter
     */

    setenv("LDMINFO_IPADDR", ldm.ipaddr, 1);

    /*
     * Get some of the environment variables we'll need.
     */

    ldminfo_init(&ldminfo_hash, &host_list, getenv("LDM_SERVER"));
    ldm.allowguest = ldm_getenv_bool("LDM_GUESTLOGIN");
    ldm.sound = ldm_getenv_bool("SOUND");
    ldm.sound_daemon = g_strdup(getenv("SOUND_DAEMON"));
    ldm.localdev = ldm_getenv_bool("LOCALDEV");
    ldm.override_port = g_strdup(getenv("SSH_OVERRIDE_PORT"));
    ldm.directx = ldm_getenv_bool("LDM_DIRECTX");
    ldm.autologin = ldm_getenv_bool("LDM_AUTOLOGIN");
    ldm.lang = g_strdup(getenv("LDM_LANGUAGE"));
    ldm.session = g_strdup(getenv("LDM_SESSION"));
    ldm.sshoptions = g_strdup(getenv("LDM_SSHOPTIONS"));
    ldm.greeter_prog = g_strdup(getenv("LDM_GREETER"));
    if (!ldm.greeter_prog) {
        ldm.greeter_prog = g_strdup(LDM_EXEC_DIR "/ldmgtkgreet");
    }
    ldm.authfile = g_strdup(getenv("XAUTHORITY"));


    /*
     * Begin running display manager
     */

    rc_files("init");                      /* Execute any rc files */

    if (!ldm.autologin) {
        gint rfd, wfd;
        loginfo(_("Spawning greeter: %s"), ldm.greeter_prog);
        ldm.greeterpid = ldm_spawn(ldm.greeter_prog, &rfd, &wfd, NULL);
        /* create GIOChannels for the greeter */
        ldm.greeterr = g_io_channel_unix_new(rfd);
        ldm.greeterw = g_io_channel_unix_new(wfd);

        /* ask greeter to fill ldm.username and ldm.server */
        if (get_userid()) {
            die(_("ERROR: get_userid from greeter failed"));
        }

        /* If user clicks on guest button above, this has changed */
        if (!ldm.autologin) {
            if (get_passwd()) {
                die(_("ERROR: get_passwd from greeter failed"));
            }
        }

        if (get_host()) {
            die(_("ERROR: get_host from greeter failed"));
        }

        if (get_language()) {
            die(_("ERROR: get_language from greeter failed"));
        }

        if (get_session()) {
            die(_("ERROR: get_session from greeter failed"));
        }
    }

    if (ldm.autologin) {
        load_guestinfo();
    }

    /* Verify that we have all info needed to connect */
    if (!ldm.username) {
        err_msg = g_strconcat(_("ERROR: no username"), "\n", NULL );
    }
    if (!ldm.password) {
        err_msg = g_strconcat(_("ERROR: no password"), "\n", NULL );
    }
    if (!ldm.server) {
        err_msg = g_strconcat(_("ERROR: no server"), "\n", NULL );
    }
    if (!ldm.session) {
        ldm.session = g_strdup("default");
    }

    if (err_msg) {
        loginfo("%s", err_msg);
        die(_("Fatal error, missing mandatory information"));
    }

    /*
     * If we run multiple ldm sessions on multiply vty's we need separate
     * control sockets.
     */

    ldm.control_socket = g_strdup_printf("/var/run/ldm_socket_%d_%s",
            ldm.pid, ldm.server);
    socket_env = g_strconcat("LDM_SOCKET=", ldm.control_socket, NULL);
    server_env = g_strconcat("LDM_SERVER=", ldm.server, NULL);
    user_env = g_strconcat("LDM_USERNAME=", ldm.username, NULL);
    putenv(socket_env);
    putenv(server_env);
    putenv(user_env);

    ssh_session();                          /* Log in via ssh */

    if (ldm.greeterpid) {
        close_greeter();
    }

    loginfo(_("Established ssh session."));

    loginfo(_("Executing rc files."));
    rc_files("start");                      /* Execute any rc files */
    loginfo(_("Beginning X session."));
    x_session();                            /* Start X session up */
    loginfo(_("X session ended."));

    /* x_session's exited.  So, clean up. */

    loginfo(_("Executing rc files."));
    rc_files("stop");                       /* Execute any rc files */

    loginfo(_("Ending ssh session."));
    ssh_endsession();                       /* Log out of server */

    /* Stop logging */
    if (logfile) {
        fclose(logfile);
    } else {
        closelog();
    }

    g_free(ldm.server);
    g_free(ldm.display);
    g_free(ldm.override_port);
    g_free(ldm.authfile);
    g_free(ldm.username);
    g_free(ldm.lang);
    g_free(ldm.session);
    g_free(ldm.xsession);
    g_free(ldm.sound_daemon);
    g_free(ldm.greeter_prog);
    g_free(ldm.control_socket);
    g_free(ldm.ipaddr);
    g_free(display_env);
    g_free(socket_env);
    g_free(server_env);
    g_free(user_env);
    g_list_foreach(host_list, (GFunc)g_free, NULL);
    g_list_free(host_list);
    g_hash_table_destroy(ldminfo_hash);
    exit(0);
}

Generated by  Doxygen 1.6.0   Back to index