]> rtime.felk.cvut.cz Git - sojka/lightdm.git/commitdiff
Add VNC server support
authorRobert Ancell <robert.ancell@canonical.com>
Sun, 11 Sep 2011 05:45:10 +0000 (15:45 +1000)
committerRobert Ancell <robert.ancell@canonical.com>
Sun, 11 Sep 2011 05:45:10 +0000 (15:45 +1000)
12 files changed:
NEWS
data/lightdm.conf
src/Makefile.am
src/lightdm.c
src/process.c
src/process.h
src/seat-xvnc.c [new file with mode: 0644]
src/seat-xvnc.h [new file with mode: 0644]
src/session.c
src/vnc-server.c [new file with mode: 0644]
src/vnc-server.h [new file with mode: 0644]
src/xserver-local.c

diff --git a/NEWS b/NEWS
index ca1b99030946678f5ee387c43d6853fdfc23f8b0..367a9b114559886523eefe6ef5a0f631416d7ced 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,6 @@
 Overview of changes in lightdm 0.9.6
 
-    *
+    * Add VNC server support
 
 Overview of changes in lightdm 0.9.5
 
index ebbea28922c9aa9eeb0e1c0bdbb370456341479a..cfabfee174e11947a70d2730b017373cf3101e1f 100644 (file)
 #enabled=false
 #port=177
 #key=
+
+#
+# VNC Server configuration
+#
+# enabled = True if VNC connections should be allowed
+# port = TCP/IP port to listen for connections on
+#
+[XDMCPServer]
+#enabled=false
+#port=5900
index 4aa9781246fb57e332c256e16fc0aaf552300258..94388dd36a5b8c5bf7ff380eeb2a9e1a2c0c74b9 100644 (file)
@@ -40,10 +40,14 @@ lightdm_SOURCES = \
        seat-xlocal.h \
        seat-xremote.c \
        seat-xremote.h \
+       seat-xvnc.c \
+       seat-xvnc.h \
        session.c \
        session.h \
        user.c \
        user.h \
+       vnc-server.c \
+       vnc-server.h \
        vt.c \
        vt.h \
        xauthority.c \
@@ -61,6 +65,8 @@ lightdm_SOURCES = \
        xserver-local.h \
        xserver-remote.c \
        xserver-remote.h \
+       xserver-xvnc.c \
+       xserver-xvnc.h \
        xserver.c \
        xserver.h \
        xsession.c \
index b1d855a17f622902d4632b2869dea9ba2c9f9069..a08dca2ea8456da0109e217584b699d5b487282d 100644 (file)
@@ -23,7 +23,9 @@
 #include "configuration.h"
 #include "display-manager.h"
 #include "xdmcp-server.h"
+#include "vnc-server.h"
 #include "seat-xdmcp-session.h"
+#include "seat-xvnc.h"
 #include "xserver.h"
 #include "user.h"
 #include "pam-session.h"
@@ -37,6 +39,7 @@ static gboolean debug = FALSE;
 
 static DisplayManager *display_manager = NULL;
 static XDMCPServer *xdmcp_server = NULL;
+static VNCServer *vnc_server = NULL;
 static GDBusConnection *bus = NULL;
 static guint bus_id;
 static GDBusNodeInfo *seat_info;
@@ -750,6 +753,17 @@ xdmcp_session_cb (XDMCPServer *server, XDMCPSession *session)
     return result;
 }
 
+static void
+vnc_connection_cb (VNCServer *server, GSocket *connection)
+{
+    SeatXVNC *seat;
+
+    seat = seat_xvnc_new (connection);
+    set_seat_properties (SEAT (seat), NULL);
+    display_manager_add_seat (display_manager, SEAT (seat));
+    g_object_unref (seat);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -1151,6 +1165,23 @@ main (int argc, char **argv)
         xdmcp_server_start (xdmcp_server);
     }
 
+    /* Start the VNC server */
+    if (config_get_boolean (config_get_instance (), "VNCServer", "enabled"))
+    {
+        vnc_server = vnc_server_new ();
+        if (config_has_key (config_get_instance (), "VNCServer", "port"))
+        {
+            gint port;
+            port = config_get_integer (config_get_instance (), "VNCServer", "port");
+            if (port > 0)
+                vnc_server_set_port (vnc_server, port);
+        }
+        g_signal_connect (vnc_server, "new-connection", G_CALLBACK (vnc_connection_cb), NULL);
+
+        g_debug ("Starting VNC server on TCP/IP port %d", vnc_server_get_port (vnc_server));
+        vnc_server_start (vnc_server);
+    }
+
     g_main_loop_run (loop);
 
     return EXIT_SUCCESS;
