summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@src.gnome.org>2001-09-14 06:30:50 +0000
committerHavoc Pennington <hp@src.gnome.org>2001-09-14 06:30:50 +0000
commitb1c7811e89932f4882e5b0452de096c7f5f32ac1 (patch)
treec9966658d0e26beb76b37bd27e76540454e331df
parent1385d192c512da4d44d84623510f73be8d19b08d (diff)
downloadmetacity-b1c7811e89932f4882e5b0452de096c7f5f32ac1.tar.gz
...
-rw-r--r--src/msm/Makefile.am2
-rw-r--r--src/msm/client.c316
-rw-r--r--src/msm/client.h7
-rw-r--r--src/msm/main.c12
-rw-r--r--src/msm/server.c166
-rw-r--r--src/msm/server.h12
-rw-r--r--src/msm/session.c358
-rw-r--r--src/msm/session.h42
-rw-r--r--src/msm/util.c108
-rw-r--r--src/msm/util.h8
10 files changed, 987 insertions, 44 deletions
diff --git a/src/msm/Makefile.am b/src/msm/Makefile.am
index 53d7c1fe..7d9a7cf9 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 abe9f407..70c02db9 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)
+{
+ 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)
{
- SmsReturnProperties (/* FIXME */);
+ 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 24759698..b542bd9a 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 27f91ef3..4b87b2ac 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 d5de84d2..1370f1e1 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 d9d66558..359c2c62 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 00000000..f204993b
--- /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 00000000..81547441
--- /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 6daa3ee8..34144d76 100644
--- a/src/msm/util.c
+++ b/src/msm/util.c
@@ -20,6 +20,7 @@
*/
#include "util.h"
+#include <unistd.h>
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 9a8d131d..76704bb7 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