Back to index

lightdm  1.3.2
seat.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 Robert Ancell.
00003  * Author: Robert Ancell <robert.ancell@canonical.com>
00004  * 
00005  * This program is free software: you can redistribute it and/or modify it under
00006  * the terms of the GNU General Public License as published by the Free Software
00007  * Foundation, either version 3 of the License, or (at your option) any later
00008  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
00009  * license.
00010  */
00011 
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <sys/wait.h>
00015 
00016 #include "seat.h"
00017 #include "guest-account.h"
00018 
00019 enum {
00020     DISPLAY_ADDED,
00021     DISPLAY_REMOVED,
00022     STOPPED,
00023     LAST_SIGNAL
00024 };
00025 static guint signals[LAST_SIGNAL] = { 0 };
00026 
00027 struct SeatPrivate
00028 {
00029     /* Configuration for this seat */
00030     GHashTable *properties;
00031 
00032     /* TRUE if able to switch users */
00033     gboolean can_switch;
00034 
00035     /* Name of guest account */
00036     gchar *guest_username;
00037 
00038     /* The displays for this seat */
00039     GList *displays;
00040 
00041     /* The active display */
00042     Display *active_display;
00043 
00044     /* TRUE if stopping this seat (waiting for displays to stop) */
00045     gboolean stopping;
00046 
00047     /* TRUE if stopped */
00048     gboolean stopped;
00049 };
00050 
00051 G_DEFINE_TYPE (Seat, seat, G_TYPE_OBJECT);
00052 
00053 typedef struct
00054 {
00055     const gchar *name;
00056     GType type;
00057 } SeatModule;
00058 static GHashTable *seat_modules = NULL;
00059 
00060 void
00061 seat_register_module (const gchar *name, GType type)
00062 {
00063     SeatModule *module;
00064 
00065     if (!seat_modules)
00066         seat_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
00067 
00068     g_debug ("Registered seat module %s", name);
00069 
00070     module = g_malloc0 (sizeof (SeatModule));
00071     module->name = g_strdup (name);
00072     module->type = type;
00073     g_hash_table_insert (seat_modules, g_strdup (name), module);
00074 }
00075 
00076 Seat *
00077 seat_new (const gchar *module_name)
00078 {
00079     Seat *seat;
00080     SeatModule *m = NULL;
00081   
00082     g_return_val_if_fail (module_name != NULL, NULL);
00083 
00084     if (seat_modules)
00085         m = g_hash_table_lookup (seat_modules, module_name);
00086     if (!m)
00087         return NULL;
00088 
00089     seat = g_object_new (m->type, NULL);
00090 
00091     return seat;
00092 }
00093 
00094 void
00095 seat_set_property (Seat *seat, const gchar *name, const gchar *value)
00096 {
00097     g_return_if_fail (seat != NULL);
00098     g_hash_table_insert (seat->priv->properties, g_strdup (name), g_strdup (value));
00099 }
00100 
00101 gboolean
00102 seat_has_property (Seat *seat, const gchar *name)
00103 {
00104     g_return_val_if_fail (seat != NULL, FALSE);
00105     return g_hash_table_lookup (seat->priv->properties, name) != NULL;
00106 }
00107 
00108 const gchar *
00109 seat_get_string_property (Seat *seat, const gchar *name)
00110 {
00111     g_return_val_if_fail (seat != NULL, NULL);
00112     return g_hash_table_lookup (seat->priv->properties, name);
00113 }
00114 
00115 gboolean
00116 seat_get_boolean_property (Seat *seat, const gchar *name)
00117 {
00118     return g_strcmp0 (seat_get_string_property (seat, name), "true") == 0;
00119 }
00120 
00121 gint
00122 seat_get_integer_property (Seat *seat, const gchar *name)
00123 {
00124     const gchar *value;
00125 
00126     value = seat_get_string_property (seat, name);
00127     return value ? atoi (value) : 0;
00128 }
00129 
00130 void
00131 seat_set_can_switch (Seat *seat, gboolean can_switch)
00132 {
00133     g_return_if_fail (seat != NULL);
00134 
00135     seat->priv->can_switch = can_switch;
00136 }
00137 
00138 gboolean
00139 seat_start (Seat *seat)
00140 {
00141     g_return_val_if_fail (seat != NULL, FALSE);
00142   
00143     SEAT_GET_CLASS (seat)->setup (seat);
00144     return SEAT_GET_CLASS (seat)->start (seat);
00145 }
00146 
00147 GList *
00148 seat_get_displays (Seat *seat)
00149 {
00150     g_return_val_if_fail (seat != NULL, NULL);
00151     return seat->priv->displays;
00152 }
00153 
00154 void
00155 seat_set_active_display (Seat *seat, Display *display)
00156 {
00157     g_return_if_fail (seat != NULL);
00158     SEAT_GET_CLASS (seat)->set_active_display (seat, display);
00159 }
00160 
00161 Display *
00162 seat_get_active_display (Seat *seat)
00163 {
00164     g_return_val_if_fail (seat != NULL, NULL);
00165     return seat->priv->active_display;
00166 }
00167 
00168 gboolean
00169 seat_get_can_switch (Seat *seat)
00170 {
00171     g_return_val_if_fail (seat != NULL, FALSE);
00172     return seat->priv->can_switch;
00173 }
00174 
00175 gboolean
00176 seat_get_allow_guest (Seat *seat)
00177 {
00178     g_return_val_if_fail (seat != NULL, FALSE);  
00179     return seat_get_boolean_property (seat, "allow-guest") && guest_account_is_installed ();
00180 }
00181 
00182 gboolean
00183 seat_get_greeter_allow_guest (Seat *seat)
00184 {
00185     g_return_val_if_fail (seat != NULL, FALSE);  
00186     return seat_get_allow_guest (seat) && seat_get_boolean_property (seat, "greeter-allow-guest");
00187 }
00188 
00189 static gboolean
00190 switch_to_user (Seat *seat, const gchar *username, gboolean unlock)
00191 {
00192     GList *link;
00193 
00194     /* Switch to active display if it exists */
00195     for (link = seat->priv->displays; link; link = link->next)
00196     {
00197         Display *display = link->data;
00198 
00199         /* If already logged in, then switch to that display */
00200         if (g_strcmp0 (display_get_username (display), username) == 0)        
00201         {
00202             if (username)
00203                 g_debug ("Switching to existing session for user %s", username);
00204             else
00205                 g_debug ("Switching to existing greeter");
00206             if (unlock)
00207                 display_unlock (display);
00208             seat_set_active_display (seat, display);
00209             return TRUE;
00210         }
00211     }
00212 
00213     return FALSE;
00214 }
00215 
00216 static gboolean
00217 display_switch_to_user_cb (Display *display, User *user, Seat *seat)
00218 {
00219     return switch_to_user (seat, user_get_name (user), TRUE);
00220 }
00221 
00222 static gboolean
00223 display_switch_to_guest_cb (Display *display, Seat *seat)
00224 {
00225     /* No guest account */
00226     if (!seat->priv->guest_username)
00227         return FALSE;
00228 
00229     return switch_to_user (seat, seat->priv->guest_username, TRUE);
00230 }
00231 
00232 static const gchar *
00233 display_get_guest_username_cb (Display *display, Seat *seat)
00234 {
00235     if (seat->priv->guest_username)
00236         return seat->priv->guest_username;
00237 
00238     seat->priv->guest_username = guest_account_setup ();
00239     return g_strdup (seat->priv->guest_username);
00240 }
00241 
00242 static gboolean
00243 run_script (Seat *seat, Display *display, const gchar *script_name, User *user)
00244 {
00245     Process *script;
00246     gboolean result = FALSE;
00247   
00248     script = process_new ();
00249 
00250     process_set_command (script, script_name);
00251 
00252     /* Set POSIX variables */
00253     process_set_clear_environment (script, TRUE);
00254     process_set_env (script, "SHELL", "/bin/sh");
00255 
00256     /* Variables required for regression tests */
00257     if (g_getenv ("LIGHTDM_TEST_ROOT"))
00258     {
00259         process_set_env (script, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
00260         process_set_env (script, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
00261         process_set_env (script, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
00262         process_set_env (script, "PATH", g_getenv ("PATH"));
00263     }
00264     else
00265         process_set_env (script, "PATH", "/usr/local/bin:/usr/bin:/bin");
00266 
00267     if (user)
00268     {
00269         process_set_env (script, "USER", user_get_name (user));
00270         process_set_env (script, "LOGNAME", user_get_name (user));
00271         process_set_env (script, "HOME", user_get_home_directory (user));
00272     }
00273     else
00274         process_set_env (script, "HOME", "/");
00275 
00276     SEAT_GET_CLASS (seat)->run_script (seat, display, script);
00277 
00278     if (process_start (script))
00279     {
00280         int exit_status;
00281 
00282         process_wait (script);
00283 
00284         exit_status = process_get_exit_status (script);
00285         if (WIFEXITED (exit_status))
00286         {
00287             g_debug ("Exit status of %s: %d", script_name, WEXITSTATUS (exit_status));
00288             result = WEXITSTATUS (exit_status) == EXIT_SUCCESS;
00289         }
00290     }
00291 
00292     g_object_unref (script);
00293 
00294     return result;
00295 }
00296 
00297 static void
00298 seat_real_run_script (Seat *seat, Display *display, Process *process)
00299 {  
00300 }
00301 
00302 static void
00303 emit_upstart_signal (const gchar *signal)
00304 {
00305     g_return_if_fail (signal != NULL);
00306     g_return_if_fail (signal[0] != 0);
00307 
00308     if (getuid () != 0)
00309         return;
00310 
00311     gchar *cmd = g_strdup_printf ("initctl -q emit %s DISPLAY_MANAGER=lightdm", signal);
00312     g_spawn_command_line_async (cmd, NULL); /* OK if it fails, probably not installed */
00313     g_free (cmd);
00314 }
00315 
00316 static gboolean
00317 display_display_server_ready_cb (Display *display, Seat *seat)
00318 {
00319     const gchar *script;
00320 
00321     /* Run setup script */
00322     script = seat_get_string_property (seat, "display-setup-script");
00323     if (script && !run_script (seat, display, script, NULL))
00324         return FALSE;
00325 
00326     emit_upstart_signal ("login-session-start");
00327 
00328     return TRUE;
00329 }
00330 
00331 static Session *
00332 display_create_session_cb (Display *display, Seat *seat)
00333 {
00334     return SEAT_GET_CLASS (seat)->create_session (seat, display);
00335 }
00336 
00337 static gboolean
00338 display_start_greeter_cb (Display *display, Seat *seat)
00339 {
00340     Session *session;
00341     const gchar *script;
00342 
00343     session = display_get_session (display);
00344 
00345     script = seat_get_string_property (seat, "greeter-setup-script");
00346     if (script)
00347         return !run_script (seat, display, script, session_get_user (session));
00348     else
00349         return FALSE;
00350 }
00351 
00352 static gboolean
00353 display_start_session_cb (Display *display, Seat *seat)
00354 {
00355     Session *session;
00356     const gchar *script;
00357 
00358     session = display_get_session (display);
00359 
00360     script = seat_get_string_property (seat, "session-setup-script");
00361     if (script)
00362         return !run_script (seat, display, script, session_get_user (session));
00363     else
00364         return FALSE;
00365 }
00366 
00367 static void
00368 session_stopped_cb (Session *session, Seat *seat)
00369 {
00370     Display *display = NULL;
00371     GList *link;
00372     const gchar *script;
00373   
00374     /* Work out what display this session is on, it's a bit hacky because we really should know already... */
00375     for (link = seat->priv->displays; link; link = link->next)
00376     {
00377         Display *d = link->data;
00378         if (display_get_session (d) == session)
00379         {
00380             display = d;
00381             break;
00382         }
00383     }
00384     g_return_if_fail (display != NULL);
00385 
00386     /* Cleanup */
00387     script = seat_get_string_property (seat, "session-cleanup-script");
00388     if (script)
00389         run_script (seat, display, script, session_get_user (session));
00390 
00391     if (seat->priv->guest_username && strcmp (session_get_username (session), seat->priv->guest_username) == 0)
00392     {
00393         guest_account_cleanup (seat->priv->guest_username);
00394         g_free (seat->priv->guest_username);
00395         seat->priv->guest_username = NULL;
00396     }
00397 }
00398 
00399 static gboolean
00400 display_session_started_cb (Display *display, Seat *seat)
00401 {
00402     g_signal_connect (display_get_session (display), "stopped", G_CALLBACK (session_stopped_cb), seat);
00403     emit_upstart_signal ("desktop-session-start");
00404     return FALSE;
00405 }
00406 
00407 static void
00408 display_ready_cb (Display *display, Seat *seat)
00409 {
00410     /* Switch to this new display */
00411     g_debug ("New display ready, switching to it");
00412     SEAT_GET_CLASS (seat)->set_active_display (seat, display);
00413 }
00414 
00415 static void
00416 check_stopped (Seat *seat)
00417 {
00418     if (seat->priv->stopping &&
00419         !seat->priv->stopped &&
00420         g_list_length (seat->priv->displays) == 0)
00421     {
00422         seat->priv->stopped = TRUE;
00423         g_debug ("Seat stopped");
00424         g_signal_emit (seat, signals[STOPPED], 0);
00425     }
00426 }
00427 
00428 static void
00429 display_stopped_cb (Display *display, Seat *seat)
00430 {
00431     seat->priv->displays = g_list_remove (seat->priv->displays, display);
00432     g_signal_handlers_disconnect_matched (display, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
00433     g_signal_emit (seat, signals[DISPLAY_REMOVED], 0, display);
00434     g_object_unref (display);
00435 
00436     check_stopped (seat);
00437 }
00438 
00439 static gboolean
00440 switch_to_user_or_start_greeter (Seat *seat, const gchar *username, gboolean use_existing, gboolean is_guest, const gchar *session_name, gboolean is_lock, gboolean autologin)
00441 {
00442     Display *display;
00443     DisplayServer *display_server;
00444 
00445     /* Switch to existing if it exists */
00446     if (use_existing && switch_to_user (seat, username, FALSE))
00447         return TRUE;
00448 
00449     /* If one don't exist then start a greeter */
00450     if (autologin)
00451     {
00452         if (is_guest)
00453             g_debug ("Starting new display for automatic guest login");
00454         else if (username)
00455             g_debug ("Starting new display for automatic login as user %s", username);
00456         else
00457             g_debug ("Starting new display for greeter");
00458     }
00459     else
00460     {
00461         if (is_guest)
00462             g_debug ("Starting new display for greeter with guest selected");
00463         else if (username)
00464             g_debug ("Starting new display for greeter with user %s selected", username);
00465         else if (is_lock)
00466             g_debug ("Starting new display for greeter (lock screen)");
00467         else
00468             g_debug ("Starting new display for greeter");
00469     }
00470 
00471     display_server = SEAT_GET_CLASS (seat)->create_display_server (seat);
00472     display = display_new (display_server);
00473     g_object_unref (display_server);
00474 
00475     g_signal_connect (display, "display-server-ready", G_CALLBACK (display_display_server_ready_cb), seat);  
00476     g_signal_connect (display, "switch-to-user", G_CALLBACK (display_switch_to_user_cb), seat);
00477     g_signal_connect (display, "switch-to-guest", G_CALLBACK (display_switch_to_guest_cb), seat);
00478     g_signal_connect (display, "get-guest-username", G_CALLBACK (display_get_guest_username_cb), seat);
00479     g_signal_connect (display, "create-session", G_CALLBACK (display_create_session_cb), seat);
00480     g_signal_connect (display, "start-greeter", G_CALLBACK (display_start_greeter_cb), seat);
00481     g_signal_connect (display, "start-session", G_CALLBACK (display_start_session_cb), seat);
00482     g_signal_connect_after (display, "start-session", G_CALLBACK (display_session_started_cb), seat);
00483     g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
00484     g_signal_connect (display, "stopped", G_CALLBACK (display_stopped_cb), seat);
00485     display_set_greeter_session (display, seat_get_string_property (seat, "greeter-session"));
00486     display_set_session_wrapper (display, seat_get_string_property (seat, "session-wrapper"));
00487     display_set_hide_users_hint (display, seat_get_boolean_property (seat, "greeter-hide-users"));
00488     display_set_show_manual_login_hint (display, seat_get_boolean_property (seat, "greeter-show-manual-login"));
00489     if (is_lock)
00490         display_set_lock_hint (display, TRUE);
00491     display_set_allow_guest (display, seat_get_allow_guest (seat));
00492     display_set_greeter_allow_guest (display, seat_get_greeter_allow_guest (seat));
00493     if (autologin)
00494         display_set_autologin_user (display, username, is_guest, 0);
00495     else
00496         display_set_select_user_hint (display, username, is_guest);
00497     if (!session_name)
00498         session_name = seat_get_string_property (seat, "user-session");
00499     display_set_user_session (display, session_name);
00500 
00501     seat->priv->displays = g_list_append (seat->priv->displays, display);
00502     g_signal_emit (seat, signals[DISPLAY_ADDED], 0, display);
00503 
00504     /* Switch to this display if currently not looking at anything */
00505     if (!seat->priv->active_display)
00506         seat_set_active_display (seat, display);
00507 
00508     return display_start (display);
00509 }
00510 
00511 gboolean
00512 seat_switch_to_greeter (Seat *seat)
00513 {
00514     g_return_val_if_fail (seat != NULL, FALSE);
00515 
00516     if (!seat->priv->can_switch)
00517         return FALSE;
00518 
00519     g_debug ("Switching to greeter");
00520     return switch_to_user_or_start_greeter (seat, NULL, TRUE, FALSE, NULL, FALSE, FALSE);
00521 }
00522 
00523 gboolean
00524 seat_switch_to_user (Seat *seat, const gchar *username, const gchar *session_name)
00525 {
00526     g_return_val_if_fail (seat != NULL, FALSE);
00527     g_return_val_if_fail (username != NULL, FALSE);
00528 
00529     if (!seat->priv->can_switch)
00530         return FALSE;
00531 
00532     g_debug ("Switching to user %s", username);
00533     return switch_to_user_or_start_greeter (seat, username, TRUE, FALSE, session_name, FALSE, FALSE);
00534 }
00535 
00536 gboolean
00537 seat_switch_to_guest (Seat *seat, const gchar *session_name)
00538 {
00539     g_return_val_if_fail (seat != NULL, FALSE);
00540 
00541     if (!seat->priv->can_switch || !seat_get_allow_guest (seat))
00542         return FALSE;
00543 
00544     if (seat->priv->guest_username)
00545         g_debug ("Switching to existing guest account %s", seat->priv->guest_username);
00546     else
00547         g_debug ("Switching to new guest account");
00548     return switch_to_user_or_start_greeter (seat, seat->priv->guest_username, TRUE, TRUE, session_name, FALSE, TRUE);
00549 }
00550 
00551 gboolean
00552 seat_lock (Seat *seat, const gchar *username)
00553 {
00554     g_return_val_if_fail (seat != NULL, FALSE);
00555 
00556     if (!seat->priv->can_switch)
00557         return FALSE;
00558 
00559     g_debug ("Locking seat");
00560     return switch_to_user_or_start_greeter (seat, username, FALSE, FALSE, NULL, TRUE, FALSE);
00561 }
00562 
00563 void
00564 seat_stop (Seat *seat)
00565 {
00566     g_return_if_fail (seat != NULL);
00567 
00568     if (seat->priv->stopping)
00569         return;
00570 
00571     g_debug ("Stopping seat");
00572     seat->priv->stopping = TRUE;
00573     SEAT_GET_CLASS (seat)->stop (seat);
00574 }
00575 
00576 gboolean
00577 seat_get_is_stopping (Seat *seat)
00578 {
00579     g_return_val_if_fail (seat != NULL, FALSE);
00580     return seat->priv->stopping;
00581 }
00582 
00583 static void
00584 seat_real_setup (Seat *seat)
00585 {
00586 }
00587 
00588 static gboolean
00589 seat_real_start (Seat *seat)
00590 {
00591     const gchar *autologin_username;
00592 
00593     g_debug ("Starting seat");
00594 
00595     /* Start showing a greeter */
00596     autologin_username = seat_get_string_property (seat, "autologin-user");
00597     if (g_strcmp0 (autologin_username, "") == 0)
00598         autologin_username = NULL;
00599 
00600     if (autologin_username)
00601         return switch_to_user_or_start_greeter (seat, autologin_username, TRUE, FALSE, NULL, FALSE, TRUE);
00602     else if (seat_get_boolean_property (seat, "autologin-guest"))
00603         return switch_to_user_or_start_greeter (seat, NULL, TRUE, TRUE, NULL, FALSE, TRUE);
00604     else
00605         return switch_to_user_or_start_greeter (seat, NULL, TRUE, FALSE, NULL, FALSE, FALSE);
00606 }
00607 
00608 static void
00609 seat_real_set_active_display (Seat *seat, Display *display)
00610 {
00611     if (display == seat->priv->active_display)
00612         return;
00613 
00614     if (seat->priv->active_display)
00615     {
00616         /* Stop the existing display if it is a greeter */
00617         if (!display_get_username (seat->priv->active_display))
00618         {
00619             g_debug ("Stopping greeter display being switched from");
00620             display_stop (seat->priv->active_display);
00621         }
00622         /* Otherwise lock it */
00623         else
00624         {
00625             display_lock (seat->priv->active_display);
00626         }
00627         g_object_unref (seat->priv->active_display);
00628     }
00629     seat->priv->active_display = g_object_ref (display);
00630 }
00631 
00632 static void
00633 seat_real_stop (Seat *seat)
00634 {
00635     GList *link;
00636 
00637     check_stopped (seat);
00638     if (seat->priv->stopped)
00639         return;
00640 
00641     for (link = seat->priv->displays; link; link = link->next)
00642     {
00643         Display *display = link->data;
00644         display_stop (display);
00645     }
00646 }
00647 
00648 static void
00649 seat_init (Seat *seat)
00650 {
00651     seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_TYPE, SeatPrivate);
00652     seat->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
00653 }
00654 
00655 static void
00656 seat_finalize (GObject *object)
00657 {
00658     Seat *self;
00659 
00660     self = SEAT (object);
00661 
00662     g_hash_table_unref (self->priv->properties);
00663     g_free (self->priv->guest_username);
00664     g_list_free_full (self->priv->displays, g_object_unref);
00665     if (self->priv->active_display)
00666         g_object_unref (self->priv->active_display);
00667 
00668     G_OBJECT_CLASS (seat_parent_class)->finalize (object);
00669 }
00670 
00671 static void
00672 seat_class_init (SeatClass *klass)
00673 {
00674     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00675 
00676     klass->setup = seat_real_setup;
00677     klass->start = seat_real_start;
00678     klass->set_active_display = seat_real_set_active_display;
00679     klass->run_script = seat_real_run_script;
00680     klass->stop = seat_real_stop;
00681 
00682     object_class->finalize = seat_finalize;
00683 
00684     g_type_class_add_private (klass, sizeof (SeatPrivate));
00685 
00686     signals[DISPLAY_ADDED] =
00687         g_signal_new ("display-added",
00688                       G_TYPE_FROM_CLASS (klass),
00689                       G_SIGNAL_RUN_LAST,
00690                       G_STRUCT_OFFSET (SeatClass, display_added),
00691                       NULL, NULL,
00692                       g_cclosure_marshal_VOID__OBJECT,
00693                       G_TYPE_NONE, 1, DISPLAY_TYPE);
00694     signals[DISPLAY_REMOVED] =
00695         g_signal_new ("display-removed",
00696                       G_TYPE_FROM_CLASS (klass),
00697                       G_SIGNAL_RUN_LAST,
00698                       G_STRUCT_OFFSET (SeatClass, display_removed),
00699                       NULL, NULL,
00700                       g_cclosure_marshal_VOID__OBJECT,
00701                       G_TYPE_NONE, 1, DISPLAY_TYPE);
00702     signals[STOPPED] =
00703         g_signal_new ("stopped",
00704                       G_TYPE_FROM_CLASS (klass),
00705                       G_SIGNAL_RUN_LAST,
00706                       G_STRUCT_OFFSET (SeatClass, stopped),
00707                       NULL, NULL,
00708                       g_cclosure_marshal_VOID__VOID,
00709                       G_TYPE_NONE, 0);
00710 }