diff --git a/src/msm/Makefile.am b/src/msm/Makefile.am index 53d7c1fea..7d9a7cf99 100644 --- a/src/msm/Makefile.am +++ b/src/msm/Makefile.am @@ -1,5 +1,5 @@ -INCLUDES=@MSM_CFLAGS@ -DMSM_PKGDATADIR=\"$(datadir)/msm\" +INCLUDES=@MSM_CFLAGS@ -DMSM_PKGDATADIR=\"$(datadir)/msm\" msm_SOURCES= \ client.c \ diff --git a/src/msm/client.c b/src/msm/client.c index abe9f4070..70c02db95 100644 --- a/src/msm/client.c +++ b/src/msm/client.c @@ -30,8 +30,40 @@ struct _MsmClient char *hostname; char *desc; int restart_style; + GList *properties; }; +static GList* find_property_link_by_name (MsmClient *client, + const char *name); + +static SmProp* find_property_by_name (MsmClient *client, + const char *name); + +static gboolean find_card8_property (MsmClient *client, + const char *name, + int *result) + +static gboolean find_string_property (MsmClient *client, + const char *name, + char **result); + +static gboolean find_vector_property (MsmClient *client, + const char *name, + int *argcp, + char ***argvp); + +static gboolean get_card8_value (SmProp *prop, + int *result); +static gboolean get_string_value (SmProp *prop, + char **result); +static gboolean get_vector_value (SmProp *prop, + int *argcp, + char ***argvp); + +static SmProp* copy_property (SmProp *prop); + +#define DEFAULT_RESTART_STYLE SmRestartIfRunning + MsmClient* msm_client_new (MsmServer *server, SmsConn cnxn) @@ -46,7 +78,8 @@ msm_client_new (MsmServer *server, client->id = NULL; client->hostname = NULL; client->desc = g_strdup ("unknown"); - client->restart_style = SmRestartIfRunning; + client->restart_style = DEFAULT_RESTART_STYLE; + client->properties = NULL; return client; } @@ -55,12 +88,25 @@ void msm_client_free (MsmClient *client) { IceConn ice_cnxn; - + GList *tmp; + ice_cnxn = SmsGetIceConnection (client->cnxn); SmsCleanUp (client->cnxn); IceSetShutdownNegotiation (ice_cnxn, False); IceCloseConnection (ice_cnxn); - + + tmp = client->properties; + while (tmp != NULL) + { + SmProp *prop = tmp->data; + + SmFreeProperty (prop); + + tmp = tmp->next; + } + + g_list_free (client->properties); + g_free (client->id); g_free (client->hostname); g_free (client->desc); @@ -92,6 +138,18 @@ msm_client_get_server (MsmClient *client) return client->server; } +const char* +msm_client_get_id (MsmClient *client) +{ + return client->id; +} + +int +msm_client_get_restart_style (MsmClient *client) +{ + return client->restart_style; +} + void msm_client_register (MsmClient *client, const char *id) @@ -233,23 +291,267 @@ msm_client_save_confirmed (MsmClient *client, } void -msm_client_set_property (MsmClient *client, - SmProp *prop) +msm_client_set_property_taking_ownership (MsmClient *client, + SmProp *prop) { + /* we own prop which should be freed with SmFreeProperty() */ + GList *list; + if (prop->name == NULL) + { + SmFreeProperty (prop); + return; + } + + list = find_property_link_by_name (prop->name); + if (list) + { + SmFreeProperty (list->data); + list->data = prop; + } + else + { + client->properties = g_list_prepend (client->properties, + prop); + } + /* update pieces of the client struct */ + if (strcmp (prop->name, "SmRestartStyleHint") == 0) + { + int hint; + if (get_card8_value (prop, &hint)) + client->restart_style = hint; + else + client->restart_style = DEFAULT_RESTART_STYLE; + } } void msm_client_unset_property (MsmClient *client, const char *name) { + GList *list; + list = find_property_link_by_name (prop->name); + if (list) + { + SmFreeProperty (list->data); + client->properties = g_list_delete_link (client->properties, + list); + } + + /* Return to default values */ + if (strcmp (name, "SmRestartStyleHint") == 0) + { + client->restart_style = DEFAULT_RESTART_STYLE; + } } void -msm_client_send_properties (MsmClient *client) +msm_client_send_properties (MsmClient *client) { - SmsReturnProperties (/* FIXME */); + int n_props; + SmProp **props; + GList *tmp; + int i; + + n_props = g_list_length (client->properties); + props = g_new (SmProp, n_props); + i = 0; + tmp = client->properties; + while (tmp != NULL) + { + props[i] = tmp->data; + + tmp = tmp->next; + ++i; + } + + SmsReturnProperties (client->cnxn, n_props, props); + + g_free (props); +} + +/* Property functions stolen from gnome-session */ + +static GList* +find_property_link_by_name (MsmClient *client, + const char *name) +{ + GList *list; + + for (list = client->properties; list; list = list->next) + { + SmProp *prop = (SmProp *) list->data; + if (strcmp (prop->name, name) == 0) + return list; + } + + return NULL; +} + + +SmProp* +find_property_by_name (MsmClient *client, const char *name) +{ + GList *list; + + list = find_property_link_by_name (client, name); + + return list ? list->data : NULL; +} + +gboolean +find_card8_property (MsmClient *client, const char *name, + int *result) +{ + SmProp *prop; + + g_return_val_if_fail (result != NULL, FALSE); + + prop = find_property_by_name (client, name); + if (prop == NULL) + return FALSE; + else + return get_card8_value (prop, result); +} + +gboolean +find_string_property (MsmClient *client, const char *name, + char **result) +{ + SmProp *prop; + + g_return_val_if_fail (result != NULL, FALSE); + + prop = find_property_by_name (client, name); + if (prop == NULL) + return FALSE; + else + return get_string_value (prop, result); +} + +gboolean +find_vector_property (MsmClient *client, const char *name, + int *argcp, char ***argvp) +{ + SmProp *prop; + + g_return_val_if_fail (argcp != NULL, FALSE); + g_return_val_if_fail (argvp != NULL, FALSE); + + prop = find_property_by_name (client, name); + if (prop == NULL) + return FALSE; + else + return get_vector_value (prop, argcp, argvp); +} + +static gboolean +get_card8_value (SmProp *prop, + int *result) +{ + g_return_val_if_fail (result != NULL, FALSE); + + if (strcmp (prop->type, SmCARD8) == 0) + { + char *p; + p = prop->vals[0].value; + *result = *p; + return TRUE; + } + else + return FALSE +} + +static gboolean +get_string_value (SmProp *prop, + char **result) +{ + g_return_val_if_fail (result != NULL, FALSE); + + if (strcmp (prop->type, SmARRAY8) == 0) + { + *result = g_malloc (prop->vals[0].length + 1); + memcpy (*result, prop->vals[0].value, prop->vals[0].length); + (*result)[prop->vals[0].length] = '\0'; + return TRUE; + } + else + return FALSE; +} + +static gboolean +get_vector_value (SmProp *prop, + int *argcp, + char ***argvp) +{ + g_return_val_if_fail (argcp != NULL, FALSE); + g_return_val_if_fail (argvp != NULL, FALSE); + + if (strcmp (prop->type, SmLISTofARRAY8) == 0) + { + int i; + + *argcp = prop->num_vals; + *argvp = g_new0 (char *, *argcp + 1); + for (i = 0; i < *argcp; ++i) + { + (*argvp)[i] = g_malloc (prop->vals[i].length + 1); + memcpy ((*argvp)[i], prop->vals[i].value, prop->vals[i].length); + (*argvp)[i][prop->vals[i].length] = '\0'; + } + + return TRUE; + } + else + return FALSE; +} + +static SmProp* +copy_property (SmProp *prop) +{ + int i; + SmProp *copy; + + /* This all uses malloc so we can use SmFreeProperty() */ + + copy = msm_non_glib_malloc (sizeof (SmProp)); + + if (prop->name) + copy->name = msm_non_glib_strdup (prop->name); + else + copy->name = NULL; + + if (prop->type) + copy->type = msm_non_glib_strdup (prop->type); + else + copy->type = NULL; + + copy->num_vals = prop->num_vals; + copy->vals = NULL; + + if (copy->num_vals > 0 && prop->vals) + { + copy->vals = msm_non_glib_malloc (sizeof (SmPropValue) * copy->num_vals); + + for (i = 0; i < copy->num_vals; i++) + { + if (prop->vals[i].value) + { + copy->vals[i].length = prop->vals[i].length; + copy->vals[i].value = msm_non_glib_malloc (copy->vals[i].length); + memcpy (copy->vals[i].value, prop->vals[i].value, + copy->vals[i].length); + } + else + { + copy->vals[i].length = 0; + copy->vals[i].value = NULL; + } + } + } + + return copy; } diff --git a/src/msm/client.h b/src/msm/client.h index 247596983..b542bd9ad 100644 --- a/src/msm/client.h +++ b/src/msm/client.h @@ -66,9 +66,12 @@ SmsConn msm_client_get_connection (MsmClient *client); const char* msm_client_get_description (MsmClient *client); MsmClientState msm_client_get_state (MsmClient *client); MsmServer* msm_client_get_server (MsmClient *client); +/* can return NULL */ +const char* msm_client_get_id (MsmClient *client); +int msm_client_get_restart_style (MsmClient *client); -void msm_client_set_property (MsmClient *client, - SmProp *prop); +void msm_client_set_property_taking_ownership (MsmClient *client, + SmProp *prop); void msm_client_unset_property (MsmClient *client, const char *name); void msm_client_send_properties (MsmClient *client); diff --git a/src/msm/main.c b/src/msm/main.c index 27f91ef3a..4b87b2ac3 100644 --- a/src/msm/main.c +++ b/src/msm/main.c @@ -60,7 +60,8 @@ main (int argc, char **argv) gboolean failsafe; struct sigaction act; sigset_t empty_mask; - + MsmServer *server; + sigemptyset (&empty_mask); act.sa_handler = SIG_IGN; act.sa_mask = empty_mask; @@ -118,7 +119,14 @@ main (int argc, char **argv) ++i; } - + + if (failsafe) + server = msm_server_new_failsafe (); + else + server = msm_server_new (session_name); + + msm_server_launch_session (server); + main_loop = g_main_loop_new (NULL, FALSE); g_main_run (main_loop); diff --git a/src/msm/server.c b/src/msm/server.c index d5de84d2d..1370f1e1a 100644 --- a/src/msm/server.c +++ b/src/msm/server.c @@ -42,6 +42,7 @@ */ #include "server.h" +#include "session.h" /* FIXME we need to time out anytime we're waiting for a client * response, such as InteractDone, SaveYourselfDone, ConnectionClosed @@ -50,13 +51,15 @@ struct _MsmServer { + MsmSession *session; GList *clients; IceAuthDataEntry *auth_entries; int n_auth_entries; MsmClient *currently_interacting; GList *interact_pending; - - guint in_shutdown : 1; + + guint in_global_save : 1; + guint in_shutdown : 1; /* TRUE only if in_global_save */ guint save_allows_interaction : 1; }; @@ -112,19 +115,21 @@ static gboolean create_auth_entries (MsmServer *server, int n_listen_objs); static void free_auth_entries (IceAuthDataEntry *entries); -MsmServer* -msm_server_new (void) +static void +msm_server_new_with_session (MsmSession *session) { char errbuf[256]; MsmServer *server; server = g_new (MsmServer, 1); + server->session = session; server->clients = NULL; server->auth_entries = NULL; server->n_auth_entries = 0; server->currently_interacting = NULL; server->interact_pending = NULL; + server->in_global_save = FALSE; server->in_shutdown = FALSE; server->save_allows_interaction = FALSE; @@ -140,6 +145,22 @@ msm_server_new (void) return server; } +MsmServer* +msm_server_new (const char *session_name) +{ + MsmSession *session; + + session = msm_session_new (session_name); + + return msm_server_new_with_session (session); +} + +MsmServer* +msm_server_new_failsafe (void) +{ + return msm_server_new_with_session (msm_session_get_failsafe ()); +} + void msm_server_free (MsmServer *server) { @@ -225,12 +246,22 @@ register_client_callback (SmsConn cnxn, } else { - /* FIXME check for pending/known client IDs and register the client, - * return TRUE if we know about this previous_id + /* check for pending/known client IDs and register the client, + * return TRUE if we know about this previous_id, return FALSE + * if we do not know about it or it's already being used. */ - - free (previous_id); - return FALSE; + if (msm_server_client_id_in_use (server, previous_id) || + !msm_session_client_id_known (server->session, previous_id)) + { + free (previous_id); + return FALSE; + } + else + { + msm_client_register (client, previous_id); + free (previous_id); + return TRUE; + } } } @@ -389,9 +420,10 @@ set_properties_callback (SmsConn cnxn, i = 0; while (i < numProps) { - msm_client_set_property (client, props[i]); + msm_client_set_property_taking_ownership (client, props[i]); - SmFreeProperty (props[i]); + /* Client owns it, so don't do this. */ + /* SmFreeProperty (props[i]); */ ++i; } @@ -452,10 +484,8 @@ new_client_callback (SmsConn cnxn, */ if (server->in_shutdown) { - /* have to use malloc() */ - *failure_reason_ret = malloc (256); - g_strncpy (*failure_reason_ret, _("Refusing new client connection because the session is currently being shut down\n"), 255); - (*failure_reason_ret)[255] = '\0'; /* paranoia */ + *failure_reason_ret = + msm_non_glib_strdup (_("Refusing new client connection because the session is currently being shut down\n")); return FALSE; } @@ -536,6 +566,8 @@ msm_server_save_all (MsmServer *server, { GList *tmp; + server->in_global_save = TRUE; + if (shut_down) /* never cancel a shutdown here */ server->in_shutdown = TRUE; @@ -567,7 +599,8 @@ msm_server_cancel_shutdown (MsmServer *server) if (!server->in_shutdown) return; - + + server->in_global_save = FALSE; server->in_shutdown = FALSE; /* Cancel any interactions in progress */ @@ -581,16 +614,26 @@ msm_server_cancel_shutdown (MsmServer *server) MsmClient *client; client = tmp->data; - - if (msm_client_get_state (client) == MSM_CLIENT_STATE_SAVING) - msm_client_shutdown_cancelled (client); + + switch (msm_client_get_state (client)) + { + case MSM_CLIENT_STATE_SAVING: + case MSM_CLIENT_STATE_PHASE2_REQUESTED: + case MSM_CLIENT_STATE_SAVING_PHASE2: + case MSM_CLIENT_STATE_SAVE_DONE: + case MSM_CLIENT_STATE_SAVE_FAILED: + msm_client_shutdown_cancelled (client); + break; + default: + break; + } tmp = tmp->next; } } /* Think about whether to move to phase 2, return to idle state, - * or shut down + * save session to disk, or shut down */ void msm_server_consider_phase_change (MsmServer *server) @@ -658,6 +701,37 @@ msm_server_consider_phase_change (MsmServer *server) return; } + if (server->in_global_save) + { + GList *tmp; + + tmp = server->clients; + while (tmp != NULL) + { + MsmClient *client = tmp->data; + + switch (msm_client_get_state (client)) + { + case MSM_CLIENT_STATE_SAVE_DONE: + /* Update it in the session, since it saved successfully. */ + msm_session_update_client (server->session, client); + break; + default: + break; + } + + tmp = tmp->next; + } + + /* Write to disk. */ + msm_session_save (server->session); + } + + /* msm_session_save() may have cancelled any shutdown that was in progress, + * so don't assume here that we are still in_global_save or in_shutdown. + * Also, client states may have changed. + */ + if (server->in_shutdown) { /* We are shutting down, and all clients are in the idle state. @@ -678,10 +752,12 @@ msm_server_consider_phase_change (MsmServer *server) tmp = tmp->next; } } + + /* We don't leave the in_shutdown/in_global_save states in this case */ } - else + else if (server->in_global_save) { - /* Send SaveComplete to all clients that are finished saving */ + /* send SaveComplete to all clients that are finished saving */ GList *tmp; tmp = server->clients; @@ -701,6 +777,9 @@ msm_server_consider_phase_change (MsmServer *server) tmp = tmp->next; } + + /* Leave in_global_save state */ + server->in_global_save = FALSE; } } @@ -721,6 +800,40 @@ msm_server_foreach_client (MsmServer *server, } } +gboolean +msm_server_client_id_in_use (MsmServer *server, + const char *id) +{ + GList *tmp; + + tmp = server->clients; + while (tmp != NULL) + { + MsmClient *client = tmp->data; + const char *cid; + + cid = msm_client_get_id (client); + + if (cid && strcmp (cid, id) == 0) + return TRUE; + + tmp = tmp->next; + } + + return FALSE; +} + +void +msm_server_launch_session (MsmServer *server) +{ + msm_session_launch (server->session); +} + +gboolean +msm_server_in_shutdown (MsmServer *server) +{ + return server->in_shutdown; +} /* * ICE utility code, cut-and-pasted from Metacity, and in turn @@ -1000,13 +1113,7 @@ create_auth_entries (MsmServer *server, original_umask = umask (0077); /* disallow non-owner access */ - path = g_getenv ("SM_SAVE_DIR"); - if (!path) - { - path = g_get_home_dir (); - if (!path) - path = "."; - } + path = msm_get_work_directory (); err = NULL; tmpl = g_strconcat (path, "/msm-add-commands-XXXXXX", NULL); @@ -1138,4 +1245,3 @@ free_auth_entries (IceAuthDataEntry *entries, add_file = NULL; remove_file = NULL; } - diff --git a/src/msm/server.h b/src/msm/server.h index d9d665583..359c2c622 100644 --- a/src/msm/server.h +++ b/src/msm/server.h @@ -31,8 +31,9 @@ typedef struct _MsmServer MsmServer; typedef void (* MsmClientFunc) (MsmClient* client); -MsmServer* msm_server_new (void); -void msm_server_free (MsmServer *server); +MsmServer* msm_server_new (const char *session_name); +MsmServer* msm_server_new_failsafe (void); +void msm_server_free (MsmServer *server); void msm_server_queue_interaction (MsmServer *server, MsmClient *client); @@ -52,4 +53,11 @@ void msm_server_drop_client (MsmServer *server, void msm_server_next_pending_interaction (MsmServer *server); +gboolean msm_server_client_id_in_use (MsmServer *server, + const char *id); + +void msm_server_launch_session (MsmServer *server); + +gboolean msm_server_in_shutdown (MsmServer *server); + #endif diff --git a/src/msm/session.c b/src/msm/session.c new file mode 100644 index 000000000..f204993bf --- /dev/null +++ b/src/msm/session.c @@ -0,0 +1,358 @@ +/* msm session */ + +/* + * Copyright (C) 2001 Havoc Pennington + * + * 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 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include "session.h" + +typedef struct _MsmSavedClient MsmSavedClient; + +struct _MsmSavedClient +{ + char **restart_command; + +}; + +struct _MsmSession +{ + char *name; + GList *clients; + char *filename; + char *full_filename; + int lock_fd; +}; + +typedef enum +{ + MSM_SESSION_FAILURE_OPENING_FILE, + MSM_SESSION_FAILURE_LOCKING, + MSM_SESSION_FAILURE_BAD_FILE, + MSM_SESSION_FAILURE_EMPTY +} MsmSessionFailureReason; + +static GHashTable *sessions = NULL; + +static MsmSession* recover_failed_session (MsmSession *session, + MsmSessionFailureReason reason, + const char *details); + +static gboolean parse_session_file (MsmSession *session, + GError **error); + +void +msm_session_update_client (MsmSession *session, + MsmClient *client) +{ + + +} + +void +msm_session_remove_client (MsmSession *session, + MsmClient *client) +{ + + +} + +gboolean +msm_session_client_id_known (MsmSession *session, + const char *previous_id) +{ + + +} + +void +msm_session_launch (MsmSession *session) +{ + + +} + +static const char* +session_dir (void) +{ + static char *dir; + + if (dir == NULL) + { + dir = g_strconcat (msm_get_work_directory (), + "/sessions", + NULL); + } + + return dir; +} + +static void +set_close_on_exec (int fd) +{ + int val; + + val = fcntl (fd, F_GETFD, 0); + if (val < 0) + { + gconf_log (GCL_DEBUG, "couldn't F_GETFD: %s\n", g_strerror (errno)); + return; + } + + val |= FD_CLOEXEC; + + if (fcntl (fd, F_SETFD, val) < 0) + gconf_log (GCL_DEBUG, "couldn't F_SETFD: %s\n", g_strerror (errno)); +} + +/* Your basic Stevens cut-and-paste */ +static int +lock_reg (int fd, int cmd, int type, off_t offset, int whence, off_t len) +{ + struct flock lock; + + lock.l_type = type; /* F_RDLCK, F_WRLCK, F_UNLCK */ + lock.l_start = offset; /* byte offset relative to whence */ + lock.l_whence = whence; /* SEEK_SET, SEEK_CUR, SEEK_END */ + lock.l_len = len; /* #bytes, 0 for eof */ + + return fcntl (fd, cmd, &lock); +} + +#define lock_entire_file(fd) \ + lock_reg ((fd), F_SETLK, F_WRLCK, 0, SEEK_SET, 0) +#define unlock_entire_file(fd) \ + lock_reg ((fd), F_SETLK, F_UNLCK, 0, SEEK_SET, 0) + +static MsmSession* +msm_session_get_for_filename (const char *name, + const char *filename) +{ + MsmSession *session; + int fd = -1; + GError *dir_error = NULL; + GError *err; + gboolean use_global_file; + + session = g_hash_table_lookup (sessions, filename); + if (session) + return session; + + session = g_new0 (MsmSession, 1); + session->name = g_strdup (name); + session->clients = NULL; + session->filename = g_strdup (filename); + session->full_filename = g_strconcat (session_dir (), "/", filename, NULL); + session->lock_fd = -1; + + dir_error = NULL; + msm_create_dir_and_parents (session_dir (), &dir_error); + /* We save dir_error for later; if creating the file fails, + * we give dir_error in the reason. + */ + + /* To use a session, we need to lock the file in the user's + * save dir (by default in .msm/sessions/). + * + * If the file didn't previously exist, then we + * init the session from the global session of the same name, + * if any. + * + * This locking stuff has several races in it, and probably doesn't + * work over NFS, and all that jazz, but avoiding the races + * introduces stale lock issues, which are in practice more serious + * for users than the usual issues one worries about when locking. + */ + + fd = open (session->full_filename, O_RDWR | O_CREAT | O_EXCL, 0700); + + if (fd < 0) + { + char *message; + + message = g_strdup_printf (_("Failed to open the session file '%s': %s (%s)"), + session->full_filename, + g_strerror (errno), + dir_error ? + dir_error->message : + _("file's parent directory created successfully")); + + if (dir_error) + g_error_free (dir_error); + + session = recover_failed_session (session, + MSM_SESSION_FAILURE_OPENING_FILE, + message); + + g_free (message); + + return session; + } + + if (dir_error) + { + g_error_free (dir_error); + dir_error = NULL; + } + + if (lock_entire_file (fd) < 0) + { + char *message; + + close (fd); + + message = g_strdup_printf (_("Failed to lock the session file '%s': %s"), + session->full_filename, + g_strerror (errno)); + + session = recover_failed_session (session, + MSM_SESSION_FAILURE_LOCKING, + message); + + g_free (message); + + return session; + } + + session->lock_fd = fd; + set_close_on_exec (fd); + + err = NULL; + if (!parse_session_file (session, &err)) + { + char *message; + + message = g_strdup_printf (_("Failed to parse the session file '%s': %s\n"), + session->full_filename, + err->message); + + g_error_free (err); + + session = recover_failed_session (session, + MSM_SESSION_FAILURE_BAD_FILE, + message); + + g_free (message); + + return session; + } + + if (session->clients == NULL) + { + session = recover_failed_session (session, + MSM_SESSION_FAILURE_EMPTY, + _("Session doesn't contain any applications")); + + return session; + } + + return session; +} + +MsmSession* +msm_session_get (const char *name) +{ + if (name == NULL) + { + return msm_session_get_for_filename (_("Default"), "Default.session"); + } + else + { + char *filename; + char *p; + MsmSession *session; + + filename = g_strconcat (name, ".session", NULL); + + /* Remove path separators from the filename */ + p = filename; + while (*p) + { + if (*p == '/') + *p = '_'; + ++p; + } + + session = msm_session_get_for_filename (name, filename); + + g_free (filename); + + return session; + } +} + +MsmSession* +msm_session_get_failsafe (void) +{ + return msm_session_get_for_filename (_("Failsafe"), "Failsafe.session"); +} + +void +msm_session_save (MsmSession *session, + MsmServer *server) +{ + + +} + +static void +recover_failed_session (MsmSession *session, + MsmSessionFailureReason reason, + const char *details) +{ + + +} + +static gboolean +parse_session_file (MsmSession *session, + GError **error) +{ + char *parse_file; + struct stat sb; + gboolean file_empty; + + parse_file = NULL; + file_empty = FALSE; + + /* If the file is empty, probably because we just created it or have + * never saved our session, then parse the global session file + * instead of the user session file for our initial state. + */ + if (fstat (session->lock_fd, &sb) < 0) + { + /* Can't imagine this actually happening */ + msm_warning (_("Failed to stat new session file descriptor (%s)\n"), + g_strerror (errno)); + } + else + { + if (sb.st_size == 0) + file_empty = TRUE; + } + + if (file_empty) + parse_file = g_strconcat (MSM_PKGDATADIR, "/", session->filename, NULL); + else + parse_file = g_strdup (session->full_filename); + + /* FIXME do the parsing */ + + g_free (parse_file); + + return TRUE; +} diff --git a/src/msm/session.h b/src/msm/session.h new file mode 100644 index 000000000..815474417 --- /dev/null +++ b/src/msm/session.h @@ -0,0 +1,42 @@ +/* msm session */ + +/* + * Copyright (C) 2001 Havoc Pennington + * + * 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 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef MSM_SESSION_H +#define MSM_SESSION_H + +#include "client.h" + +typedef struct _MsmSession MsmSession; + +MsmSession* msm_session_get (const char *name); +MsmSession* msm_session_get_failsafe (void); +void msm_session_save (MsmSession *session); +void msm_session_update_client (MsmSession *session, + MsmClient *client); +void msm_session_remove_client (MsmSession *session, + MsmClient *client); +void msm_session_launch (MsmSession *session); +gboolean msm_session_client_id_known (MsmSession *session, + const char *previous_id); + + +#endif + diff --git a/src/msm/util.c b/src/msm/util.c index 6daa3ee8b..34144d760 100644 --- a/src/msm/util.c +++ b/src/msm/util.c @@ -20,6 +20,7 @@ */ #include "util.h" +#include void msm_fatal (const char *format, ...) @@ -65,4 +66,111 @@ msm_warning (const char *format, ...) exit (1); } + +gboolean +msm_create_dir_and_parents (const char *dir, + int mode, + GError **error) +{ + char *parent; + GSList *parents; + GSList *tmp; + /* This function is crap; GNU fileutils has some really + * robust code that does this. + */ + + parents = NULL; + parent = g_path_get_dirname (dir); + while (parent && parent[0] && + strcmp (parent, ".") != 0 && + strcmp (parent, "..") != 0 && + strcmp (parent, "/") != 0 && + /* an optimization since we will normally be using a homedir */ + strcmp (parent, g_get_home_dir ()) != 0) + { + parents = g_slist_prepend (parents, parent); + parent = g_path_get_dirname (parent); + } + + /* Errors are a bit tricky; if we can't create /foo because + * we lack write perms, and can't create /foo/bar because it exists, + * but can create /foo/bar/baz, then it's not really an error. + * + * We more or less punt, and just display an error for the last mkdir. + */ + tmp = parents; + while (tmp != NULL) + { + mkdir (tmp->data, mode); + + g_free (tmp->data); + + tmp = tmp->next; + } + + g_slist_free (parents); + + if (mkdir (dir, mode) < 0) + { + if (errno != EEXIST) + { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + _("Failed to create directory '%s': %s\n"), + dir, g_strerror (errno)); + return FALSE; + } + } + + return TRUE; +} + +const char* +msm_get_work_directory (void) +{ + static char *dir = NULL; + + if (dir == NULL) + { + dir = g_getenv ("SM_SAVE_DIR"); + if (dir == NULL) + dir = g_strconcat (g_get_home_dir (), "/.msm", NULL); + } + + /* ignore errors here, we'll catch them later when we + * try to use the dir + */ + msm_create_dir_and_parents (dir, 0700, NULL); + + return dir; +} + +char* +msm_non_glib_strdup (const char *str) +{ + char *new_str; + + if (str) + { + new_str = msm_non_glib_malloc (strlen (str) + 1); + strcpy (new_str, str); + } + else + new_str = NULL; + + return new_str; +} + +void* +msm_non_glib_malloc (int bytes) +{ + void *ptr; + + ptr = malloc (bytes); + if (ptr == NULL) + g_error ("Failed to allocate %d bytes\n", bytes); + + return ptr; +} diff --git a/src/msm/util.h b/src/msm/util.h index 9a8d131d3..76704bb7a 100644 --- a/src/msm/util.h +++ b/src/msm/util.h @@ -36,5 +36,13 @@ void msm_fatal (const char *format, void msm_quit (void); +const char* msm_get_work_directory (void); + +char* msm_non_glib_strdup (const char *src); +void* msm_non_glib_malloc (int bytes); + +gboolean msm_create_dir_and_parents (const char *dir, + int mode, + GError **error); #endif