index 845bafc781bb512d5e3b002818b0258f2208c7b0..e4e931476db7e6a60d1cdee7323d85667859e740 100644 (file)
@@ -22,6 +22,7 @@
 #include "process.h"
 
 enum {
+    RUN,
     STARTED,
     GOT_DATA,
     GOT_SIGNAL,  
@@ -37,17 +38,23 @@ struct ProcessPrivate
     /* Environment variables */
     GHashTable *env;
 
+    /* Command to run */
+    gchar *command;
+
+    /* Working directory */
+    gchar *working_directory;
+
     /* User to run as */
     User *user;
 
     /* Path of file to log to */
-    gchar *log_file;
-  
-    /* Timeout waiting for process to quit */
-    guint quit_timeout;
-
+    gchar *log_file; 
     /* Process ID */
     GPid pid;
+
+    /* Timeout waiting for process to quit */
+    guint quit_timeout;
 };
 
 G_DEFINE_TYPE (Process, process, G_TYPE_OBJECT);
@@ -74,6 +81,22 @@ process_new (void)
     return g_object_new (PROCESS_TYPE, NULL);
 }
 
+void
+process_set_command (Process *process, const gchar *command)
+{
+    g_return_if_fail (process != NULL);
+
+    g_free (process->priv->command);
+    process->priv->command = g_strdup (command);
+}
+
+const gchar *
+process_get_command (Process *process)
+{
+    g_return_val_if_fail (process != NULL, NULL);
+    return process->priv->command;
+}
+
 void
 process_set_log_file (Process *process, const gchar *log_file)
 {
@@ -89,7 +112,40 @@ process_get_log_file (Process *process)
     g_return_val_if_fail (process != NULL, NULL);
     return process->priv->log_file;
 }
-  
+
+void
+process_set_working_directory (Process *process, const gchar *working_directory)
+{
+    g_return_if_fail (process != NULL);
+
+    g_free (process->priv->working_directory);
+    process->priv->working_directory = g_strdup (working_directory);
+}
+
+const gchar *
+process_get_working_directory (Process *process)
+{
+    g_return_val_if_fail (process != NULL, NULL);
+    return process->priv->working_directory;
+}
+
+void
+process_set_user (Process *process, User *user)
+{
+    g_return_if_fail (process != NULL);
+
+    if (process->priv->user)
+        g_object_unref (process->priv->user);
+    process->priv->user = g_object_ref (user);
+}
+
+User *
+process_get_user (Process *process)
+{
+    g_return_val_if_fail (process != NULL, NULL);
+    return process->priv->user;
+}
+
 void
 process_set_env (Process *process, const gchar *name, const gchar *value)
 {
@@ -130,30 +186,39 @@ process_watch_cb (GPid pid, gint status, gpointer data)
 }
 
 static void
-run_process (Process *process, char *const argv[])
+process_run (Process *process)
+{
+    gint argc;
+    gchar **argv;
+    GError *error = NULL;
+
+    if (!g_shell_parse_argv (process->priv->command, &argc, &argv, &error))
+    {
+        g_warning ("Error parsing command %s: %s", process->priv->command, error->message);
+        _exit (EXIT_FAILURE);
+    }
+
+    execv (argv[0], argv);
+
+    g_warning ("Error executing child process %s: %s", argv[0], g_strerror (errno));
+    _exit (EXIT_FAILURE);
+}
+
+static void
+run (Process *process)
 {
     GHashTableIter iter;
     gpointer key, value;
-    int fd;
 
     /* FIXME: Close existing file descriptors */
 
-    /* Make input non-blocking */
-    fd = g_open ("/dev/null", O_RDONLY);
-    dup2 (fd, STDIN_FILENO);
-    close (fd);
-
     /* Set environment */
     clearenv ();
     g_hash_table_iter_init (&iter, process->priv->env);
     while (g_hash_table_iter_next (&iter, &key, &value))
         g_setenv ((gchar *)key, (gchar *)value, TRUE);
 
-    /* Set SIGUSR1 to ignore so the child process can indicate it when it is ready */
-    // FIXME: Should be in the xserver only
-    signal (SIGUSR1, SIG_IGN);
-
-    /* Make this process its own session so */
+    /* Make this process its own session */
     if (setsid () < 0)
         g_warning ("Failed to make process a new session: %s", strerror (errno));
 
@@ -203,32 +268,21 @@ run_process (Process *process, char *const argv[])
          }
     }
 
-    execv (argv[0], argv);
-
-    g_warning ("Error executing child process %s: %s", argv[0], g_strerror (errno));
-    _exit (EXIT_FAILURE);
+    g_signal_emit (process, signals[RUN], 0); 
 }
 
 gboolean
-process_start (Process *process,
-               User *user,
-               const gchar *working_dir,
-               const gchar *command,
-               GError **error)
+process_start (Process *process)
 {
-    gboolean result;
-    gint argc;
-    gchar **argv;
     GString *string;
     gpointer key, value;
     GHashTableIter iter;
     pid_t pid;
 
     g_return_val_if_fail (process != NULL, FALSE);
+    g_return_val_if_fail (process->priv->command != NULL, FALSE);  
     g_return_val_if_fail (process->priv->pid == 0, FALSE);
 
-    process->priv->user = g_object_ref (user);
-
     /* Create the log file owned by the target user */
     if (process->priv->log_file)
     {
@@ -238,10 +292,6 @@ process_start (Process *process,
             g_warning ("Failed to set process log file ownership: %s", strerror (errno));
     }
 
-    result = g_shell_parse_argv (command, &argc, &argv, error);
-    if (!result)
-        return FALSE;
-
     pid = fork ();
     if (pid < 0)
     {
@@ -250,14 +300,13 @@ process_start (Process *process,
     }
 
     if (pid == 0)
-        run_process (process, argv);
-    g_strfreev (argv);
+        run (process);
 
     string = g_string_new ("");
     g_hash_table_iter_init (&iter, process->priv->env);
     while (g_hash_table_iter_next (&iter, &key, &value))
         g_string_append_printf (string, "%s=%s ", (gchar *)key, (gchar *)value);
-    g_string_append (string, command);
+    g_string_append (string, process->priv->command);
     g_debug ("Launching process %d: %s", pid, string->str);
     g_string_free (string, TRUE);
 
@@ -334,12 +383,15 @@ process_finalize (GObject *object)
 
     self = PROCESS (object);
 
-    if (self->priv->user)
-        g_object_unref (self->priv->user);
-
     if (self->priv->pid > 0)
         g_hash_table_remove (processes, GINT_TO_POINTER (self->priv->pid));
 
+    g_free (self->priv->command);
+    g_free (self->priv->working_directory);
+    g_free (self->priv->log_file);
+    if (self->priv->user)
+        g_object_unref (self->priv->user);
+
     if (self->priv->pid)
         kill (self->priv->pid, SIGTERM);
 
@@ -388,11 +440,20 @@ process_class_init (ProcessClass *klass)
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     struct sigaction action;
 
+    klass->run = process_run;
     klass->stopped = process_stopped;
     object_class->finalize = process_finalize;  
 
     g_type_class_add_private (klass, sizeof (ProcessPrivate));
 
+    signals[RUN] =
+        g_signal_new ("run",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (ProcessClass, run),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0); 
     signals[STARTED] =
         g_signal_new ("started",
                       G_TYPE_FROM_CLASS (klass),
index 5fe5380d8c7774e60ce10d2db5443a0504c40ec4..ed64690262ca707e50a32c1d285189d992cc9ffa 100644 (file)
@@ -34,6 +34,7 @@ typedef struct
 typedef struct
 {
     GObjectClass parent_class;
+    void (*run)(Process *process);
     void (*started)(Process *process);
     void (*got_data)(Process *process);
     void (*got_signal)(Process *process, int signum);
@@ -48,19 +49,27 @@ Process *process_get_current (void);
 
 Process *process_new (void);
 
+void process_set_command (Process *process, const gchar *command);
+
+const gchar *process_get_command (Process *process);
+
 void process_set_log_file (Process *process, const gchar *log_file);
 
 const gchar *process_get_log_file (Process *process);
 
+void process_set_working_directory (Process *process, const gchar *working_directory);
+
+const gchar *process_get_working_directory (Process *process);
+
+void process_set_user (Process *process, User *user);
+
+User *process_get_user (Process *process);
+
 void process_set_env (Process *process, const gchar *name, const gchar *value);
 
 const gchar *process_get_env (Process *process, const gchar *name);
 
-gboolean process_start (Process *process,
-                        User *user,
-                        const gchar *working_dir,
-                        const gchar *command,
-                        GError **error);
+gboolean process_start (Process *process);
 
 gboolean process_get_is_running (Process *process);
 
@@ -68,8 +77,10 @@ GPid process_get_pid (Process *process);
 
 void process_signal (Process *process, int signum);
 
+// FIXME: Move out of here
 GIOChannel *process_get_to_child_channel (Process *process);
 
+// FIXME: Move out of here
 GIOChannel *process_get_from_child_channel (Process *process);
 
 void process_stop (Process *process);
diff --git a/src/seat-xvnc.c b/src/seat-xvnc.c
new file mode 100644 (file)
index 0000000..82339e5
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#include "seat-xvnc.h"
+#include "xdisplay.h"
+#include "xserver-xvnc.h"
+
+G_DEFINE_TYPE (SeatXVNC, seat_xvnc, SEAT_TYPE);
+
+struct SeatXVNCPrivate
+{
+    /* Remote display */
+    XDisplay *display;
+
+    /* VNC connection */
+    GSocket *connection;
+};
+
+SeatXVNC *seat_xvnc_new (GSocket *connection)
+{
+    SeatXVNC *seat;
+
+    seat = g_object_new (SEAT_XVNC_TYPE, NULL);
+    seat->priv->connection = g_object_ref (connection);
+
+    return seat;
+}
+
+static Display *
+seat_xvnc_add_display (Seat *seat)
+{
+    XServerXVNC *xserver;
+
+    xserver = xserver_xvnc_new ();
+    xserver_xvnc_set_stdin (xserver, g_socket_get_fd (SEAT_XVNC (seat)->priv->connection));
+
+    SEAT_XVNC (seat)->priv->display = xdisplay_new (XSERVER (xserver));
+    g_object_unref (xserver);
+
+    return DISPLAY (SEAT_XVNC (seat)->priv->display);  
+}
+
+static void
+seat_xvnc_display_removed (Seat *seat, Display *display)
+{
+    seat_stop (seat);
+}
+
+static void
+seat_xvnc_init (SeatXVNC *seat)
+{
+    seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_XVNC_TYPE, SeatXVNCPrivate);
+}
+
+static void
+seat_xdmcp_session_finalize (GObject *object)
+{
+    SeatXVNC *self;
+
+    self = SEAT_XVNC (object);
+
+    if (self->priv->display)
+        g_object_unref (self->priv->display);
+    g_object_unref (self->priv->connection);
+
+    G_OBJECT_CLASS (seat_xvnc_parent_class)->finalize (object);
+}
+
+static void
+seat_xvnc_class_init (SeatXVNCClass *klass)
+{
+    SeatClass *seat_class = SEAT_CLASS (klass);
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    seat_class->add_display = seat_xvnc_add_display;
+    seat_class->display_removed = seat_xvnc_display_removed;
+    object_class->finalize = seat_xdmcp_session_finalize;
+
+    g_type_class_add_private (klass, sizeof (SeatXVNCPrivate));
+}
diff --git a/src/seat-xvnc.h b/src/seat-xvnc.h
new file mode 100644 (file)
index 0000000..64eec2e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef _SEAT_xvnc_H_
+#define _SEAT_xvnc_H_
+
+#include <glib-object.h>
+#include "seat.h"
+
+G_BEGIN_DECLS
+
+#define SEAT_XVNC_TYPE (seat_xvnc_get_type())
+#define SEAT_XVNC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEAT_XVNC_TYPE, SeatXVNC))
+
+typedef struct SeatXVNCPrivate SeatXVNCPrivate;
+
+typedef struct
+{
+    Seat               parent_instance;
+    SeatXVNCPrivate *priv;
+} SeatXVNC;
+
+typedef struct
+{
+    SeatClass parent_class;
+} SeatXVNCClass;
+
+GType seat_xvnc_get_type (void);
+
+SeatXVNC *seat_xvnc_new (GSocket *connection);
+
+G_END_DECLS
+
+#endif /* _SEAT_xvnc_H_ */
index f476f9b1dc8922c4d635f67e3337c415f29bfcbe..763d9c225e7776233fb97103b8ccfff1c11122ae 100644 (file)
@@ -160,7 +160,6 @@ session_real_start (Session *session)
     User *user;
     gboolean result;
     gchar *absolute_command;
-    GError *error = NULL;
 
     absolute_command = get_absolute_command (session->priv->command);
     if (!absolute_command)
@@ -168,17 +167,13 @@ session_real_start (Session *session)
         g_debug ("Can't launch session %s, not found in path", session->priv->command);
         return FALSE;
     }
+    process_set_command (PROCESS (session), absolute_command);
+    g_free (absolute_command);
 
     user = pam_session_get_user (session->priv->authentication);
-    result = process_start (PROCESS (session),
-                            user,
-                            user_get_home_directory (user),
-                            absolute_command,
-                            &error);
-    if (error)
-        g_warning ("Failed to spawn session: %s", error->message);
-    g_clear_error (&error);
-    g_free (absolute_command);
+    process_set_user (PROCESS (session), user);
+    process_set_working_directory (PROCESS (session), user_get_home_directory (user));
+    result = process_start (PROCESS (session));
 
     return result;
 }
@@ -243,6 +238,17 @@ session_stop (Session *session)
     SESSION_GET_CLASS (session)->stop (session);
 }
 
+static void
+session_run (Process *process)
+{
+    int fd;
+
+    /* Make input non-blocking */
+    fd = g_open ("/dev/null", O_RDONLY);
+    dup2 (fd, STDIN_FILENO);
+    close (fd);
+}
+
 static void
 session_stopped (Process *process)
 {
@@ -282,6 +288,7 @@ session_class_init (SessionClass *klass)
 
     klass->start = session_real_start;
     klass->stop = session_real_stop;
+    process_class->run = session_run;
     process_class->stopped = session_stopped;
     object_class->finalize = session_finalize;
 
diff --git a/src/vnc-server.c b/src/vnc-server.c
new file mode 100644 (file)
index 0000000..a6402a4
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#include <gio/gio.h>
+
+#include "vnc-server.h"
+
+enum {
+    NEW_CONNECTION,
+    LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct VNCServerPrivate
+{
+    /* Port to listen on */
+    guint port;
+
+    /* Listening sockets */
+    GSocket *socket, *socket6;
+};
+
+G_DEFINE_TYPE (VNCServer, vnc_server, G_TYPE_OBJECT);
+
+VNCServer *
+vnc_server_new (void)
+{
+    return g_object_new (VNC_SERVER_TYPE, NULL);
+}
+
+void
+vnc_server_set_port (VNCServer *server, guint port)
+{
+    g_return_if_fail (server != NULL);
+    server->priv->port = port;
+}
+
+guint
+vnc_server_get_port (VNCServer *server)
+{
+    g_return_val_if_fail (server != NULL, 0);
+    return server->priv->port;
+}
+
+static gboolean
+read_cb (GSocket *socket, GIOCondition condition, VNCServer *server)
+{
+    GError *error = NULL;
+    GSocket *client_socket;
+
+    client_socket = g_socket_accept (socket, NULL, &error);
+    if (error)
+        g_warning ("Failed to get connection from from VNC socket: %s", error->message);
+    g_clear_error (&error);
+
+    if (client_socket)
+    {
+        GInetSocketAddress *address;
+        gchar *hostname;
+
+        address = G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (client_socket, NULL));
+        hostname = g_inet_address_to_string (g_inet_socket_address_get_address (address));
+        g_debug ("Got VNC connection from %s:%d", hostname, g_inet_socket_address_get_port (address));
+        g_free (hostname);
+
+        g_signal_emit (server, signals[NEW_CONNECTION], 0, client_socket);
+    }
+
+    return TRUE;
+}
+
+static GSocket *
+open_tcp_socket (GSocketFamily family, guint port, GError **error)
+{
+    GSocket *socket;
+    GSocketAddress *address;
+  
+    socket = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, error);
+    if (!socket)
+        return NULL;
+
+    address = g_inet_socket_address_new (g_inet_address_new_any (family), port);
+    if (!g_socket_bind (socket, address, TRUE, error) ||
+        !g_socket_listen (socket, error))
+    {
+        g_object_unref (socket);
+        return NULL;
+    }
+
+    return socket;
+}
+
+gboolean
+vnc_server_start (VNCServer *server)
+{
+    GSource *source;
+    GError *error = NULL;
+
+    g_return_val_if_fail (server != NULL, FALSE);
+  
+    server->priv->socket = open_tcp_socket (G_SOCKET_FAMILY_IPV4, server->priv->port, &error);
+    if (error)
+        g_warning ("Failed to create IPv4 VNC socket: %s", error->message);
+    g_clear_error (&error);
+  
+    if (server->priv->socket)
+    {
+        source = g_socket_create_source (server->priv->socket, G_IO_IN, NULL);
+        g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
+        g_source_attach (source, NULL);
+    }
+    
+    server->priv->socket6 = open_tcp_socket (G_SOCKET_FAMILY_IPV6, server->priv->port, &error);
+    if (error)
+        g_warning ("Failed to create IPv6 VNC socket: %s", error->message);
+    g_clear_error (&error);
+
+    if (server->priv->socket6)
+    {
+        source = g_socket_create_source (server->priv->socket6, G_IO_IN, NULL);
+        g_source_set_callback (source, (GSourceFunc) read_cb, server, NULL);
+        g_source_attach (source, NULL);
+    }
+
+    if (!server->priv->socket && !server->priv->socket6)
+        return FALSE;
+
+    return TRUE;
+}
+
+static void
+vnc_server_init (VNCServer *server)
+{
+    server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, VNC_SERVER_TYPE, VNCServerPrivate);
+    server->priv->port = 5900;
+}
+
+static void
+vnc_server_finalize (GObject *object)
+{
+    VNCServer *self;
+
+    self = VNC_SERVER (object);
+  
+    if (self->priv->socket)
+        g_object_unref (self->priv->socket);
+    if (self->priv->socket6)
+        g_object_unref (self->priv->socket6);
+  
+    G_OBJECT_CLASS (vnc_server_parent_class)->finalize (object);  
+}
+
+static void
+vnc_server_class_init (VNCServerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->finalize = vnc_server_finalize;  
+
+    g_type_class_add_private (klass, sizeof (VNCServerPrivate));
+
+    signals[NEW_CONNECTION] =
+        g_signal_new ("new-connection",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (VNCServerClass, new_connection),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__OBJECT,
+                      G_TYPE_NONE, 1, G_TYPE_SOCKET);
+}
diff --git a/src/vnc-server.h b/src/vnc-server.h
new file mode 100644 (file)
index 0000000..6445038
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+#ifndef _VNC_SERVER_H_
+#define _VNC_SERVER_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define VNC_SERVER_TYPE (vnc_server_get_type())
+#define VNC_SERVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VNC_SERVER_TYPE, VNCServer));
+
+typedef struct VNCServerPrivate VNCServerPrivate;
+
+typedef struct
+{
+    GObject         parent_instance;
+    VNCServerPrivate *priv;
+} VNCServer;
+
+typedef struct
+{
+    GObjectClass parent_class;
+
+    gboolean (*new_connection)(VNCServer *server, GSocket *socket);
+} VNCServerClass;
+
+GType vnc_server_get_type (void);
+
+VNCServer *vnc_server_new (void);
+
+void vnc_server_set_port (VNCServer *server, guint port);
+
+guint vnc_server_get_port (VNCServer *server);
+
+gboolean vnc_server_start (VNCServer *server);
+
+G_END_DECLS
+
+#endif /* _VNC_SERVER_H_ */
index 06e9ca9a7dd0696a51452c64efe3f26ffef23619..2f494090fdf62b6be54ae1c11cf26abba07f6190 100644 (file)
@@ -25,9 +25,6 @@ struct XServerLocalPrivate
     /* X server process */
     Process *xserver_process;
 
-    /* Path of file to log to */
-    gchar *log_file;
-  
     /* Command to run the X server */
     gchar *command;
 
@@ -61,44 +58,30 @@ struct XServerLocalPrivate
 
 G_DEFINE_TYPE (XServerLocal, xserver_local, XSERVER_TYPE);
 
-static GList *display_numbers = NULL;
-
-static gboolean
-display_number_used (guint number)
-{
-    gchar *path;
-    gboolean result;
-  
-    if (g_list_find (display_numbers, GINT_TO_POINTER (number)))
-        return TRUE;
-  
-    path = g_strdup_printf ("/tmp/.X%d-lock", number);
-    result = g_file_test (path, G_FILE_TEST_EXISTS);
-    g_free (path);
-
-    return result;
-}
-
 static guint
 get_free_display_number (void)
 {
     guint number;
 
     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
-    while (display_number_used (number))
-        number++;
+    while (TRUE)
+    {
+        gchar *path;
+        gboolean result;
+  
+        path = g_strdup_printf ("/tmp/.X%d-lock", number);
+        result = g_file_test (path, G_FILE_TEST_EXISTS);
+        g_free (path);
 
-    display_numbers = g_list_append (display_numbers, GINT_TO_POINTER (number));
+        if (!result)
+            break;
 
+        number++;
+    }
+  
     return number;
 }
 
-static void
-release_display_number (guint number)
-{
-    display_numbers = g_list_remove (display_numbers, GINT_TO_POINTER (number));
-}
-
 XServerLocal *
 xserver_local_new (void)
 {
@@ -227,6 +210,20 @@ get_absolute_command (const gchar *command)
     return absolute_command;
 }
 
+static void
+run_cb (Process *process, XServerLocal *server)
+{
+    int fd;
+
+    /* Make input non-blocking */
+    fd = open ("/dev/null", O_RDONLY);
+    dup2 (fd, STDIN_FILENO);
+    close (fd);
+
+    /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
+    signal (SIGUSR1, SIG_IGN);
+}
+
 static void
 got_signal_cb (Process *process, int signum, XServerLocal *server)
 {
@@ -247,19 +244,6 @@ got_signal_cb (Process *process, int signum, XServerLocal *server)
     }
 }
 
-static void
-exit_cb (Process *process, int status, XServerLocal *server)
-{
-    if (status != 0)
-        g_debug ("X server exited with value %d", status);
-}
-
-static void
-terminated_cb (Process *process, int signum, XServerLocal *server)
-{
-    g_debug ("X server terminated with signal %d", signum);
-}
-
 static void
 stopped_cb (Process *process, XServerLocal *server)
 {
@@ -286,8 +270,6 @@ stopped_cb (Process *process, XServerLocal *server)
         server->priv->authority_file = NULL;
     }
 
-    release_display_number (xserver_get_display_number (XSERVER (server)));
-
     if (server->priv->vt >= 0)
     {
         vt_unref (server->priv->vt);
@@ -349,7 +331,6 @@ xserver_local_start (DisplayServer *display_server)
     gchar *filename, *dir, *path, *absolute_command;
     gchar hostname[1024], *number;
     GString *command;
-    GError *error = NULL;
 
     g_return_val_if_fail (server->priv->xserver_process == NULL, FALSE);
 
@@ -358,9 +339,8 @@ xserver_local_start (DisplayServer *display_server)
     g_return_val_if_fail (server->priv->command != NULL, FALSE);
 
     server->priv->xserver_process = process_new ();
+    g_signal_connect (server->priv->xserver_process, "run", G_CALLBACK (run_cb), server);  
     g_signal_connect (server->priv->xserver_process, "got-signal", G_CALLBACK (got_signal_cb), server);
-    g_signal_connect (server->priv->xserver_process, "exited", G_CALLBACK (exit_cb), server);
-    g_signal_connect (server->priv->xserver_process, "terminated", G_CALLBACK (terminated_cb), server);
     g_signal_connect (server->priv->xserver_process, "stopped", G_CALLBACK (stopped_cb), server);
 
     /* Setup logging */
@@ -421,6 +401,8 @@ xserver_local_start (DisplayServer *display_server)
 
     if (server->priv->replacing_plymouth)
         g_string_append (command, " -background none");
+    process_set_command (server->priv->xserver_process, command->str);
+    g_string_free (command, TRUE);
 
     g_debug ("Launching X Server");
 
@@ -447,15 +429,8 @@ xserver_local_start (DisplayServer *display_server)
         process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
     }
 
-    result = process_start (server->priv->xserver_process,
-                            user_get_current (),
-                            NULL,
-                            command->str,
-                            &error);
-    if (error)
-        g_warning ("Unable to create display: %s", error->message);
-    g_clear_error (&error);
-    g_string_free (command, TRUE);
+    process_set_user (server->priv->xserver_process, user_get_current ());
+    result = process_start (server->priv->xserver_process);
 
     if (result)
         g_debug ("Waiting for ready signal from X server :%d", xserver_get_display_number (XSERVER (server)));