summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:55 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:55 +0000
commitc8ddd5d82a72167de6e0a544b55f9b03ab93053e (patch)
tree878ae3ddd67e48798427db3d709940a664801cd0
R6.6 is the Xorg base-lineXORG-MAIN
-rw-r--r--XSm.ad119
-rw-r--r--auth.c254
-rw-r--r--auth.h29
-rw-r--r--choose.c841
-rw-r--r--choose.h30
-rw-r--r--globals.c88
-rw-r--r--info.c1050
-rw-r--r--info.h29
-rw-r--r--list.c170
-rw-r--r--list.h42
-rw-r--r--lock.c176
-rw-r--r--lock.h31
-rw-r--r--log.c167
-rw-r--r--log.h29
-rw-r--r--mainwin.c135
-rw-r--r--mainwin.h27
-rw-r--r--misc.c157
-rw-r--r--popup.c184
-rw-r--r--popup.h31
-rw-r--r--printhex.c81
-rw-r--r--prop.c420
-rw-r--r--prop.h34
-rw-r--r--remote.c259
-rw-r--r--restart.c624
-rw-r--r--restart.h30
-rw-r--r--save.c1529
-rw-r--r--save.h35
-rw-r--r--saveutil.c557
-rw-r--r--saveutil.h29
-rw-r--r--signals.c251
-rw-r--r--system.xsm4
-rw-r--r--xsm.c1397
-rw-r--r--xsm.h194
-rw-r--r--xsm.man343
-rw-r--r--xtwatch.c124
-rw-r--r--xtwatch.h27
36 files changed, 9527 insertions, 0 deletions
diff --git a/XSm.ad b/XSm.ad
new file mode 100644
index 0000000..5129722
--- /dev/null
+++ b/XSm.ad
@@ -0,0 +1,119 @@
+! $Xorg: XSm.ad,v 1.3 2000/08/17 19:55:04 cpqbld Exp $
+*chooseSessionPopup*font: 12x24
+*chooseSessionLabel.label: Session Menu
+*chooseSessionMessageLabel.label: Press button again to confirm, or hit Cancel...
+*chooseSessionLoadButton.label: Load Session
+*chooseSessionDeleteButton.label: Delete Session
+*chooseSessionFailSafeButton.label: Default/Fail Safe
+*chooseSessionCancelButton.label: Cancel
+*chooseSessionBreakLockButton.label: Break Lock
+*chooseSessionLoadButton.background: light steel blue
+*chooseSessionDeleteButton.background: light steel blue
+*chooseSessionBreakLockButton.background: light steel blue
+
+*chooseSessionFailSafeButton.background:light steel blue
+*chooseSessionCancelButton.background: light steel blue
+
+*clientInfoButton.label: Client List
+*logButton.label: Session Log
+*checkPointButton.label: Checkpoint
+*shutdownButton.label: Shutdown
+*shutdownSave.label: With Checkpoint
+*shutdownDontSave.label: Immediately
+
+*logPopup.title: Session Log
+*logPopup.iconName: Session Log
+*logOkButton.label: OK
+*logText.width: 600
+*logText.height: 300
+
+*clientInfoPopup.title: Client List
+*clientInfoPopup.iconName: Client List
+*noClientsLabel.label: There are no clients in the session
+*viewPropButton.label: View Properties
+*cloneButton.label: Clone
+*killClientButton.label: Kill
+*restartHintButton.label: Restart Hint
+*restartIfRunning.label: If Running
+*restartAnyway.label: Anyway
+*restartImmediately.label: Immediately
+*restartNever.label: Never
+
+*clientInfoDoneButton.label: Done
+*manualRestartLabel.label: Restart the following non-session-aware clients...
+
+*clientPropTextWidget.width: 500
+*clientPropTextWidget.height: 300
+*clientPropDoneButton.label: Done
+
+*saveMessageLabel.label: Session name
+
+*saveTypeLabel.label: Save Type
+*saveTypeNone.label: None
+*saveTypeLocal.label: Local
+*saveTypeGlobal.label: Global
+*saveTypeBoth.label: Both
+
+*interactStyleLabel.label: Interact Style
+*interactStyleNone.label: None
+*interactStyleErrors.label: Errors
+*interactStyleAny.label: Any
+
+*saveCancelButton.label: Cancel
+
+*helpSaveButton.label: Help
+*helpSaveOkButton.label: OK
+
+*helpSaveText.label:\n\
+Save types\n\
+----------\n\
+Local - Applications should save enough information to\n\
+ restore the state as seen by the user.\n\
+ The save should not affect data seen by other users.\n\
+\n\
+Global - Applications should commit all of their data to\n\
+ permanent, globally accessible storage.\n\
+\n\
+Both - Applications should commit their data to global\n\
+ storage and also save state local to the user.\n\
+\n\n\
+Interaction styles\n\
+------------------\n\
+None - Don't allow user interaction\n\
+Errors - Allow user interaction only if an error occurs\n\
+Any - Allow user interaction for any reason\n\
+\n
+
+*nameInUsePopup.title: Warning
+*nameInUseOverwriteButton.label: Overwrite
+
+*badSavePopup.title: Save Failure
+*badSaveLabel.label: The following applications did not report\n\
+a successful save of their state:
+*badSaveOkButton.label: OK
+*badSaveCancelButton.label: Cancel Shutdown
+
+*chooseSessionListWidget.Translations: #override\n\
+ <Key>Up: ChooseSessionUp()\n\
+ <Key>KP_Up: ChooseSessionUp()\n\
+ Ctrl<Key>P: ChooseSessionUp()\n\
+ <Key>Down: ChooseSessionDown()\n\
+ <Key>KP_Down: ChooseSessionDown()\n\
+ Ctrl<Key>N: ChooseSessionDown()\n\
+ <Btn1Down>: Set() ChooseSessionBtn1Down()\n
+
+*chooseSessionLoadButton.Accelerators: #override\n\
+ <Btn1Down>(2+): set() notify() unset()\n\
+ <Key>Return: set() notify() unset()\n\
+ <Key>KP_Enter: set() notify() unset()\n
+
+*checkPointButton.Translations: #override\n\
+ <Btn1Up>: notify() reset()\n
+*shutdownButton.Translations: #override\n\
+ <Btn1Up>: notify() reset()\n
+
+*saveOkButton.Accelerators: #override\n\
+ <Key>Return: set() notify() unset()\n
+
+*badSaveOkButton.Accelerators: #override\n\
+ <Key>Return: set() notify() unset()\n
diff --git a/auth.c b/auth.c
new file mode 100644
index 0000000..beb9b77
--- /dev/null
+++ b/auth.c
@@ -0,0 +1,254 @@
+/* $Xorg: auth.c,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+
+#include <X11/ICE/ICEutil.h>
+
+static char *addAuthFile = NULL;
+static char *remAuthFile = NULL;
+
+
+
+/*
+ * Host Based Authentication Callback. This callback is invoked if
+ * the connecting client can't offer any authentication methods that
+ * we can accept. We can accept/reject based on the hostname.
+ */
+
+Bool
+HostBasedAuthProc (hostname)
+
+char *hostname;
+
+{
+ return (0); /* For now, we don't support host based authentication */
+}
+
+
+
+/*
+ * We use temporary files which contain commands to add/remove entries from
+ * the .ICEauthority file.
+ */
+
+static void
+write_iceauth (addfp, removefp, entry)
+
+FILE *addfp;
+FILE *removefp;
+IceAuthDataEntry *entry;
+
+{
+ fprintf (addfp,
+ "add %s \"\" %s %s ",
+ entry->protocol_name,
+ entry->network_id,
+ entry->auth_name);
+ fprintfhex (addfp, entry->auth_data_length, entry->auth_data);
+ fprintf (addfp, "\n");
+
+ fprintf (removefp,
+ "remove protoname=%s protodata=\"\" netid=%s authname=%s\n",
+ entry->protocol_name,
+ entry->network_id,
+ entry->auth_name);
+}
+
+
+
+static char *
+unique_filename (path, prefix)
+
+char *path;
+char *prefix;
+
+{
+#ifndef X_NOT_POSIX
+ return ((char *) tempnam (path, prefix));
+#else
+ char tempFile[PATH_MAX];
+ char *tmp;
+
+ sprintf (tempFile, "%s/%sXXXXXX", path, prefix);
+ tmp = (char *) mktemp (tempFile);
+ if (tmp)
+ {
+ char *ptr = (char *) malloc (strlen (tmp) + 1);
+ strcpy (ptr, tmp);
+ return (ptr);
+ }
+ else
+ return (NULL);
+#endif
+}
+
+
+
+
+/*
+ * Provide authentication data to clients that wish to connect
+ */
+
+#define MAGIC_COOKIE_LEN 16
+
+Status
+SetAuthentication (count, listenObjs, authDataEntries)
+
+int count;
+IceListenObj *listenObjs;
+IceAuthDataEntry **authDataEntries;
+
+{
+ FILE *addfp = NULL;
+ FILE *removefp = NULL;
+ char *path;
+ int original_umask;
+ char command[256];
+ int i;
+
+ original_umask = umask (0077); /* disallow non-owner access */
+
+ path = (char *) getenv ("SM_SAVE_DIR");
+ if (!path)
+ {
+ path = (char *) getenv ("HOME");
+ if (!path)
+ path = ".";
+ }
+
+ if ((addAuthFile = unique_filename (path, ".xsm")) == NULL)
+ goto bad;
+
+ if (!(addfp = fopen (addAuthFile, "w")))
+ goto bad;
+
+ if ((remAuthFile = unique_filename (path, ".xsm")) == NULL)
+ goto bad;
+
+ if (!(removefp = fopen (remAuthFile, "w")))
+ goto bad;
+
+ if ((*authDataEntries = (IceAuthDataEntry *) XtMalloc (
+ count * 2 * sizeof (IceAuthDataEntry))) == NULL)
+ goto bad;
+
+ for (i = 0; i < count * 2; i += 2)
+ {
+ (*authDataEntries)[i].network_id =
+ IceGetListenConnectionString (listenObjs[i/2]);
+ (*authDataEntries)[i].protocol_name = "ICE";
+ (*authDataEntries)[i].auth_name = "MIT-MAGIC-COOKIE-1";
+
+ (*authDataEntries)[i].auth_data =
+ IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
+ (*authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN;
+
+ (*authDataEntries)[i+1].network_id =
+ IceGetListenConnectionString (listenObjs[i/2]);
+ (*authDataEntries)[i+1].protocol_name = "XSMP";
+ (*authDataEntries)[i+1].auth_name = "MIT-MAGIC-COOKIE-1";
+
+ (*authDataEntries)[i+1].auth_data =
+ IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
+ (*authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN;
+
+ write_iceauth (addfp, removefp, &(*authDataEntries)[i]);
+ write_iceauth (addfp, removefp, &(*authDataEntries)[i+1]);
+
+ IceSetPaAuthData (2, &(*authDataEntries)[i]);
+
+ IceSetHostBasedAuthProc (listenObjs[i/2], HostBasedAuthProc);
+ }
+
+ fclose (addfp);
+ fclose (removefp);
+
+ umask (original_umask);
+
+ sprintf (command, "iceauth source %s", addAuthFile);
+ execute_system_command (command);
+
+ unlink (addAuthFile);
+
+ return (1);
+
+ bad:
+
+ if (addfp)
+ fclose (addfp);
+
+ if (removefp)
+ fclose (removefp);
+
+ if (addAuthFile)
+ {
+ unlink (addAuthFile);
+ free (addAuthFile);
+ }
+ if (remAuthFile)
+ {
+ unlink (remAuthFile);
+ free (remAuthFile);
+ }
+
+ return (0);
+}
+
+
+
+/*
+ * Free up authentication data.
+ */
+
+void
+FreeAuthenticationData (count, authDataEntries)
+
+int count;
+IceAuthDataEntry *authDataEntries;
+
+{
+ /* Each transport has entries for ICE and XSMP */
+
+ char command[256];
+ int i;
+
+ for (i = 0; i < count * 2; i++)
+ {
+ free (authDataEntries[i].network_id);
+ free (authDataEntries[i].auth_data);
+ }
+
+ XtFree ((char *) authDataEntries);
+
+ sprintf (command, "iceauth source %s", remAuthFile);
+ execute_system_command (command);
+
+ unlink (remAuthFile);
+
+ free (addAuthFile);
+ free (remAuthFile);
+}
diff --git a/auth.h b/auth.h
new file mode 100644
index 0000000..65149b9
--- /dev/null
+++ b/auth.h
@@ -0,0 +1,29 @@
+/* $Xorg: auth.h,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern Status SetAuthentication ();
+extern void FreeAuthenticationData ();
+extern Bool HostBasedAuthProc ();
diff --git a/choose.c b/choose.c
new file mode 100644
index 0000000..4f1255c
--- /dev/null
+++ b/choose.c
@@ -0,0 +1,841 @@
+/* $Xorg: choose.c,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include "saveutil.h"
+#include "lock.h"
+#include <sys/types.h>
+
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Command.h>
+
+#ifndef X_NOT_POSIX
+#include <dirent.h>
+#else
+#ifdef SYSV
+#include <dirent.h>
+#else
+#ifdef USG
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#ifndef dirent
+#define dirent direct
+#endif
+#endif
+#endif
+#endif
+
+static Pixel save_message_foreground;
+static Pixel save_message_background;
+
+static int delete_session_phase = 0;
+static int break_lock_phase = 0;
+
+Widget chooseSessionPopup;
+Widget chooseSessionForm;
+Widget chooseSessionLabel;
+Widget chooseSessionListWidget;
+Widget chooseSessionMessageLabel;
+Widget chooseSessionLoadButton;
+Widget chooseSessionDeleteButton;
+Widget chooseSessionBreakLockButton;
+Widget chooseSessionFailSafeButton;
+Widget chooseSessionCancelButton;
+
+
+
+int
+GetSessionNames (count_ret, short_names_ret, long_names_ret, locked_ret)
+
+int *count_ret;
+String **short_names_ret;
+String **long_names_ret;
+Bool **locked_ret;
+
+{
+ DIR *dir;
+ struct dirent *entry;
+ char *path;
+ int count;
+
+ path = (char *) getenv ("SM_SAVE_DIR");
+ if (!path)
+ {
+ path = (char *) getenv ("HOME");
+ if (!path)
+ path = ".";
+ }
+
+ *count_ret = 0;
+ *short_names_ret = NULL;
+ *locked_ret = NULL;
+ if (long_names_ret)
+ *long_names_ret = NULL;
+
+ if ((dir = opendir (path)) == NULL)
+ return 0;
+
+ count = 0;
+
+ while ((entry = readdir (dir)) != NULL)
+ {
+ if (strncmp (entry->d_name, ".XSM-", 5) == 0)
+ count++;
+ }
+
+ if (count == 0 ||
+ ((*short_names_ret = (String *) XtMalloc (
+ count * sizeof (String))) == NULL) ||
+ (long_names_ret && (*long_names_ret =
+ (String *) XtMalloc (count * sizeof (String))) == NULL) ||
+ ((*locked_ret = (Bool *) XtMalloc (count * sizeof (Bool))) == NULL))
+ {
+ closedir (dir);
+ if (*short_names_ret)
+ XtFree ((char *) *short_names_ret);
+ if (long_names_ret && *long_names_ret)
+ XtFree ((char *) *long_names_ret);
+ return 0;
+ }
+
+ rewinddir (dir);
+
+ while ((entry = readdir (dir)) != NULL && *count_ret < count)
+ {
+ if (strncmp (entry->d_name, ".XSM-", 5) == 0)
+ {
+ char *name = (char *) entry->d_name + 5;
+ char *id = NULL;
+ Bool locked = CheckSessionLocked (name, long_names_ret, &id);
+
+ (*short_names_ret)[*count_ret] = XtNewString (name);
+ (*locked_ret)[*count_ret] = locked;
+
+ if (long_names_ret)
+ {
+ if (!locked)
+ {
+ (*long_names_ret)[*count_ret] =
+ (*short_names_ret)[*count_ret];
+ }
+ else
+ {
+ char *host = ((char *) strchr (id, '/')) + 1;
+ char *colon = (char *) strchr (host, ':');
+
+ *colon = '\0';
+
+ (*long_names_ret)[*count_ret] =
+ XtMalloc (strlen (name) + strlen (host) + 14);
+
+ sprintf ((*long_names_ret)[*count_ret],
+ "%s (locked at %s)", name, host);
+ *colon = ':';
+
+ XtFree (id);
+ }
+ }
+
+ (*count_ret)++;
+ }
+ }
+
+ closedir (dir);
+
+ return 1;
+}
+
+
+
+void
+FreeSessionNames (count, namesShort, namesLong, lockFlags)
+
+int count;
+String *namesShort;
+String *namesLong;
+Bool *lockFlags;
+
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ XtFree ((char *) namesShort[i]);
+ XtFree ((char *) namesShort);
+
+ if (namesLong)
+ {
+ for (i = 0; i < count; i++)
+ if (lockFlags[i])
+ XtFree ((char *) namesLong[i]);
+ XtFree ((char *) namesLong);
+ }
+
+ XtFree ((char *) lockFlags);
+}
+
+
+
+static void
+SessionSelected (number, highlight)
+
+int number;
+Bool highlight;
+
+{
+ if (number >= 0)
+ {
+ Bool locked = sessionsLocked[number];
+
+ if (highlight)
+ XawListHighlight (chooseSessionListWidget, number);
+
+ XtSetSensitive (chooseSessionLoadButton, !locked);
+ XtSetSensitive (chooseSessionDeleteButton, !locked);
+ XtSetSensitive (chooseSessionBreakLockButton, locked);
+ }
+ else
+ {
+ XtSetSensitive (chooseSessionLoadButton, False);
+ XtSetSensitive (chooseSessionDeleteButton, False);
+ XtSetSensitive (chooseSessionBreakLockButton, False);
+ }
+}
+
+
+
+static void
+AddSessionNames (count, names)
+
+int count;
+String *names;
+
+{
+ int i;
+
+ XawListChange (chooseSessionListWidget, names, count, 0, True);
+
+ /*
+ * Highlight the first unlocked session, if any.
+ */
+
+ for (i = 0; i < sessionNameCount; i++)
+ if (!sessionsLocked[i])
+ break;
+
+ SessionSelected (i < sessionNameCount ? i : -1, True);
+}
+
+
+
+void
+ChooseWindowStructureNotifyXtHandler (w, closure, event, continue_to_dispatch)
+
+Widget w;
+XtPointer closure;
+XEvent *event;
+Boolean *continue_to_dispatch;
+
+{
+ if (event->type == MapNotify)
+ {
+ /*
+ * Set the input focus to the choose window and direct all keyboard
+ * events to the list widget. This way, the user can make selections
+ * using the keyboard.
+ */
+
+ XtSetKeyboardFocus (chooseSessionPopup, chooseSessionListWidget);
+
+ XSetInputFocus (XtDisplay (topLevel), XtWindow (chooseSessionPopup),
+ RevertToPointerRoot, CurrentTime);
+
+ XSync (XtDisplay (topLevel), 0);
+
+ XtRemoveEventHandler (chooseSessionPopup, StructureNotifyMask, False,
+ ChooseWindowStructureNotifyXtHandler, NULL);
+ }
+}
+
+
+void
+ChooseSession ()
+
+{
+ Dimension width, height;
+ Position x, y;
+
+
+ /*
+ * Add the session names to the list
+ */
+
+ AddSessionNames (sessionNameCount, sessionNamesLong);
+
+
+ /*
+ * Center popup containing choice of sessions
+ */
+
+ XtRealizeWidget (chooseSessionPopup);
+
+ XtVaGetValues (chooseSessionPopup,
+ XtNwidth, &width,
+ XtNheight, &height,
+ NULL);
+
+ x = (Position)(WidthOfScreen (XtScreen (topLevel)) - width) / 2;
+ y = (Position)(HeightOfScreen (XtScreen (topLevel)) - height) / 3;
+
+ XtVaSetValues (chooseSessionPopup,
+ XtNx, x,
+ XtNy, y,
+ NULL);
+
+ XtVaSetValues (chooseSessionListWidget,
+ XtNlongest, width,
+ NULL);
+
+ XtVaSetValues (chooseSessionLabel,
+ XtNwidth, width,
+ NULL);
+
+ XtVaGetValues (chooseSessionMessageLabel,
+ XtNforeground, &save_message_foreground,
+ XtNbackground, &save_message_background,
+ NULL);
+
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNwidth, width,
+ XtNforeground, save_message_background,
+ NULL);
+
+ /*
+ * Wait for a map notify on the popup, then set input focus.
+ */
+
+ XtAddEventHandler (chooseSessionPopup, StructureNotifyMask, False,
+ ChooseWindowStructureNotifyXtHandler, NULL);
+
+ XtPopup (chooseSessionPopup, XtGrabNone);
+}
+
+
+
+static void
+CheckDeleteCancel ()
+
+{
+ if (delete_session_phase > 0)
+ {
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_background,
+ NULL);
+
+ delete_session_phase = 0;
+ }
+}
+
+
+static void
+CheckBreakLockCancel ()
+
+{
+ if (break_lock_phase > 0)
+ {
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_background,
+ NULL);
+
+ break_lock_phase = 0;
+ }
+}
+
+
+
+static void
+ChooseSessionUp (w, event, params, numParams)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *numParams;
+
+{
+ XawListReturnStruct *current;
+
+ CheckDeleteCancel ();
+ CheckBreakLockCancel ();
+
+ current = XawListShowCurrent (chooseSessionListWidget);
+ if (current->list_index > 0)
+ SessionSelected (current->list_index - 1, True);
+ XtFree ((char *) current);
+}
+
+
+static void
+ChooseSessionDown (w, event, params, numParams)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *numParams;
+
+{
+ XawListReturnStruct *current;
+
+ CheckDeleteCancel ();
+ CheckBreakLockCancel ();
+
+ current = XawListShowCurrent (chooseSessionListWidget);
+ if (current->list_index < sessionNameCount - 1)
+ SessionSelected (current->list_index + 1, True);
+ XtFree ((char *) current);
+}
+
+
+
+static void
+ChooseSessionBtn1Down (w, event, params, numParams)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *numParams;
+
+{
+ XawListReturnStruct *current;
+
+ CheckDeleteCancel ();
+ CheckBreakLockCancel ();
+
+ current = XawListShowCurrent (chooseSessionListWidget);
+ SessionSelected (current->list_index, False /* already highlighted */);
+ XtFree ((char *) current);
+}
+
+
+
+static void
+ChooseSessionLoadXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XawListReturnStruct *current;
+
+ CheckDeleteCancel ();
+ CheckBreakLockCancel ();
+
+ current = XawListShowCurrent (chooseSessionListWidget);
+
+ if (!current || !current->string || *(current->string) == '\0')
+ {
+ if (current)
+ XtFree ((char *) current);
+#ifdef XKB
+ XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+ return;
+ }
+
+ /*
+ * Pop down choice of sessions and start the specified session.
+ */
+
+ XtPopdown (chooseSessionPopup);
+
+ if (session_name)
+ XtFree (session_name);
+
+ session_name = XtNewString (current->string);
+
+ XtFree ((char *) current);
+
+ FreeSessionNames (sessionNameCount,
+ sessionNamesShort, sessionNamesLong, sessionsLocked);
+
+
+ /*
+ * Start the session, looking for .XSM-<session name> startup file.
+ */
+
+ if (!StartSession (session_name, False))
+ UnableToLockSession (session_name);
+}
+
+
+
+static void
+ChooseSessionDeleteXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XawListReturnStruct *current;
+ int longest;
+ char *name;
+
+ CheckBreakLockCancel ();
+
+ current = XawListShowCurrent (chooseSessionListWidget);
+
+ if (!current || !(name = current->string) || *name == '\0')
+ {
+ if (current)
+ XtFree ((char *) current);
+#ifdef XKB
+ XkbStdBell(XtDisplay(w),XtWindow(w),0,XkbBI_BadValue);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+ return;
+ }
+
+ delete_session_phase++;
+
+ if (delete_session_phase == 1)
+ {
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_foreground,
+ NULL);
+
+#ifdef XKB
+ XkbStdBell(XtDisplay(w),XtWindow(w),0,XkbBI_BadValue);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+ }
+ else
+ {
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_background,
+ NULL);
+
+ if (DeleteSession (name))
+ {
+ int i, j;
+
+ for (i = 0; i < sessionNameCount; i++)
+ {
+ if (strcmp (sessionNamesLong[i], name) == 0)
+ {
+ XtFree ((char *) sessionNamesShort[i]);
+
+ if (sessionsLocked[i])
+ XtFree ((char *) sessionNamesLong[i]);
+
+ for (j = i; j < sessionNameCount - 1; j++)
+ {
+ sessionNamesLong[j] = sessionNamesLong[j + 1];
+ sessionNamesShort[j] = sessionNamesShort[j + 1];
+ sessionsLocked[j] = sessionsLocked[j + 1];
+ }
+ sessionNameCount--;
+ break;
+ }
+ }
+
+ if (sessionNameCount == 0)
+ {
+ XtSetSensitive (chooseSessionLoadButton, 0);
+ XtSetSensitive (chooseSessionDeleteButton, 0);
+ XtUnmanageChild (chooseSessionListWidget);
+ }
+ else
+ {
+ XtVaGetValues (chooseSessionListWidget,
+ XtNlongest, &longest,
+ NULL);
+
+ XawListChange (chooseSessionListWidget,
+ sessionNamesLong, sessionNameCount, longest, True);
+
+ SessionSelected (-1, False);
+ }
+ }
+
+ delete_session_phase = 0;
+ }
+
+ XtFree ((char *) current);
+}
+
+
+
+static void
+ChooseSessionBreakLockXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XawListReturnStruct *current;
+ char *name;
+
+ CheckDeleteCancel ();
+
+ current = XawListShowCurrent (chooseSessionListWidget);
+
+ if (!current || !(name = current->string) || *name == '\0')
+ {
+ if (current)
+ XtFree ((char *) current);
+#ifdef XKB
+ XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+ return;
+ }
+
+ break_lock_phase++;
+
+ if (break_lock_phase == 1)
+ {
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_foreground,
+ NULL);
+
+#ifdef XKB
+ XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+ }
+ else
+ {
+ char *id;
+ int longest;
+
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_background,
+ NULL);
+
+ name = sessionNamesShort[current->list_index];
+
+ id = GetLockId (name);
+
+ UnlockSession (name);
+
+ sessionsLocked[current->list_index] = False;
+ XtFree ((char *) sessionNamesLong[current->list_index]);
+ sessionNamesLong[current->list_index] =
+ sessionNamesShort[current->list_index];
+
+ XtVaGetValues (chooseSessionListWidget,
+ XtNlongest, &longest,
+ NULL);
+
+ XawListChange (chooseSessionListWidget,
+ sessionNamesLong, sessionNameCount, longest, True);
+
+ SessionSelected (current->list_index, True);
+
+ break_lock_phase = 0;
+ }
+
+ XtFree ((char *) current);
+}
+
+
+
+static void
+ChooseSessionFailSafeXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ /*
+ * Pop down choice of sessions, and start the fail safe session.
+ */
+
+ CheckDeleteCancel ();
+ CheckBreakLockCancel ();
+
+ XtPopdown (chooseSessionPopup);
+
+ if (session_name)
+ XtFree (session_name);
+
+ session_name = XtNewString (FAILSAFE_SESSION_NAME);
+
+ FreeSessionNames (sessionNameCount,
+ sessionNamesShort, sessionNamesLong, sessionsLocked);
+
+
+ /*
+ * We don't need to check return value of StartSession in this case,
+ * because we are using the default session, and StartSession will
+ * not try to lock the session at this time. It will try to lock
+ * it as soon as the user gives the session a name.
+ */
+
+ StartSession (session_name,
+ True /* Use ~/.xsmstartup if found, else system.xsm */);
+}
+
+
+
+static void
+ChooseSessionCancelXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ if (delete_session_phase > 0 || break_lock_phase > 0)
+ {
+ XtVaSetValues (chooseSessionMessageLabel,
+ XtNforeground, save_message_background,
+ NULL);
+
+ delete_session_phase = 0;
+ break_lock_phase = 0;
+ }
+ else
+ EndSession (2);
+}
+
+
+
+void
+create_choose_session_popup ()
+
+{
+ static XtActionsRec choose_actions[] = {
+ {"ChooseSessionUp", ChooseSessionUp},
+ {"ChooseSessionDown", ChooseSessionDown},
+ {"ChooseSessionBtn1Down", ChooseSessionBtn1Down}
+ };
+
+ /*
+ * Pop up for choosing session at startup
+ */
+
+ chooseSessionPopup = XtVaCreatePopupShell (
+ "chooseSessionPopup", transientShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+
+ chooseSessionForm = XtVaCreateManagedWidget (
+ "chooseSessionForm", formWidgetClass, chooseSessionPopup,
+ NULL);
+
+
+ chooseSessionLabel = XtVaCreateManagedWidget (
+ "chooseSessionLabel", labelWidgetClass, chooseSessionForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNborderWidth, 0,
+ XtNresizable, True,
+ XtNjustify, XtJustifyCenter,
+ NULL);
+
+ chooseSessionListWidget = XtVaCreateManagedWidget (
+ "chooseSessionListWidget", listWidgetClass, chooseSessionForm,
+ XtNresizable, True,
+ XtNdefaultColumns, 1,
+ XtNforceColumns, True,
+ XtNfromHoriz, NULL,
+ XtNfromVert, chooseSessionLabel,
+ XtNvertDistance, 25,
+ NULL);
+
+ chooseSessionMessageLabel = XtVaCreateManagedWidget (
+ "chooseSessionMessageLabel", labelWidgetClass, chooseSessionForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, chooseSessionListWidget,
+ XtNborderWidth, 0,
+ XtNresizable, True,
+ XtNjustify, XtJustifyCenter,
+ NULL);
+
+ chooseSessionLoadButton = XtVaCreateManagedWidget (
+ "chooseSessionLoadButton", commandWidgetClass, chooseSessionForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, chooseSessionMessageLabel,
+ NULL);
+
+ XtAddCallback (chooseSessionLoadButton, XtNcallback,
+ ChooseSessionLoadXtProc, 0);
+
+ chooseSessionDeleteButton = XtVaCreateManagedWidget (
+ "chooseSessionDeleteButton", commandWidgetClass, chooseSessionForm,
+ XtNfromHoriz, chooseSessionLoadButton,
+ XtNfromVert, chooseSessionMessageLabel,
+ NULL);
+
+ XtAddCallback (chooseSessionDeleteButton, XtNcallback,
+ ChooseSessionDeleteXtProc, 0);
+
+ chooseSessionBreakLockButton = XtVaCreateManagedWidget (
+ "chooseSessionBreakLockButton",
+ commandWidgetClass, chooseSessionForm,
+ XtNfromHoriz, chooseSessionDeleteButton,
+ XtNfromVert, chooseSessionMessageLabel,
+ NULL);
+
+ XtAddCallback (chooseSessionBreakLockButton, XtNcallback,
+ ChooseSessionBreakLockXtProc, 0);
+
+ chooseSessionFailSafeButton = XtVaCreateManagedWidget (
+ "chooseSessionFailSafeButton", commandWidgetClass, chooseSessionForm,
+ XtNfromHoriz, chooseSessionBreakLockButton,
+ XtNfromVert, chooseSessionMessageLabel,
+ NULL);
+
+ XtAddCallback (chooseSessionFailSafeButton, XtNcallback,
+ ChooseSessionFailSafeXtProc, 0);
+
+
+ chooseSessionCancelButton = XtVaCreateManagedWidget (
+ "chooseSessionCancelButton", commandWidgetClass, chooseSessionForm,
+ XtNfromHoriz, chooseSessionFailSafeButton,
+ XtNfromVert, chooseSessionMessageLabel,
+ NULL);
+
+ XtAddCallback (chooseSessionCancelButton, XtNcallback,
+ ChooseSessionCancelXtProc, 0);
+
+ XtAppAddActions (appContext, choose_actions, XtNumber (choose_actions));
+
+ XtInstallAllAccelerators (chooseSessionListWidget, chooseSessionPopup);
+}
diff --git a/choose.h b/choose.h
new file mode 100644
index 0000000..6816839
--- /dev/null
+++ b/choose.h
@@ -0,0 +1,30 @@
+/* $Xorg: choose.h,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void create_choose_session_popup ();
+extern int GetSessionNames ();
+extern void FreeSessionNames ();
+extern void ChooseSession ();
diff --git a/globals.c b/globals.c
new file mode 100644
index 0000000..4c6d1cd
--- /dev/null
+++ b/globals.c
@@ -0,0 +1,88 @@
+/* $Xorg: globals.c,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include <X11/ICE/ICEutil.h>
+
+int Argc;
+char **Argv;
+
+List *RunningList;
+List *PendingList;
+List *RestartAnywayList;
+List *RestartImmedList;
+
+List *WaitForSaveDoneList;
+List *FailedSaveList;
+List *WaitForInteractList;
+List *WaitForPhase2List;
+
+Bool wantShutdown = False;
+Bool shutdownInProgress = False;
+Bool phase2InProgress = False;
+Bool saveInProgress = False;
+Bool shutdownCancelled = False;
+
+Bool verbose = False;
+
+char *sm_id = NULL;
+
+char *networkIds = NULL;
+char *session_name = NULL;
+
+IceAuthDataEntry *authDataEntries = NULL;
+int numTransports = 0;
+
+Bool client_info_visible = False;
+Bool client_prop_visible = False;
+Bool client_log_visible = False;
+
+String *clientListNames = NULL;
+ClientRec **clientListRecs = NULL;
+int numClientListNames = 0;
+
+int current_client_selected;
+
+int sessionNameCount = 0;
+String *sessionNamesShort = NULL;
+String *sessionNamesLong = NULL;
+Bool *sessionsLocked = NULL;
+
+int num_clients_in_last_session = -1;
+
+char **non_session_aware_clients = NULL;
+int non_session_aware_count = 0;
+
+char *display_env = NULL, *non_local_display_env = NULL;
+char *session_env = NULL, *non_local_session_env = NULL;
+char *audio_env = NULL;
+
+Bool need_to_name_session = False;
+
+Bool remote_allowed;
+
+XtAppContext appContext;
+Widget topLevel;
+
diff --git a/info.c b/info.c
new file mode 100644
index 0000000..ba79f0d
--- /dev/null
+++ b/info.c
@@ -0,0 +1,1050 @@
+/* $Xorg: info.c,v 1.5 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include "restart.h"
+#include "popup.h"
+
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/AsciiText.h>
+
+static Pixmap checkBitmap;
+
+Widget clientInfoPopup;
+Widget clientInfoForm;
+Widget viewPropButton;
+Widget cloneButton;
+Widget killClientButton;
+Widget clientInfoDoneButton;
+Widget restartHintButton;
+Widget restartHintMenu;
+Widget restartIfRunning;
+Widget restartAnyway;
+Widget restartImmediately;
+Widget restartNever;
+Widget clientListWidget;
+Widget noClientsLabel;
+Widget manualRestartLabel;
+Widget manualRestartCommands;
+
+Widget clientPropPopup;
+Widget clientPropForm;
+Widget clientPropDoneButton;
+Widget clientPropTextWidget;
+
+
+
+void
+ShowHint (client)
+
+ClientRec *client;
+
+{
+ static Widget active = NULL;
+ int hint = client->restartHint;
+
+ if (active)
+ XtVaSetValues (active, XtNleftBitmap, None, NULL);
+
+ if (hint == SmRestartIfRunning)
+ active = restartIfRunning;
+ else if (hint == SmRestartAnyway)
+ active = restartAnyway;
+ else if (hint == SmRestartImmediately)
+ active = restartImmediately;
+ else if (hint == SmRestartNever)
+ active = restartNever;
+
+ XtVaSetValues (active, XtNleftBitmap, checkBitmap, NULL);
+}
+
+
+
+typedef struct {
+ char *bufStart;
+ char *bufPtr;
+ int bufSize;
+ int bytesLeft;
+} Buffer;
+
+#define BUF_START_SIZE 1024
+#define BUF_GROW_SIZE 256
+
+
+static void
+AppendStr (buffer, str)
+
+Buffer *buffer;
+char *str;
+
+{
+ int len = strlen (str);
+
+ if ((buffer->bytesLeft - 1) < len)
+ {
+ int newBufSize = buffer->bufSize + len + BUF_GROW_SIZE;
+ char *newbuf = (char *) malloc (newBufSize);
+ int bytesUsed = buffer->bufPtr - buffer->bufStart;
+ memcpy (newbuf, buffer->bufStart, bytesUsed);
+ newbuf[bytesUsed] = '\0';
+ free (buffer->bufStart);
+ buffer->bufStart = newbuf;
+ buffer->bufPtr = newbuf + bytesUsed;
+ buffer->bufSize = newBufSize;
+ buffer->bytesLeft = newBufSize - bytesUsed;
+ }
+
+ strcat (buffer->bufPtr, str);
+ buffer->bufPtr += len;
+ buffer->bytesLeft -= len;
+}
+
+
+void
+DisplayProps (client)
+
+ClientRec *client;
+
+{
+ int index;
+ List *pl, *pj, *vl;
+ PropValue *pval;
+ Buffer buffer;
+ static int first_time = 1;
+
+ for (index = 0; index < numClientListNames; index++)
+ if (clientListRecs[index] == client)
+ break;
+
+ if (index >= numClientListNames)
+ return;
+
+ buffer.bufStart = buffer.bufPtr = (char *) malloc (BUF_START_SIZE);
+ buffer.bufSize = buffer.bytesLeft = BUF_START_SIZE;
+ buffer.bufStart[0] = '\0';
+
+ if (ListCount (client->props) > 0)
+ {
+ char number[10];
+ char *ptr;
+
+ AppendStr (&buffer, "*** ID = ");
+ AppendStr (&buffer, client->clientId);
+ AppendStr (&buffer, " ***\n\n");
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+
+ AppendStr (&buffer, "Name: ");
+ AppendStr (&buffer, pprop->name);
+ AppendStr (&buffer, "\n");
+ AppendStr (&buffer, "Type: ");
+ AppendStr (&buffer, pprop->type);
+ AppendStr (&buffer, "\n");
+ AppendStr (&buffer, "Num values: ");
+ sprintf (number, "%d", ListCount (pprop->values));
+ AppendStr (&buffer, number);
+ AppendStr (&buffer, "\n");
+
+ if (strcmp (pprop->type, SmCARD8) == 0)
+ {
+ char *card8;
+ int value;
+
+ vl = ListFirst (pprop->values);
+ pval = (PropValue *) vl->thing;
+
+ card8 = pval->value;
+ value = *card8;
+
+ AppendStr (&buffer, "Value 1: ");
+ sprintf (number, "%d", value);
+ AppendStr (&buffer, number);
+
+ if (strcmp (pprop->name, SmRestartStyleHint) == 0)
+ {
+ if (value == SmRestartAnyway)
+ AppendStr (&buffer, " (Restart Anyway)");
+ else if (value == SmRestartImmediately)
+ AppendStr (&buffer, " (Restart Immediately)");
+ else if (value == SmRestartNever)
+ AppendStr (&buffer, " (Restart Never)");
+ else
+ AppendStr (&buffer, " (Restart If Running)");
+ }
+
+ AppendStr (&buffer, "\n");
+ }
+ else
+ {
+ int propnum = 0;
+
+ for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
+ {
+ propnum++;
+
+ pval = (PropValue *) pj->thing;
+ AppendStr (&buffer, "Value ");
+ sprintf (number, "%d", propnum);
+ AppendStr (&buffer, number);
+ AppendStr (&buffer, ": ");
+ AppendStr (&buffer, (char *) pval->value);
+ AppendStr (&buffer, "\n");
+ }
+ }
+
+ AppendStr (&buffer, "\n");
+ }
+
+ XtVaSetValues (clientPropTextWidget,
+ XtNstring, buffer.bufStart,
+ NULL);
+
+ sprintf (buffer.bufStart,
+ "SM Properties : %s", clientListNames[index]);
+
+ ptr = Strstr (buffer.bufStart, ") Restart");
+ if (ptr) *(ptr + 1) = '\0';
+
+ XtVaSetValues (clientPropPopup,
+ XtNtitle, buffer.bufStart,
+ XtNiconName, buffer.bufStart,
+ NULL);
+
+ if (client_prop_visible)
+ {
+ /* Make sure it is visible */
+
+ XMapRaised (XtDisplay (topLevel), XtWindow (clientPropPopup));
+ }
+ else
+ {
+ PopupPopup (mainWindow, clientPropPopup,
+ False, first_time, 50, 150, "DelPropWinAction()");
+
+ client_prop_visible = 1;
+
+ if (first_time)
+ first_time = 0;
+ }
+ }
+
+ free (buffer.bufStart);
+}
+
+
+
+static void
+ClientListXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XawListReturnStruct *current = (XawListReturnStruct *) callData;
+ ClientRec *client;
+
+ if (!current || current->list_index < 0)
+ return;
+
+ client = clientListRecs[current->list_index];
+ ShowHint (client);
+ current_client_selected = current->list_index;
+ if (client_prop_visible)
+ DisplayProps (client);
+}
+
+
+
+
+static void
+ViewPropXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ ClientRec *client;
+ XawListReturnStruct *current;
+
+ current = XawListShowCurrent (clientListWidget);
+
+ if (!current || current->list_index < 0)
+ {
+ if (current)
+ XtFree ((char *) current);
+ return;
+ }
+
+ client = clientListRecs[current->list_index];
+ DisplayProps (client);
+ XtFree ((char *) current);
+}
+
+
+
+static void
+CloneXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ ClientRec *client;
+ XawListReturnStruct *current;
+
+ current = XawListShowCurrent (clientListWidget);
+
+ if (!current || current->list_index < 0)
+ {
+ if (current)
+ XtFree ((char *) current);
+ return;
+ }
+
+ client = clientListRecs[current->list_index];
+
+ if (client)
+ Clone (client, False /* don't use saved state */);
+
+ XtFree ((char *) current);
+}
+
+
+
+static void
+KillClientXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ ClientRec *client;
+ XawListReturnStruct *current;
+
+ current = XawListShowCurrent (clientListWidget);
+
+ if (!current || current->list_index < 0)
+ {
+ if (current)
+ XtFree ((char *) current);
+ return;
+ }
+
+ client = clientListRecs[current->list_index];
+
+ SmsDie (client->smsConn);
+
+ XtFree ((char *) current);
+}
+
+
+
+static void
+listDoneXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XtPopdown (clientInfoPopup);
+ client_info_visible = 0;
+}
+
+
+
+char *
+GetProgramName (fullname)
+
+char *fullname;
+
+{
+ char *lastSlash = NULL;
+ int i;
+
+ for (i = 0; i < (int) strlen (fullname); i++)
+ if (fullname[i] == '/')
+ lastSlash = &fullname[i];
+
+ if (lastSlash)
+ return (lastSlash + 1);
+ else
+ return (fullname);
+}
+
+
+
+void
+UpdateClientList ()
+
+{
+ ClientRec *client;
+ char *progName, *hostname, *tmp1, *tmp2;
+ String clientInfo;
+ int maxlen1, maxlen2;
+ char extraBuf1[80], extraBuf2[80];
+ char *restart_service_prop;
+ List *cl, *pl;
+ int i, k;
+ static int reenable_asap = 0;
+
+ if (clientListNames)
+ {
+ /*
+ * Free the previous list of names. Xaw doesn't make a copy of
+ * our list, so we need to keep it around.
+ */
+
+ for (i = 0; i < numClientListNames; i++)
+ XtFree (clientListNames[i]);
+
+ XtFree ((char *) clientListNames);
+
+ clientListNames = NULL;
+ }
+
+ if (clientListRecs)
+ {
+ /*
+ * Free the mapping of client names to client records
+ */
+
+ XtFree ((char *) clientListRecs);
+ clientListRecs = NULL;
+ }
+
+ maxlen1 = maxlen2 = 0;
+ numClientListNames = 0;
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ progName = NULL;
+ restart_service_prop = NULL;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+ List *vl = ListFirst (pprop->values);
+ if (vl != NULL)
+ {
+ PropValue *pval = (PropValue *) vl->thing;
+
+ if (strcmp (pprop->name, SmProgram) == 0)
+ {
+ progName = GetProgramName ((char *) pval->value);
+
+ if ((int) strlen (progName) > maxlen1)
+ maxlen1 = strlen (progName);
+ }
+ else if (strcmp (pprop->name, "_XC_RestartService") == 0)
+ {
+ restart_service_prop = (char *) pval->value;
+ }
+ }
+ }
+
+ if (!progName)
+ continue;
+
+ if (restart_service_prop)
+ tmp1 = restart_service_prop;
+ else if (client->clientHostname)
+ tmp1 = client->clientHostname;
+ else
+ continue;
+
+ if ((tmp2 = (char *) strchr (tmp1, '/')) == NULL)
+ hostname = tmp1;
+ else
+ hostname = tmp2 + 1;
+
+ if ((int) strlen (hostname) > maxlen2)
+ maxlen2 = strlen (hostname);
+
+ numClientListNames++;
+ }
+
+ if (numClientListNames == 0)
+ {
+ XtSetSensitive (viewPropButton, 0);
+ XtSetSensitive (cloneButton, 0);
+ XtSetSensitive (killClientButton, 0);
+ XtSetSensitive (restartHintButton, 0);
+
+ XtUnmanageChild (clientListWidget);
+ XtManageChild (noClientsLabel);
+
+ reenable_asap = 1;
+
+ return;
+ }
+
+ if (reenable_asap)
+ {
+ XtSetSensitive (viewPropButton, 1);
+ XtSetSensitive (cloneButton, 1);
+ XtSetSensitive (killClientButton, 1);
+ XtSetSensitive (restartHintButton, 1);
+
+ XtUnmanageChild (noClientsLabel);
+ XtManageChild (clientListWidget);
+
+ reenable_asap = 0;
+ }
+
+ clientListNames = (String *) XtMalloc (
+ numClientListNames * sizeof (String));
+ clientListRecs = (ClientRec **) XtMalloc (
+ numClientListNames * sizeof (ClientRec *));
+
+ i = 0;
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+ int extra1, extra2;
+ char *hint;
+
+ progName = NULL;
+ restart_service_prop = NULL;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+ List *vl = ListFirst (pprop->values);
+
+ if (vl != NULL)
+ {
+ PropValue *pval = (PropValue *) vl->thing;
+ if (strcmp (pprop->name, SmProgram) == 0)
+ {
+ progName = GetProgramName ((char *) pval->value);
+ }
+ else if (strcmp (pprop->name, "_XC_RestartService") == 0)
+ {
+ restart_service_prop = (char *) pval->value;
+ }
+ }
+ }
+
+ if (!progName)
+ continue;
+
+ if (restart_service_prop)
+ tmp1 = restart_service_prop;
+ else if (client->clientHostname)
+ tmp1 = client->clientHostname;
+ else
+ continue;
+
+ if ((tmp2 = (char *) strchr (tmp1, '/')) == NULL)
+ hostname = tmp1;
+ else
+ hostname = tmp2 + 1;
+
+ extra1 = maxlen1 - strlen (progName) + 5;
+ extra2 = maxlen2 - strlen (hostname);
+
+ if (client->restartHint == SmRestartIfRunning)
+ hint = "Restart If Running";
+ else if (client->restartHint == SmRestartAnyway)
+ hint = "Restart Anyway";
+ else if (client->restartHint == SmRestartImmediately)
+ hint = "Restart Immediately";
+ else if (client->restartHint == SmRestartNever)
+ hint = "Restart Never";
+ else
+ hint = "";
+
+ clientInfo = (String) XtMalloc (strlen (progName) +
+ extra1 + extra2 + 3 + strlen (hostname) + 3 + strlen (hint) + 1);
+
+ for (k = 0; k < extra1; k++)
+ extraBuf1[k] = ' ';
+ extraBuf1[extra1] = '\0';
+
+ for (k = 0; k < extra2; k++)
+ extraBuf2[k] = ' ';
+ extraBuf2[extra2] = '\0';
+
+ sprintf (clientInfo, "%s%s (%s%s) %s", progName, extraBuf1,
+ hostname, extraBuf2, hint);
+
+ clientListRecs[i] = client;
+ clientListNames[i++] = clientInfo;
+ }
+
+ XawListChange (clientListWidget,
+ clientListNames, numClientListNames, 0, True);
+}
+
+
+
+static void
+RestartHintXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XawListReturnStruct *current;
+ ClientRec *client;
+ Widget active;
+ int found = 0;
+ List *pl;
+ char hint;
+
+ current = XawListShowCurrent (clientListWidget);
+
+ if (!current || current->list_index < 0)
+ {
+ if (current)
+ XtFree ((char *) current);
+ return;
+ }
+
+ client = clientListRecs[current->list_index];
+
+ active = XawSimpleMenuGetActiveEntry (restartHintMenu);
+
+ if (active == restartIfRunning)
+ hint = SmRestartIfRunning;
+ else if (active == restartAnyway)
+ hint = SmRestartAnyway;
+ else if (active == restartImmediately)
+ hint = SmRestartImmediately;
+ else if (active == restartNever)
+ hint = SmRestartNever;
+ else
+ {
+ XtFree ((char *) current);
+ return;
+ }
+
+ client->restartHint = hint;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+
+ if (strcmp (SmRestartStyleHint, pprop->name) == 0)
+ {
+ List *vl = ListFirst (pprop->values);
+ PropValue *pval = (PropValue *) vl->thing;
+
+ *((char *) (pval->value)) = hint;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ SmProp prop;
+ SmPropValue propval;
+
+ prop.name = SmRestartStyleHint;
+ prop.type = SmCARD8;
+ prop.num_vals = 1;
+ prop.vals = &propval;
+ propval.value = (SmPointer) &hint;
+ propval.length = 1;
+
+ SetProperty (client, &prop, False /* don't free it */);
+ }
+
+ UpdateClientList ();
+ XawListHighlight (clientListWidget, current_client_selected);
+ ShowHint (client);
+
+ if (client_prop_visible && clientListRecs &&
+ clientListRecs[current_client_selected] == client)
+ {
+ DisplayProps (client);
+ }
+
+ XtFree ((char *) current);
+}
+
+
+
+static void
+clientPropDoneXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XtPopdown (clientPropPopup);
+ client_prop_visible = 0;
+}
+
+
+
+void
+ClientInfoStructureNotifyXtHandler (w, closure, event, continue_to_dispatch)
+
+Widget w;
+XtPointer closure;
+XEvent *event;
+Boolean *continue_to_dispatch;
+
+{
+ if (event->type == MapNotify)
+ {
+ UpdateClientList ();
+
+ if (current_client_selected >= 0)
+ XawListHighlight (clientListWidget, current_client_selected);
+
+ XtRemoveEventHandler (clientInfoPopup, StructureNotifyMask, False,
+ ClientInfoStructureNotifyXtHandler, NULL);
+ }
+}
+
+
+
+void
+ClientInfoXtProc (w, client_data, callData)
+ Widget w;
+ XtPointer client_data;
+ XtPointer callData;
+
+{
+ static int first_time = 1;
+
+ if (client_info_visible)
+ {
+ /* Make sure it is visible */
+
+ XMapRaised (XtDisplay (topLevel), XtWindow (clientInfoPopup));
+ }
+ else
+ {
+ UpdateClientList ();
+
+ if (clientListRecs && clientListRecs[0])
+ {
+ current_client_selected = 0;
+ XawListHighlight (clientListWidget, 0);
+ ShowHint (clientListRecs[0]);
+ if (client_prop_visible)
+ DisplayProps (clientListRecs[0]);
+ }
+ else
+ current_client_selected = -1;
+
+ if (first_time && num_clients_in_last_session > 0 &&
+ num_clients_in_last_session != numClientListNames)
+ {
+ XtAddEventHandler (clientInfoPopup, StructureNotifyMask, False,
+ ClientInfoStructureNotifyXtHandler, NULL);
+ }
+
+ PopupPopup (mainWindow, clientInfoPopup,
+ False, first_time, 100, 50, "DelClientInfoWinAction()");
+
+ client_info_visible = 1;
+
+ if (first_time)
+ first_time = 0;
+ }
+}
+
+
+#define CHECK_WIDTH 9
+#define CHECK_HEIGHT 8
+
+static unsigned char check_bits[] = {
+ 0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
+ 0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00
+};
+
+
+
+static void
+DelClientInfoWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (clientInfoDoneButton, XtNcallback, NULL);
+}
+
+
+
+static void
+DelPropWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (clientPropDoneButton, XtNcallback, NULL);
+}
+
+
+
+void
+create_client_info_popup ()
+
+{
+ /*
+ * Install actions for WM_DELETE_WINDOW
+ */
+
+ static XtActionsRec actions[] = {
+ {"DelClientInfoWinAction", DelClientInfoWinAction},
+ {"DelPropWinAction", DelPropWinAction}
+ };
+
+ XtAppAddActions (appContext, actions, XtNumber (actions));
+
+
+ /*
+ * Make checkmark bitmap
+ */
+
+ checkBitmap = XCreateBitmapFromData (
+ XtDisplay (topLevel), RootWindowOfScreen (XtScreen (topLevel)),
+ (char *) check_bits, CHECK_WIDTH, CHECK_HEIGHT);
+
+
+ /*
+ * Pop up for List Clients button.
+ */
+
+ clientInfoPopup = XtVaCreatePopupShell (
+ "clientInfoPopup", topLevelShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+
+ clientInfoForm = XtVaCreateManagedWidget (
+ "clientInfoForm", formWidgetClass, clientInfoPopup,
+ NULL);
+
+ viewPropButton = XtVaCreateManagedWidget (
+ "viewPropButton", commandWidgetClass, clientInfoForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ XtAddCallback (viewPropButton, XtNcallback, ViewPropXtProc, 0);
+
+
+ cloneButton = XtVaCreateManagedWidget (
+ "cloneButton", commandWidgetClass, clientInfoForm,
+ XtNfromHoriz, viewPropButton,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ XtAddCallback (cloneButton, XtNcallback, CloneXtProc, 0);
+
+
+ killClientButton = XtVaCreateManagedWidget (
+ "killClientButton", commandWidgetClass, clientInfoForm,
+ XtNfromHoriz, cloneButton,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ XtAddCallback (killClientButton, XtNcallback, KillClientXtProc, 0);
+
+
+ restartHintButton = XtVaCreateManagedWidget (
+ "restartHintButton", menuButtonWidgetClass, clientInfoForm,
+ XtNmenuName, "restartHintMenu",
+ XtNfromHoriz, killClientButton,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ restartHintMenu = XtVaCreatePopupShell (
+ "restartHintMenu", simpleMenuWidgetClass, clientInfoForm,
+ NULL);
+
+ restartIfRunning = XtVaCreateManagedWidget (
+ "restartIfRunning", smeBSBObjectClass, restartHintMenu,
+ XtNleftMargin, 18,
+ NULL);
+
+ restartAnyway = XtVaCreateManagedWidget (
+ "restartAnyway", smeBSBObjectClass, restartHintMenu,
+ XtNleftMargin, 18,
+ NULL);
+
+ restartImmediately = XtVaCreateManagedWidget (
+ "restartImmediately", smeBSBObjectClass, restartHintMenu,
+ XtNleftMargin, 18,
+ NULL);
+
+ restartNever = XtVaCreateManagedWidget (
+ "restartNever", smeBSBObjectClass, restartHintMenu,
+ XtNleftMargin, 18,
+ NULL);
+
+ XtAddCallback (restartIfRunning, XtNcallback, RestartHintXtProc, 0);
+ XtAddCallback (restartAnyway, XtNcallback, RestartHintXtProc, 0);
+ XtAddCallback (restartImmediately, XtNcallback, RestartHintXtProc, 0);
+ XtAddCallback (restartNever, XtNcallback, RestartHintXtProc, 0);
+
+
+ clientInfoDoneButton = XtVaCreateManagedWidget (
+ "clientInfoDoneButton", commandWidgetClass, clientInfoForm,
+ XtNfromHoriz, restartHintButton,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ XtAddCallback (clientInfoDoneButton, XtNcallback, listDoneXtProc, 0);
+
+
+ clientListWidget = XtVaCreateManagedWidget (
+ "clientListWidget", listWidgetClass, clientInfoForm,
+ XtNdefaultColumns, 1,
+ XtNforceColumns, True,
+ XtNfromHoriz, NULL,
+ XtNfromVert, viewPropButton,
+ XtNresizable, True,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ NULL);
+
+ XtAddCallback (clientListWidget, XtNcallback, ClientListXtProc, 0);
+
+ noClientsLabel = XtVaCreateWidget (
+ "noClientsLabel", labelWidgetClass, clientInfoForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, viewPropButton,
+ XtNborderWidth, 0,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ NULL);
+
+ manualRestartLabel = XtVaCreateManagedWidget (
+ "manualRestartLabel", labelWidgetClass, clientInfoForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, clientListWidget,
+ XtNborderWidth, 0,
+ XtNvertDistance, 20,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ manualRestartCommands = XtVaCreateManagedWidget (
+ "manualRestartCommands", asciiTextWidgetClass, clientInfoForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, manualRestartLabel,
+ XtNeditType, XawtextEdit,
+ XtNresizable, True,
+ XtNresize, XawtextResizeWidth,
+ XtNscrollVertical, XawtextScrollAlways,
+ XtNwidth, 350,
+ XtNheight, 100,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ /*
+ * Pop up for viewing client properties
+ */
+
+ clientPropPopup = XtVaCreatePopupShell (
+ "clientPropPopup", topLevelShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+
+ clientPropForm = XtVaCreateManagedWidget (
+ "clientPropForm", formWidgetClass, clientPropPopup,
+ NULL);
+
+ clientPropDoneButton = XtVaCreateManagedWidget (
+ "clientPropDoneButton", commandWidgetClass, clientPropForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ XtAddCallback (clientPropDoneButton, XtNcallback, clientPropDoneXtProc, 0);
+
+
+ clientPropTextWidget = XtVaCreateManagedWidget (
+ "clientPropTextWidget", asciiTextWidgetClass, clientPropForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, clientPropDoneButton,
+ XtNeditType, XawtextRead,
+ XtNdisplayCaret, False,
+ XtNscrollVertical, XawtextScrollWhenNeeded,
+ XtNscrollHorizontal, XawtextScrollWhenNeeded,
+ XtNresizable, True,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainBottom,
+ NULL);
+}
diff --git a/info.h b/info.h
new file mode 100644
index 0000000..9d98b26
--- /dev/null
+++ b/info.h
@@ -0,0 +1,29 @@
+/* $Xorg: info.h,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void create_client_info_popup ();
+extern void UpdateClientList ();
+extern void ClientInfoXtProc ();
diff --git a/list.c b/list.c
new file mode 100644
index 0000000..25d2647
--- /dev/null
+++ b/list.c
@@ -0,0 +1,170 @@
+/* $Xorg: list.c,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+
+List *
+ListInit()
+{
+ List *l;
+
+ l = (List *)XtMalloc(sizeof *l);
+ if(!l) return l;
+ l->next = l;
+ l->prev = l;
+ l->thing = NULL;
+ return l;
+}
+
+List *
+ListFirst(l)
+List *l;
+{
+ if(l->next->thing) return l->next;
+ else return NULL;
+}
+
+List *
+ListNext(l)
+List *l;
+{
+ if(l->next->thing) return l->next;
+ else return NULL;
+}
+
+void
+ListFreeAll(l)
+List *l;
+{
+ char *thing;
+ List *next;
+
+ next = l->next;
+ do {
+ l = next;
+ next = l->next;
+ thing = l->thing;
+ XtFree((char *)l);
+ } while(thing);
+}
+
+void
+ListFreeAllButHead(l)
+List *l;
+{
+ List *p, *next;
+
+ p = ListFirst(l);
+
+ while (p)
+ {
+ next = ListNext (p);
+ XtFree((char *) p);
+ p = next;
+ }
+
+ l->next = l;
+ l->prev = l;
+}
+
+List *
+ListAddFirst(l, v)
+List *l;
+char *v;
+{
+ List *e;
+
+ e = (List *)XtMalloc(sizeof *e);
+ if(!e) return NULL;
+
+ e->thing = v;
+ e->prev = l;
+ e->next = e->prev->next;
+ e->prev->next = e;
+ e->next->prev = e;
+
+ return e;
+}
+
+List *
+ListAddLast(l, v)
+List *l;
+char *v;
+{
+ List *e;
+
+ e = (List *)XtMalloc(sizeof *e);
+ if(!e) return NULL;
+
+ e->thing = v;
+ e->next = l;
+ e->prev = e->next->prev;
+ e->prev->next = e;
+ e->next->prev = e;
+
+ return e;
+}
+
+void
+ListFreeOne(e)
+List *e;
+{
+ e->next->prev = e->prev;
+ e->prev->next = e->next;
+ XtFree((char *)e);
+}
+
+
+Status
+ListSearchAndFreeOne(l,thing)
+List *l;
+char *thing;
+{
+ List *p;
+
+ for (p = ListFirst (l); p; p = ListNext (p))
+ if (((char *) p->thing) == (char *) thing)
+ {
+ ListFreeOne (p);
+ return (1);
+ }
+
+ return (0);
+}
+
+
+int
+ListCount(l)
+List *l;
+{
+ int i;
+ List *e;
+
+ i = 0;
+ for(e = ListFirst(l); e; e = ListNext(e)) i++;
+
+ return i;
+}
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..97b0593
--- /dev/null
+++ b/list.h
@@ -0,0 +1,42 @@
+/* $Xorg: list.h,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+typedef struct _List {
+ struct _List *prev;
+ struct _List *next;
+ char *thing;
+} List;
+
+extern List *ListInit();
+extern List *ListFirst();
+extern List *ListNext();
+extern void ListFreeAll();
+extern void ListFreeAllButHead();
+extern void ListFreeOne();
+extern Status ListSearchAndFreeOne();
+extern List *ListAddFirst();
+extern List *ListAddLast();
+extern int ListCount();
diff --git a/lock.c b/lock.c
new file mode 100644
index 0000000..46b5083
--- /dev/null
+++ b/lock.c
@@ -0,0 +1,176 @@
+/* $Xorg: lock.c,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include <sys/types.h>
+
+
+static char *
+GetPath ()
+
+{
+ char *path = (char *) getenv ("SM_SAVE_DIR");
+
+ if (!path)
+ {
+ path = (char *) getenv ("HOME");
+ if (!path)
+ path = ".";
+ }
+
+ return (path);
+}
+
+
+Status
+LockSession (session_name, write_id)
+
+char *session_name;
+Bool write_id;
+
+{
+ char *path;
+ char lock_file[PATH_MAX];
+ char temp_lock_file[PATH_MAX];
+ Status status;
+ int fd;
+
+ path = GetPath ();
+
+ sprintf (lock_file, "%s/.XSMlock-%s", path, session_name);
+ sprintf (temp_lock_file, "%s/.XSMtlock-%s", path, session_name);
+
+ if ((fd = creat (temp_lock_file, 0444)) < 0)
+ return (0);
+
+ if (write_id &&
+ (write (fd, networkIds, strlen (networkIds)) != strlen (networkIds)) ||
+ (write (fd, "\n", 1) != 1))
+ {
+ close (fd);
+ return (0);
+ }
+
+ close (fd);
+
+ status = 1;
+
+ if (link (temp_lock_file, lock_file) < 0)
+ status = 0;
+
+ if (unlink (temp_lock_file) < 0)
+ status = 0;
+
+ return (status);
+}
+
+
+void
+UnlockSession (session_name)
+
+char *session_name;
+
+{
+ char *path;
+ char lock_file[PATH_MAX];
+
+ path = GetPath ();
+
+ sprintf (lock_file, "%s/.XSMlock-%s", path, session_name);
+
+ unlink (lock_file);
+}
+
+
+char *
+GetLockId (session_name)
+
+char *session_name;
+
+{
+ char *path;
+ FILE *fp;
+ char lock_file[PATH_MAX];
+ char buf[256];
+ char *ret;
+
+ path = GetPath ();
+
+ sprintf (lock_file, "%s/.XSMlock-%s", path, session_name);
+
+ if ((fp = fopen (lock_file, "r")) == NULL)
+ {
+ return (NULL);
+ }
+
+ buf[0] = '\0';
+ fscanf (fp, "%s\n", buf);
+ ret = XtNewString (buf);
+
+ fclose (fp);
+
+ return (ret);
+}
+
+
+Bool
+CheckSessionLocked (session_name, get_id, id_ret)
+
+char *session_name;
+Bool get_id;
+char **id_ret;
+
+{
+ if (get_id)
+ *id_ret = GetLockId (session_name);
+
+ if (!LockSession (session_name, False))
+ return (1);
+
+ UnlockSession (session_name);
+ return (0);
+}
+
+
+void
+UnableToLockSession (session_name)
+
+char *session_name;
+
+{
+ /*
+ * We should popup a dialog here giving error.
+ */
+
+#ifdef XKB
+ XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_Failure);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+ sleep (2);
+
+ ChooseSession ();
+}
diff --git a/lock.h b/lock.h
new file mode 100644
index 0000000..2153c00
--- /dev/null
+++ b/lock.h
@@ -0,0 +1,31 @@
+/* $Xorg: lock.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern Status LockSession ();
+extern void UnlockSession ();
+extern char *GetLockId ();
+extern Bool CheckSessionLocked ();
+extern void UnableToLockSession ();
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..f8f642a
--- /dev/null
+++ b/log.c
@@ -0,0 +1,167 @@
+/* $Xorg: log.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include "save.h"
+#include "popup.h"
+
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/AsciiText.h>
+
+Widget logPopup;
+Widget logForm;
+Widget logText;
+Widget logOkButton;
+
+
+
+void
+DisplayLogXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ static int first_time = 1;
+
+ if (client_log_visible)
+ {
+ /* Make sure it is visible */
+
+ XMapRaised (XtDisplay (topLevel), XtWindow (logPopup));
+ }
+ else
+ {
+ PopupPopup (mainWindow, logPopup,
+ False, first_time, 50, 50, "DelLogWinAction()");
+
+ client_log_visible = 1;
+
+ if (first_time)
+ first_time = 0;
+ }
+}
+
+
+
+static void
+logOkXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XtPopdown (logPopup);
+ client_log_visible = 0;
+}
+
+
+
+void
+add_log_text (str)
+
+char *str;
+
+{
+ XawTextPosition pos = XawTextGetInsertionPoint (logText);
+ XawTextBlock text;
+
+ text.firstPos = 0;
+ text.length = strlen (str);
+ text.ptr = str;
+ text.format = XawFmt8Bit;
+
+ XawTextReplace (logText, pos, pos, &text);
+}
+
+
+
+static void
+DelLogWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (logOkButton, XtNcallback, NULL);
+}
+
+
+
+void
+create_log_popup ()
+
+{
+ /*
+ * Pop up for session log
+ */
+
+ static XtActionsRec actions[] = {
+ {"DelLogWinAction", DelLogWinAction}
+ };
+
+ XtAppAddActions (appContext, actions, XtNumber (actions));
+
+ logPopup = XtVaCreatePopupShell ("logPopup",
+ topLevelShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+ logForm = XtVaCreateManagedWidget (
+ "logForm", formWidgetClass, logPopup,
+ NULL);
+
+ logText = XtVaCreateManagedWidget (
+ "logText", asciiTextWidgetClass, logForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNeditType, XawtextAppend,
+ XtNdisplayCaret, False,
+ XtNscrollVertical, XawtextScrollAlways,
+ XtNscrollHorizontal, XawtextScrollWhenNeeded,
+ XtNresizable, True,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ logOkButton = XtVaCreateManagedWidget (
+ "logOkButton", commandWidgetClass, logForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, logText,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ XtNleft, XawChainLeft,
+ XtNright, XawChainLeft,
+ NULL);
+
+ XtAddCallback (logOkButton, XtNcallback, logOkXtProc, 0);
+}
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..414ea5a
--- /dev/null
+++ b/log.h
@@ -0,0 +1,29 @@
+/* $Xorg: log.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void DisplayLogXtProc ();
+extern void create_log_popup ();
+extern void add_log_text ();
diff --git a/mainwin.c b/mainwin.c
new file mode 100644
index 0000000..ac317d6
--- /dev/null
+++ b/mainwin.c
@@ -0,0 +1,135 @@
+/* $Xorg: mainwin.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include "info.h"
+#include "save.h"
+#include "log.h"
+
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/SmeBSB.h>
+
+Widget mainWindow;
+Widget clientInfoButton;
+Widget logButton;
+Widget checkPointButton;
+Widget shutdownButton;
+Widget shutdownMenu;
+Widget shutdownSave;
+Widget shutdownDontSave;
+
+
+
+static void
+DelMainWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (shutdownSave, XtNcallback, NULL);
+}
+
+
+
+void
+create_main_window ()
+
+{
+ /*
+ * Main window
+ */
+
+ static XtActionsRec actions[] = {
+ {"DelMainWinAction", DelMainWinAction}
+ };
+
+ mainWindow = XtVaCreateManagedWidget (
+ "mainWindow", formWidgetClass, topLevel,
+ NULL);
+
+ clientInfoButton = XtVaCreateManagedWidget (
+ "clientInfoButton", commandWidgetClass, mainWindow,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNresizable, True,
+ XtNjustify, XtJustifyLeft,
+ NULL);
+
+ XtAddCallback (clientInfoButton, XtNcallback, ClientInfoXtProc, 0);
+
+ logButton = XtVaCreateManagedWidget (
+ "logButton", commandWidgetClass, mainWindow,
+ XtNfromHoriz, clientInfoButton,
+ XtNfromVert, NULL,
+ XtNresizable, True,
+ XtNjustify, XtJustifyLeft,
+ NULL);
+
+ XtAddCallback (logButton, XtNcallback, DisplayLogXtProc, 0);
+
+ checkPointButton = XtVaCreateManagedWidget (
+ "checkPointButton", commandWidgetClass, mainWindow,
+ XtNfromHoriz, NULL,
+ XtNfromVert, clientInfoButton,
+ XtNresizable, True,
+ XtNjustify, XtJustifyLeft,
+ NULL);
+
+ XtAddCallback (checkPointButton, XtNcallback, CheckPointXtProc, 0);
+
+ shutdownButton = XtVaCreateManagedWidget (
+ "shutdownButton", menuButtonWidgetClass, mainWindow,
+ XtNmenuName, "shutdownMenu",
+ XtNfromHoriz, checkPointButton,
+ XtNfromVert, clientInfoButton,
+ XtNresizable, True,
+ XtNjustify, XtJustifyLeft,
+ NULL);
+
+ shutdownMenu = XtVaCreatePopupShell (
+ "shutdownMenu", simpleMenuWidgetClass, mainWindow,
+ NULL);
+
+ shutdownSave = XtVaCreateManagedWidget (
+ "shutdownSave", smeBSBObjectClass, shutdownMenu,
+ NULL);
+
+ shutdownDontSave = XtVaCreateManagedWidget (
+ "shutdownDontSave", smeBSBObjectClass, shutdownMenu,
+ NULL);
+
+ XtAddCallback (shutdownSave, XtNcallback, ShutdownSaveXtProc, 0);
+ XtAddCallback (shutdownDontSave, XtNcallback, ShutdownDontSaveXtProc, 0);
+
+ XtAppAddActions (appContext, actions, XtNumber (actions));
+}
diff --git a/mainwin.h b/mainwin.h
new file mode 100644
index 0000000..54063b9
--- /dev/null
+++ b/mainwin.h
@@ -0,0 +1,27 @@
+/* $Xorg: mainwin.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void create_main_window ();
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..0a33e1f
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,157 @@
+/* $Xorg: misc.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+
+
+#ifdef NOPUTENV
+/*
+ * define our own putenv() if the system doesn't have one.
+ * putenv(s): place s (a string of the form "NAME=value") in
+ * the environment; replacing any existing NAME. s is placed in
+ * environment, so if you change s, the environment changes (like
+ * putenv on a sun). Binding removed if you putenv something else
+ * called NAME.
+ */
+int
+putenv(s)
+ char *s;
+{
+ char *v;
+ int varlen, idx;
+ extern char **environ;
+ char **newenv;
+ static int virgin = 1; /* true while "environ" is a virgin */
+
+ v = strchr(s, '=');
+ if(v == 0)
+ return 0; /* punt if it's not of the right form */
+ varlen = (v + 1) - s;
+
+ for (idx = 0; environ[idx] != 0; idx++) {
+ if (strncmp(environ[idx], s, varlen) == 0) {
+ if(v[1] != 0) { /* true if there's a value */
+ environ[idx] = s;
+ return 0;
+ } else {
+ do {
+ environ[idx] = environ[idx+1];
+ } while(environ[++idx] != 0);
+ return 0;
+ }
+ }
+ }
+
+ /* add to environment (unless no value; then just return) */
+ if(v[1] == 0)
+ return 0;
+ if(virgin) {
+ register i;
+
+ newenv = (char **) XtMalloc((unsigned) ((idx + 2) * sizeof(char*)));
+ if(newenv == 0)
+ return -1;
+ for(i = idx-1; i >= 0; --i)
+ newenv[i] = environ[i];
+ virgin = 0; /* you're not a virgin anymore, sweety */
+ } else {
+ newenv = (char **) realloc((char *) environ,
+ (unsigned) ((idx + 2) * sizeof(char*)));
+ if (newenv == 0)
+ return -1;
+ }
+
+ environ = newenv;
+ environ[idx] = s;
+ environ[idx+1] = 0;
+
+ return 0;
+}
+
+#endif /* NOPUTENV */
+
+
+
+strbw (a, b)
+
+char *a;
+char *b;
+
+{
+ return !strncmp (a, b, strlen (b));
+}
+
+
+
+#ifdef X_NOT_STDC_ENV
+
+char *Strstr (s1, s2)
+
+char *s1;
+char *s2;
+
+{
+ int n1, n2;
+
+ n1 = strlen (s1);
+ n2 = strlen (s2);
+
+ for (; n1 >= n2; s1++, n1--)
+ {
+ if (!strncmp (s1, s2, n2))
+ return s1;
+ }
+
+ return NULL;
+}
+
+#endif
+
+
+
+#if defined(sun) && defined(SVR4)
+int System (s)
+ char *s;
+{
+ int pid, status;
+ if ((pid = fork ()) == 0) {
+ (void) setpgrp();
+ execl ("/bin/sh", "sh", "-c", s, 0);
+ } else
+ waitpid (pid, &status, 0);
+ return status;
+}
+#endif
+
+
+
+nomem ()
+
+{
+ fprintf (stderr, "Insufficient memory.\n");
+ exit (255);
+}
+
diff --git a/popup.c b/popup.c
new file mode 100644
index 0000000..b6ecfab
--- /dev/null
+++ b/popup.c
@@ -0,0 +1,184 @@
+/* $Xorg: popup.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+
+/*
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+#include "xsm.h"
+#include <X11/Shell.h>
+
+
+void
+PopupPopup (parent, popup, transient, first_time,
+ offset_x, offset_y, delAction)
+
+Widget parent;
+Widget popup;
+Bool transient;
+Bool first_time;
+int offset_x;
+int offset_y;
+String delAction;
+
+{
+ if (!transient && !first_time)
+ {
+ /*
+ * For non-transient windows, if this isn't the first time
+ * it's being popped up, just pop it up in the old position.
+ */
+
+ XtPopup (popup, XtGrabNone);
+ return;
+ }
+ else
+ {
+ Position parent_x, parent_y;
+ Position root_x, root_y;
+ Position popup_x, popup_y;
+ Dimension parent_width, parent_height, parent_border;
+ Dimension popup_width, popup_height, popup_border;
+ char geom[16];
+ Bool repos = 0;
+
+ /*
+ * The window we pop up must be visible on the screen. We first
+ * try to position it at the desired location (relative to the
+ * parent widget). Once we are able to compute the popup's
+ * geometry (after it's realized), we can determine if we need
+ * to reposition it.
+ */
+
+ XtVaGetValues (parent,
+ XtNx, &parent_x,
+ XtNy, &parent_y,
+ XtNwidth, &parent_width,
+ XtNheight, &parent_height,
+ XtNborderWidth, &parent_border,
+ NULL);
+
+ XtTranslateCoords (parent, parent_x, parent_y, &root_x, &root_y);
+
+ popup_x = root_x + offset_x;
+ popup_y = root_y + offset_y;
+
+ if (transient)
+ {
+ XtVaSetValues (popup,
+ XtNx, popup_x,
+ XtNy, popup_y,
+ NULL);
+ }
+ else
+ {
+ sprintf (geom, "+%d+%d", popup_x, popup_y);
+
+ XtVaSetValues (popup,
+ XtNgeometry, geom,
+ NULL);
+ }
+
+ if (first_time)
+ {
+ /*
+ * Realize it for the first time
+ */
+
+ XtRealizeWidget (popup);
+
+
+ /*
+ * Set support for WM_DELETE_WINDOW
+ */
+
+ (void) SetWM_DELETE_WINDOW (popup, delAction);
+ }
+
+ /*
+ * Now make sure it's visible.
+ */
+
+ XtVaGetValues (popup,
+ XtNwidth, &popup_width,
+ XtNheight, &popup_height,
+ XtNborderWidth, &popup_border,
+ NULL);
+
+ popup_border <<= 1;
+
+ if ((int) (popup_x + popup_width + popup_border) >
+ WidthOfScreen (XtScreen (topLevel)))
+ {
+ popup_x = WidthOfScreen (XtScreen (topLevel)) -
+ popup_width - popup_border - parent_width - parent_border;
+
+ repos = 1;
+ }
+
+ if ((int) (popup_y + popup_height + popup_border) >
+ HeightOfScreen (XtScreen (topLevel)))
+ {
+ popup_y = HeightOfScreen (XtScreen (topLevel)) -
+ popup_height - popup_border - parent_height - parent_border;
+
+ repos = 1;
+ }
+
+ if (repos)
+ {
+ if (transient)
+ {
+ XtVaSetValues (popup,
+ XtNx, popup_x,
+ XtNy, popup_y,
+ NULL);
+ }
+ else
+ {
+ /*
+ * The only way we can reposition a non-transient
+ * is by unrealizing it, setting the position, then
+ * doing a realize.
+ */
+
+ XtUnrealizeWidget (popup);
+
+ sprintf (geom, "+%d+%d", popup_x, popup_y);
+
+ XtVaSetValues (popup,
+ XtNgeometry, geom,
+ NULL);
+
+ XtRealizeWidget (popup);
+
+ (void) SetWM_DELETE_WINDOW (popup, delAction);
+ }
+ }
+
+ XtPopup (popup, XtGrabNone);
+ }
+}
diff --git a/popup.h b/popup.h
new file mode 100644
index 0000000..68f04b1
--- /dev/null
+++ b/popup.h
@@ -0,0 +1,31 @@
+/* $Xorg: popup.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+
+/*
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+*/
+
+extern void PopupPopup ();
diff --git a/printhex.c b/printhex.c
new file mode 100644
index 0000000..b411f9e
--- /dev/null
+++ b/printhex.c
@@ -0,0 +1,81 @@
+/* $Xorg: printhex.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include <stdio.h>
+
+static char *hex_table[] = { /* for printing hex digits */
+ "00", "01", "02", "03", "04", "05", "06", "07",
+ "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
+ "10", "11", "12", "13", "14", "15", "16", "17",
+ "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
+ "20", "21", "22", "23", "24", "25", "26", "27",
+ "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
+ "30", "31", "32", "33", "34", "35", "36", "37",
+ "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
+ "40", "41", "42", "43", "44", "45", "46", "47",
+ "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
+ "50", "51", "52", "53", "54", "55", "56", "57",
+ "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
+ "60", "61", "62", "63", "64", "65", "66", "67",
+ "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
+ "70", "71", "72", "73", "74", "75", "76", "77",
+ "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
+ "80", "81", "82", "83", "84", "85", "86", "87",
+ "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
+ "90", "91", "92", "93", "94", "95", "96", "97",
+ "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
+ "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
+ "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
+ "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7",
+ "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
+ "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7",
+ "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "d8", "d9", "da", "db", "dc", "dd", "de", "df",
+ "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7",
+ "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
+ "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+ "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff",
+};
+
+
+void
+fprintfhex (fp, len, cp)
+
+register FILE *fp;
+unsigned int len;
+char *cp;
+
+{
+ unsigned char *ucp = (unsigned char *) cp;
+
+ for (; len > 0; len--, ucp++)
+ {
+ register char *s = hex_table[*ucp];
+ putc (s[0], fp);
+ putc (s[1], fp);
+ }
+}
diff --git a/prop.c b/prop.c
new file mode 100644
index 0000000..b8a4d25
--- /dev/null
+++ b/prop.c
@@ -0,0 +1,420 @@
+/* $Xorg: prop.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+
+extern Widget clientListWidget;
+
+
+void
+FreePropValues (propValues)
+
+List *propValues;
+
+{
+ List *pv;
+ PropValue *pval;
+
+ for (pv = ListFirst (propValues); pv; pv = ListNext (pv))
+ {
+ pval = (PropValue *) pv->thing;
+ XtFree ((char *) pval->value);
+ XtFree ((char *) pval);
+ }
+
+ ListFreeAll (propValues);
+}
+
+
+
+void
+FreeProp (prop)
+
+Prop *prop;
+
+{
+ FreePropValues (prop->values);
+ XtFree (prop->name);
+ XtFree (prop->type);
+ XtFree ((char *) prop);
+}
+
+
+
+void
+SetInitialProperties (client, props)
+
+ClientRec *client;
+List *props;
+
+{
+ List *pl;
+
+ if (verbose)
+ printf("Setting initial properties for %s\n", client->clientId);
+
+ if (client->props)
+ {
+ /*
+ * The only way client->props could be non-NULL is if the list
+ * was initialized, but nothing was added yet. So we just free
+ * the head of the list.
+ */
+
+ XtFree ((char *) client->props);
+ }
+
+ client->props = props;
+
+ for (pl = ListFirst (props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop;
+ PropValue *pval;
+ List *vl;
+
+ pprop = (Prop *) pl->thing;
+
+ if (strcmp (pprop->name, SmDiscardCommand) == 0)
+ {
+ if (client->discardCommand)
+ XtFree (client->discardCommand);
+
+ vl = ListFirst (pprop->values);
+ pval = (PropValue *) vl->thing;
+
+ client->discardCommand = (char *) XtNewString (
+ (char *) pval->value);
+ }
+ else if (strcmp (pprop->name, SmRestartStyleHint) == 0)
+ {
+ int hint;
+
+ vl = ListFirst (pprop->values);
+ pval = (PropValue *) vl->thing;
+
+ hint = (int) *((char *) (pval->value));
+
+ if (hint == SmRestartIfRunning || hint == SmRestartAnyway ||
+ hint == SmRestartImmediately || hint == SmRestartNever)
+ {
+ client->restartHint = hint;
+ }
+ }
+ }
+}
+
+
+
+void
+SetProperty (client, theProp, freeIt)
+
+ClientRec *client;
+SmProp *theProp;
+Bool freeIt;
+
+{
+ List *pl;
+ Prop *pprop = NULL;
+ int found = 0, i;
+
+ /*
+ * If the property exists, delete the property values. We can
+ * re-use the actual property header.
+ */
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ pprop = (Prop *) pl->thing;
+
+ if (strcmp (theProp->name, pprop->name) == 0 &&
+ strcmp (theProp->type, pprop->type) == 0)
+ {
+ FreePropValues (pprop->values);
+ found = 1;
+ break;
+ }
+ }
+
+
+ /*
+ * Add the new property
+ */
+
+ if (!found)
+ {
+ pprop = (Prop *) XtMalloc (sizeof (Prop));
+ pprop->name = XtNewString (theProp->name);
+ pprop->type = XtNewString (theProp->type);
+ }
+
+ pprop->values = ListInit ();
+
+ for (i = 0; i < theProp->num_vals; i++)
+ {
+ PropValue *pval = (PropValue *) XtMalloc (sizeof (PropValue));
+
+ pval->length = theProp->vals[i].length;
+ pval->value = (XtPointer) XtMalloc (theProp->vals[i].length + 1);
+ memcpy (pval->value, theProp->vals[i].value, theProp->vals[i].length);
+ ((char *) pval->value)[theProp->vals[i].length] = '\0';
+
+ ListAddLast (pprop->values, (char *) pval);
+ }
+
+ if (pl)
+ pl->thing = (char *) pprop;
+ else
+ ListAddLast (client->props, (char *) pprop);
+
+ if (strcmp (theProp->name, SmDiscardCommand) == 0)
+ {
+ if (saveInProgress)
+ {
+ /*
+ * We are in the middle of a save yourself. We save the
+ * discard command we get now, and make it the current discard
+ * command when the save is over.
+ */
+
+ if (client->saveDiscardCommand)
+ XtFree (client->saveDiscardCommand);
+ client->saveDiscardCommand =
+ (char *) XtNewString (theProp->vals[0].value);
+
+ client->receivedDiscardCommand = True;
+ }
+ else
+ {
+ if (client->discardCommand)
+ XtFree (client->discardCommand);
+ client->discardCommand =
+ (char *) XtNewString (theProp->vals[0].value);
+ }
+ }
+ else if (strcmp (theProp->name, SmRestartStyleHint) == 0)
+ {
+ int hint = (int) *((char *) (theProp->vals[0].value));
+
+ if (hint == SmRestartIfRunning || hint == SmRestartAnyway ||
+ hint == SmRestartImmediately || hint == SmRestartNever)
+ {
+ client->restartHint = hint;
+ }
+ }
+
+ if (freeIt)
+ SmFreeProperty (theProp);
+}
+
+
+
+void
+DeleteProperty (client, propname)
+
+ClientRec *client;
+char *propname;
+
+{
+ List *pl;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+
+ if (strcmp (pprop->name, propname) == 0)
+ {
+ FreeProp (pprop);
+ ListFreeOne (pl);
+
+ if (strcmp (propname, SmDiscardCommand) == 0)
+ {
+ if (client->discardCommand)
+ {
+ XtFree (client->discardCommand);
+ client->discardCommand = NULL;
+ }
+
+ if (client->saveDiscardCommand)
+ {
+ XtFree (client->saveDiscardCommand);
+ client->saveDiscardCommand = NULL;
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+
+void
+SetPropertiesProc (smsConn, managerData, numProps, props)
+
+SmsConn smsConn;
+SmPointer managerData;
+int numProps;
+SmProp **props;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+ int updateList, i;
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, received SET PROPERTIES ", client->clientId);
+ printf ("[Num props = %d]\n", numProps);
+ }
+
+ updateList = (ListCount (client->props) == 0) &&
+ numProps > 0 && client_info_visible;
+
+ for (i = 0; i < numProps; i++)
+ {
+ SetProperty (client, props[i], True /* free it */);
+ }
+
+ free ((char *) props);
+
+ if (updateList)
+ {
+ /*
+ * We have enough info from the client to display it in our list.
+ */
+
+ UpdateClientList ();
+ XawListHighlight (clientListWidget, current_client_selected);
+ }
+ else if (client_prop_visible && clientListRecs &&
+ clientListRecs[current_client_selected] == client)
+ {
+ DisplayProps (client);
+ }
+}
+
+
+
+void
+DeletePropertiesProc (smsConn, managerData, numProps, propNames)
+
+SmsConn smsConn;
+SmPointer managerData;
+int numProps;
+char **propNames;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+ int i;
+
+ if (verbose) {
+ printf ("Client Id = %s, received DELETE PROPERTIES ",
+ client->clientId);
+ printf ("[Num props = %d]\n", numProps);
+ }
+
+ for (i = 0; i < numProps; i++)
+ {
+ if (verbose)
+ printf (" Name: %s\n", propNames[i]);
+
+ DeleteProperty (client, propNames[i]);
+
+ free (propNames[i]);
+ }
+
+ free ((char *) propNames);
+}
+
+
+
+void
+GetPropertiesProc (smsConn, managerData)
+
+SmsConn smsConn;
+SmPointer managerData;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+ SmProp **propsRet, *propRet;
+ SmPropValue *propValRet;
+ Prop *pprop;
+ PropValue *pval;
+ List *pl, *pj;
+ int numProps;
+ int index, i;
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, received GET PROPERTIES\n", client->clientId);
+ printf ("\n");
+ }
+
+ /*
+ * Unfortunately, we store the properties in a format different
+ * from the one required by SMlib.
+ */
+
+ numProps = ListCount (client->props);
+ propsRet = (SmProp **) XtMalloc (numProps * sizeof (SmProp *));
+
+ index = 0;
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ propsRet[index] = propRet = (SmProp *) XtMalloc (sizeof (SmProp));
+
+ pprop = (Prop *) pl->thing;
+
+ propRet->name = XtNewString (pprop->name);
+ propRet->type = XtNewString (pprop->type);
+ propRet->num_vals = ListCount (pprop->values);
+ propRet->vals = propValRet = (SmPropValue *) XtMalloc (
+ propRet->num_vals * sizeof (SmPropValue));
+
+ for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
+ {
+ pval = (PropValue *) pj->thing;
+
+ propValRet->length = pval->length;
+ propValRet->value = (SmPointer) XtMalloc (pval->length);
+ memcpy (propValRet->value, pval->value, pval->length);
+
+ propValRet++;
+ }
+
+ index++;
+ }
+
+ SmsReturnProperties (smsConn, numProps, propsRet);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent PROPERTIES REPLY [Num props = %d]\n",
+ client->clientId, numProps);
+ }
+
+ for (i = 0; i < numProps; i++)
+ SmFreeProperty (propsRet[i]);
+ XtFree ((char *) propsRet);
+}
diff --git a/prop.h b/prop.h
new file mode 100644
index 0000000..0b45ffe
--- /dev/null
+++ b/prop.h
@@ -0,0 +1,34 @@
+/* $Xorg: prop.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void FreePropValues ();
+extern void FreeProp ();
+extern void SetInitialProperties ();
+extern void SetProperty ();
+extern void DeleteProperty ();
+extern void SetPropertiesProc ();
+extern void DeletePropertiesProc ();
+extern void GetPropertiesProc ();
diff --git a/remote.c b/remote.c
new file mode 100644
index 0000000..0b20bcc
--- /dev/null
+++ b/remote.c
@@ -0,0 +1,259 @@
+/* $Xorg: remote.c,v 1.5 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+/*
+ * We use the rstart protocol to restart clients on remote machines.
+ */
+
+#include "xsm.h"
+
+#include <X11/ICE/ICEutil.h>
+
+static char *format_rstart_env();
+
+extern IceAuthDataEntry *authDataEntries;
+extern int numTransports;
+extern void fprintfhex ();
+
+
+void
+remote_start (restart_protocol, restart_machine, program, args, cwd, env,
+ non_local_display_env, non_local_session_env)
+
+char *restart_protocol;
+char *restart_machine;
+char *program;
+char **args;
+char *cwd;
+char **env;
+char *non_local_display_env;
+char *non_local_session_env;
+
+{
+ FILE *fp;
+ int pipefd[2];
+ char msg[256];
+ int i;
+
+ if (strcmp (restart_protocol, "rstart-rsh") != 0)
+ {
+ if (verbose)
+ printf ("Only rstart-rsh remote execution protocol supported.\n");
+ return;
+ }
+
+ if (!restart_machine)
+ {
+ if (verbose)
+ printf ("Bad remote machine specified for remote execute.\n");
+ return;
+ }
+
+ if (verbose)
+ printf ("Attempting to restart remote client on %s\n",
+ restart_machine);
+
+ if (pipe (pipefd) < 0)
+ {
+ sprintf (msg, "%s: pipe() error during remote start of %s",
+ Argv[0], program);
+ add_log_text (msg);
+ perror (msg);
+ }
+ else
+ {
+ switch(fork())
+ {
+ case -1:
+
+ sprintf (msg, "%s: fork() error during remote start of %s",
+ Argv[0], program);
+ add_log_text (msg);
+ perror (msg);
+ break;
+
+ case 0: /* kid */
+
+ close (pipefd[1]);
+ close (0);
+ dup (pipefd[0]);
+ close (pipefd[0]);
+
+ execlp (RSHCMD, restart_machine, "rstartd", (char *) 0);
+
+ sprintf (msg,
+ "%s: execlp() of rstartd failed for remote start of %s",
+ Argv[0], program);
+ perror (msg);
+ /*
+ * TODO : We would like to send this log information to the
+ * log window in the parent. This would require using the
+ * pipe between the parent and child. The child would
+ * set close-on-exec. If the exec succeeds, the pipe will
+ * be closed. If it fails, the child can write a message
+ * to the parent.
+ */
+ _exit(255);
+
+ default: /* parent */
+
+ close (pipefd[0]);
+ fp = (FILE *) fdopen (pipefd[1], "w");
+
+ fprintf (fp, "CONTEXT X\n");
+ fprintf (fp, "DIR %s\n", cwd);
+ fprintf (fp, "DETACH\n");
+
+ if (env)
+ {
+ /*
+ * The application saved its environment.
+ */
+
+ for (i = 0; env[i]; i++)
+ {
+ /*
+ * rstart requires that any spaces, backslashes, or
+ * non-printable characters inside of a string be
+ * represented by octal escape sequences.
+ */
+
+ char *temp = format_rstart_env (env[i]);
+ fprintf (fp, "MISC X %s\n", temp);
+ if (temp != env[i])
+ XtFree (temp);
+ }
+ }
+ else
+ {
+ /*
+ * The application did not save its environment.
+ * The default PATH set up by rstart may not contain
+ * the program we want to restart. We play it safe
+ * and pass xsm's PATH. This will most likely contain
+ * the path we need.
+ */
+
+ char *path = (char *) getenv ("PATH");
+
+ if (path)
+ fprintf (fp, "MISC X PATH=%s\n", path);
+ }
+
+ fprintf (fp, "MISC X %s\n", non_local_display_env);
+ fprintf (fp, "MISC X %s\n", non_local_session_env);
+
+ /*
+ * Pass the authentication data.
+ * Each transport has auth data for ICE and XSMP.
+ * Don't pass local auth data.
+ */
+
+ for (i = 0; i < numTransports * 2; i++)
+ {
+ if (Strstr (authDataEntries[i].network_id, "local/"))
+ continue;
+
+ fprintf (fp, "AUTH ICE %s \"\" %s %s ",
+ authDataEntries[i].protocol_name,
+ authDataEntries[i].network_id,
+ authDataEntries[i].auth_name);
+
+ fprintfhex (fp,
+ authDataEntries[i].auth_data_length,
+ authDataEntries[i].auth_data);
+
+ fprintf (fp, "\n");
+ }
+
+ /*
+ * And execute the program
+ */
+
+ fprintf (fp, "EXEC %s %s", program, program);
+ for (i = 1; args[i]; i++)
+ fprintf (fp, " %s", args[i]);
+ fprintf (fp, "\n\n");
+ fclose (fp);
+ break;
+ }
+ }
+}
+
+
+
+/*
+ * rstart requires that any spaces/backslashes/non-printable characters
+ * inside of a string be represented by octal escape sequences.
+ */
+
+static char *
+format_rstart_env (str)
+
+char *str;
+
+{
+ int escape_count = 0, i;
+ char *temp = str;
+
+ while (*temp != '\0')
+ {
+ if (!isgraph (*temp) || *temp == '\\')
+ escape_count++;
+ temp++;
+ }
+
+ if (escape_count == 0)
+ return (str);
+ else
+ {
+ int len = strlen (str) + 1 + (escape_count * 3);
+ char *ret = (char *) XtMalloc (len);
+ char *ptr = ret;
+
+ temp = str;
+ while (*temp != '\0')
+ {
+ if (!isgraph (*temp) || *temp == '\\')
+ {
+ char octal[3];
+ sprintf (octal, "%o", *temp);
+ *(ptr++) = '\\';
+ for (i = 0; i < (3 - (int) strlen (octal)); i++)
+ *(ptr++) = '0';
+ strcpy (ptr, octal);
+ ptr += strlen (octal);
+ }
+ else
+ *(ptr++) = *temp;
+
+ temp++;
+ }
+
+ *ptr = '\0';
+ return (ret);
+ }
+}
diff --git a/restart.c b/restart.c
new file mode 100644
index 0000000..daff7eb
--- /dev/null
+++ b/restart.c
@@ -0,0 +1,624 @@
+/* $Xorg: restart.c,v 1.5 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include "log.h"
+
+extern void remote_start ();
+
+
+/*
+ * Until XSMP provides a better way to know which clients are "managers",
+ * we have to hard code the list.
+ */
+
+Bool
+CheckIsManager (program)
+
+char *program;
+
+{
+ return (strcmp (program, "twm") == 0);
+}
+
+
+
+/*
+ * GetRestartInfo() will determine which method should be used to
+ * restart a client.
+ *
+ * 'restart_service_prop' is a property set by the client, or NULL.
+ * The format is "remote_start_protocol/remote_start_data". An
+ * example is "rstart-rsh/hostname". This is a non-standard property,
+ * which is the whole reason we need this function in order to determine
+ * the restart method. The proxy uses this property to over-ride the
+ * 'client_host_name' from the ICE connection (the proxy is connected to
+ * the SM via a local connection, but the proxy may be acting as a proxy
+ * for a remote client).
+ *
+ * 'client_host_name' is the connection info obtained from the ICE
+ * connection. It's format is "transport/host_info". An example
+ * is "tcp/machine:port".
+ *
+ * If 'restart_service_prop' is NULL, we use 'client_host_name' to
+ * determine the restart method. If the transport is "local", we
+ * do a local restart. Otherwise, we use the default "rstart-rsh" method.
+ *
+ * If 'restart_service_prop' is non-NULL, we check the remote_start_protocol
+ * field. "local" means a local restart. Currently, the only remote
+ * protocol we recognize is "rstart-rsh". If the remote protocol is
+ * "rstart-rsh" but the hostname in the 'restart_service_prop' matches
+ * 'client_host_name', we do a local restart.
+ *
+ * On return, set the run_local flag, restart_protocol and restart_machine.
+ */
+
+void
+GetRestartInfo (restart_service_prop, client_host_name,
+ run_local, restart_protocol, restart_machine)
+
+char *restart_service_prop;
+char *client_host_name;
+Bool *run_local;
+char **restart_protocol;
+char **restart_machine;
+
+{
+ char hostnamebuf[80];
+ char *temp;
+
+ *run_local = False;
+ *restart_protocol = NULL;
+ *restart_machine = NULL;
+
+ if (restart_service_prop)
+ {
+ gethostname (hostnamebuf, sizeof hostnamebuf);
+
+ if ((temp = (char *) strchr (
+ restart_service_prop, '/')) == NULL)
+ {
+ *restart_protocol = (char *) XtNewString ("rstart-rsh");
+ *restart_machine = (char *) XtNewString (restart_service_prop);
+ }
+ else
+ {
+ *restart_protocol = (char *) XtNewString (restart_service_prop);
+ (*restart_protocol)[temp - restart_service_prop] = '\0';
+ *restart_machine = (char *) XtNewString (temp + 1);
+ }
+
+ if (strcmp (*restart_machine, hostnamebuf) == 0 ||
+ strcmp (*restart_protocol, "local") == 0)
+ {
+ *run_local = True;
+ }
+ }
+ else
+ {
+ if (strncmp (client_host_name, "tcp/", 4) != 0 &&
+ strncmp (client_host_name, "decnet/", 7) != 0)
+ {
+ *run_local = True;
+ }
+ else
+ {
+ *restart_protocol = (char *) XtNewString ("rstart-rsh");
+
+ if ((temp = (char *) strchr (
+ client_host_name, '/')) == NULL)
+ {
+ *restart_machine = (char *) XtNewString (client_host_name);
+ }
+ else
+ {
+ *restart_machine = (char *) XtNewString (temp + 1);
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Restart clients. The flag indicates RESTART_MANAGERS or
+ * RESTART_REST_OF_CLIENTS.
+ */
+
+Status
+Restart (flag)
+
+int flag;
+
+{
+ List *cl, *pl, *vl;
+ PendingClient *c;
+ Prop *prop;
+ char *cwd;
+ char *program;
+ char **args;
+ char **env;
+ char **pp;
+ int cnt;
+ extern char **environ;
+ char *p;
+ char *restart_service_prop;
+ char *restart_protocol;
+ char *restart_machine;
+ Bool run_local;
+ Bool is_manager;
+ Bool ran_manager = 0;
+
+ for(cl = ListFirst(PendingList); cl; cl = ListNext(cl)) {
+ c = (PendingClient *)cl->thing;
+
+ if (verbose) {
+ printf("Restarting id '%s'...\n", c->clientId);
+ printf("Host = %s\n", c->clientHostname);
+ }
+ cwd = ".";
+ env = NULL;
+ program=NULL;
+ args=NULL;
+ restart_service_prop=NULL;
+
+ is_manager = 0;
+
+ for(pl = ListFirst(c->props); pl; pl = ListNext(pl)) {
+ prop = (Prop *)pl->thing;
+ if(!strcmp(prop->name, SmProgram)) {
+ vl = ListFirst(prop->values);
+ if(vl) program = ((PropValue *)vl->thing)->value;
+ if (CheckIsManager (program))
+ is_manager = 1;
+ } else if(!strcmp(prop->name, SmCurrentDirectory)) {
+ vl = ListFirst(prop->values);
+ if(vl) cwd = ((PropValue *)vl->thing)->value;
+ } else if(!strcmp(prop->name, "_XC_RestartService")) {
+ vl = ListFirst(prop->values);
+ if(vl) restart_service_prop =
+ ((PropValue *)vl->thing)->value;
+ } else if(!strcmp(prop->name, SmRestartCommand)) {
+ cnt = ListCount(prop->values);
+ args = (char **)XtMalloc((cnt+1) * sizeof(char *));
+ pp = args;
+ for(vl = ListFirst(prop->values); vl; vl = ListNext(vl)) {
+ *pp++ = ((PropValue *)vl->thing)->value;
+ }
+ *pp = NULL;
+ } else if(!strcmp(prop->name, SmEnvironment)) {
+ cnt = ListCount(prop->values);
+ env = (char **)XtMalloc((cnt+3+1) * sizeof(char *));
+ pp = env;
+ for(vl = ListFirst(prop->values); vl; vl = ListNext(vl)) {
+ p = ((PropValue *)vl->thing)->value;
+ if((display_env && strbw(p, "DISPLAY="))
+ || (session_env && strbw(p, "SESSION_MANAGER="))
+ || (audio_env && strbw(p, "AUDIOSERVER="))
+ ) continue;
+ *pp++ = p;
+ }
+ if(display_env) *pp++ = display_env;
+ if(session_env) *pp++ = session_env;
+ if(audio_env) *pp++ = audio_env;
+ *pp = NULL;
+ }
+ }
+
+ if(program && args) {
+ char logtext[256];
+
+ if ((flag == RESTART_MANAGERS && !is_manager) ||
+ (flag == RESTART_REST_OF_CLIENTS && is_manager))
+ {
+ if(args) XtFree((char *)args);
+ if(env) XtFree((char *)env);
+ continue;
+ }
+
+ if (flag == RESTART_MANAGERS && is_manager)
+ ran_manager = 1;
+
+ if (verbose) {
+ printf("\t%s\n", program);
+ printf("\t");
+ for(pp = args; *pp; pp++) printf("%s ", *pp);
+ printf("\n");
+ }
+
+ GetRestartInfo (restart_service_prop, c->clientHostname,
+ &run_local, &restart_protocol, &restart_machine);
+
+ if (run_local)
+ {
+ /*
+ * The client is being restarted on the local machine.
+ */
+
+ sprintf (logtext, "Restarting locally : ");
+ for (pp = args; *pp; pp++)
+ {
+ strcat (logtext, *pp);
+ strcat (logtext, " ");
+ }
+
+ strcat (logtext, "\n");
+ add_log_text (logtext);
+
+ switch(fork()) {
+ case -1:
+ sprintf (logtext,
+ "%s: Can't fork() %s", Argv[0], program);
+ add_log_text (logtext);
+ perror (logtext);
+ break;
+ case 0: /* kid */
+ chdir(cwd);
+ if(env) environ = env;
+ execvp(program, args);
+ sprintf (logtext, "%s: Can't execvp() %s",
+ Argv[0], program);
+ perror (logtext);
+ /*
+ * TODO : We would like to send this log information to the
+ * log window in the parent. This would require opening
+ * a pipe between the parent and child. The child would
+ * set close-on-exec. If the exec succeeds, the pipe will
+ * be closed. If it fails, the child can write a message
+ * to the parent.
+ */
+ _exit(255);
+ default: /* parent */
+ break;
+ }
+ }
+ else if (!remote_allowed)
+ {
+ fprintf(stderr,
+ "Can't remote start client ID '%s': only local supported\n",
+ c->clientId);
+ }
+ else
+ {
+ /*
+ * The client is being restarted on a remote machine.
+ */
+
+ sprintf (logtext, "Restarting remotely on %s : ",
+ restart_machine);
+ for (pp = args; *pp; pp++)
+ {
+ strcat (logtext, *pp);
+ strcat (logtext, " ");
+ }
+ strcat (logtext, "\n");
+ add_log_text (logtext);
+
+ remote_start (restart_protocol, restart_machine,
+ program, args, cwd, env,
+ non_local_display_env, non_local_session_env);
+ }
+
+ if (restart_protocol)
+ XtFree (restart_protocol);
+
+ if (restart_machine)
+ XtFree (restart_machine);
+
+ } else {
+ fprintf(stderr, "Can't restart ID '%s': no program or no args\n",
+ c->clientId);
+ }
+ if(args) XtFree((char *)args);
+ if(env) XtFree((char *)env);
+ }
+
+ if (flag == RESTART_MANAGERS && !ran_manager)
+ return (0);
+ else
+ return (1);
+}
+
+
+
+/*
+ * Clone a client
+ */
+
+void
+Clone (client, useSavedState)
+
+ClientRec *client;
+Bool useSavedState;
+
+{
+ char *cwd;
+ char *program;
+ char **args;
+ char **env;
+ char **pp;
+ extern char **environ;
+ char *p;
+ char *restart_service_prop;
+ char *restart_protocol;
+ char *restart_machine;
+ Bool run_local;
+ List *pl, *pj;
+
+ if (verbose)
+ {
+ printf ("Cloning id '%s', useSavedState = %d...\n",
+ client->clientId, useSavedState);
+ printf ("Host = %s\n", client->clientHostname);
+ }
+
+ cwd = ".";
+ env = NULL;
+ program = NULL;
+ args = NULL;
+ restart_service_prop = NULL;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+ List *vl = ListFirst (pprop->values);
+ PropValue *pval = (PropValue *) vl->thing;
+
+ if (strcmp (pprop->name, SmProgram) == 0)
+ program = (char *) pval->value;
+ else if (strcmp (pprop->name, SmCurrentDirectory) == 0)
+ cwd = (char *) pval->value;
+ else if (strcmp (pprop->name, "_XC_RestartService") == 0)
+ restart_service_prop = (char *) pval->value;
+ else if (
+ (!useSavedState && strcmp (pprop->name, SmCloneCommand) == 0) ||
+ (useSavedState && strcmp (pprop->name, SmRestartCommand) == 0))
+ {
+ args = (char **) XtMalloc (
+ (ListCount (pprop->values) + 1) * sizeof (char *));
+
+ pp = args;
+
+ for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
+ {
+ pval = (PropValue *) pj->thing;
+ *pp++ = (char *) pval->value;
+ }
+ *pp = NULL;
+ }
+ else if (strcmp (pprop->name, SmEnvironment) == 0)
+ {
+ env = (char **) XtMalloc (
+ (ListCount (pprop->values) + 3 + 1) * sizeof (char *));
+ pp = env;
+
+ for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
+ {
+ pval = (PropValue *) pj->thing;
+ p = (char *) pval->value;
+
+ if ((display_env && strbw (p, "DISPLAY="))
+ || (session_env && strbw (p, "SESSION_MANAGER="))
+ || (audio_env && strbw (p, "AUDIOSERVER=")))
+ continue;
+
+ *pp++ = p;
+ }
+
+ if (display_env)
+ *pp++ = display_env;
+ if (session_env)
+ *pp++ = session_env;
+ if (audio_env)
+ *pp++ = audio_env;
+
+ *pp = NULL;
+ }
+ }
+
+ if (program && args)
+ {
+ if (verbose)
+ {
+ printf("\t%s\n", program);
+ printf("\t");
+ for (pp = args; *pp; pp++)
+ printf ("%s ", *pp);
+ printf("\n");
+ }
+
+ GetRestartInfo (restart_service_prop, client->clientHostname,
+ &run_local, &restart_protocol, &restart_machine);
+
+ if (run_local)
+ {
+ /*
+ * The client is being cloned on the local machine.
+ */
+
+ char msg[256];
+
+ switch(fork()) {
+ case -1:
+ sprintf (msg, "%s: Can't fork() %s", Argv[0], program);
+ add_log_text (msg);
+ perror (msg);
+ break;
+ case 0: /* kid */
+ chdir(cwd);
+ if(env) environ = env;
+ execvp(program, args);
+ sprintf (msg, "%s: Can't execvp() %s", Argv[0], program);
+ perror (msg);
+ /*
+ * TODO : We would like to send this log information to the
+ * log window in the parent. This would require opening
+ * a pipe between the parent and child. The child would
+ * set close-on-exec. If the exec succeeds, the pipe will
+ * be closed. If it fails, the child can write a message
+ * to the parent.
+ */
+ _exit(255);
+ default: /* parent */
+ break;
+ }
+ }
+ else if (!remote_allowed)
+ {
+ fprintf(stderr,
+ "Can't remote clone client ID '%s': only local supported\n",
+ client->clientId);
+ }
+ else
+ {
+ /*
+ * The client is being cloned on a remote machine.
+ */
+
+ remote_start (restart_protocol, restart_machine,
+ program, args, cwd, env,
+ non_local_display_env, non_local_session_env);
+ }
+
+ if (restart_protocol)
+ XtFree (restart_protocol);
+
+ if (restart_machine)
+ XtFree (restart_machine);
+
+ }
+ else
+ {
+#ifdef XKB
+ XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_Failure);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+
+ fprintf(stderr, "Can't restart ID '%s': no program or no args\n",
+ client->clientId);
+ }
+
+ if (args)
+ XtFree ((char *)args);
+ if (env)
+ XtFree ((char *)env);
+}
+
+
+
+void
+StartDefaultApps ()
+
+{
+ FILE *f;
+ char *buf, *p, *home, filename[128];
+ int buflen, len;
+
+ /*
+ * First try ~/.xsmstartup, then system.xsm
+ */
+
+ home = (char *) getenv ("HOME");
+ if (!home)
+ home = ".";
+ sprintf (filename, "%s/.xsmstartup", home);
+
+ f = fopen (filename, "r");
+
+ if (!f)
+ {
+ f = fopen (SYSTEM_INIT_FILE, "r");
+ if (!f)
+ {
+ printf ("Could not find default apps file. Make sure you did\n");
+ printf ("a 'make install' in the xsm build directory.\n");
+ exit (1);
+ }
+ }
+
+ buf = NULL;
+ buflen = 0;
+
+ while (getline(&buf, &buflen, f))
+ {
+ char logtext[256];
+
+ if (buf[0] == '!')
+ continue; /* a comment */
+
+ if ((p = strchr (buf, '\n')))
+ *p = '\0';
+
+ sprintf (logtext, "Starting locally : %s\n", buf);
+ add_log_text (logtext);
+
+ len = strlen (buf);
+
+ buf[len] = '&';
+ buf[len+1] = '\0';
+
+ /* let the shell parse the stupid args */
+
+ execute_system_command (buf);
+ }
+
+ if (buf)
+ free (buf);
+}
+
+
+
+void
+StartNonSessionAwareApps ()
+
+{
+ char logtext[256];
+ int i;
+
+ for (i = 0; i < non_session_aware_count; i++)
+ {
+ /*
+ * Let the shell parse the stupid args. We need to add an "&"
+ * at the end of the command. We previously allocated an extra
+ * byte for this.
+ */
+
+ sprintf (logtext, "Restarting locally : %s\n",
+ non_session_aware_clients[i]);
+ add_log_text (logtext);
+
+ strcat (non_session_aware_clients[i], "&");
+ execute_system_command (non_session_aware_clients[i]);
+ free ((char *) non_session_aware_clients[i]);
+ }
+
+ if (non_session_aware_clients)
+ {
+ free ((char *) non_session_aware_clients);
+ non_session_aware_clients = NULL;
+ }
+}
diff --git a/restart.h b/restart.h
new file mode 100644
index 0000000..6dcf553
--- /dev/null
+++ b/restart.h
@@ -0,0 +1,30 @@
+/* $Xorg: restart.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void StartDefaultApps ();
+extern void StartNonSessionAwareApps ();
+extern Status Restart ();
+extern void Clone ();
diff --git a/save.c b/save.c
new file mode 100644
index 0000000..36bb8dc
--- /dev/null
+++ b/save.c
@@ -0,0 +1,1529 @@
+/* $Xorg: save.c,v 1.5 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+#include "save.h"
+#include "saveutil.h"
+#include "popup.h"
+
+#include <X11/Shell.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Command.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/AsciiText.h>
+
+
+Widget savePopup;
+Widget saveForm;
+Widget saveMessageLabel;
+Widget saveName;
+Widget saveTypeLabel;
+Widget saveTypeGlobal;
+Widget saveTypeLocal;
+Widget saveTypeBoth;
+Widget interactStyleLabel;
+Widget interactStyleNone;
+Widget interactStyleErrors;
+Widget interactStyleAny;
+Widget saveOkButton;
+Widget helpSaveButton;
+Widget saveCancelButton;
+Widget helpPopup;
+Widget helpForm;
+Widget helpSaveText;
+Widget helpSaveOkButton;
+Widget nameInUsePopup;
+Widget nameInUseForm;
+Widget nameInUseLabel;
+Widget nameInUseOverwriteButton;
+Widget nameInUseCancelButton;
+Widget badSavePopup;
+Widget badSaveForm;
+Widget badSaveLabel;
+Widget badSaveOkButton;
+Widget badSaveCancelButton;
+Widget badSaveListWidget;
+
+extern Widget clientInfoPopup;
+extern Widget clientPropPopup;
+extern Widget nameSessionPopup;
+
+extern int checkpoint_from_signal;
+
+static int saveTypeData[] = {
+ SmSaveLocal,
+ SmSaveGlobal,
+ SmSaveBoth
+};
+
+static int interactStyleData[] = {
+ SmInteractStyleNone,
+ SmInteractStyleErrors,
+ SmInteractStyleAny
+};
+
+static String *failedNames = NULL;
+static int numFailedNames = 0;
+
+static Bool help_visible = False;
+
+static String name_in_use = NULL;
+static Bool name_locked = False;
+
+void SetSaveSensitivity ();
+
+
+static void
+MakeCurrentSession (new_name, name_changed)
+
+String new_name;
+Bool name_changed;
+
+{
+ char title[256];
+ List *cl;
+
+ if (session_name)
+ {
+ /*
+ * In the old session, for any client that was not restarted by the
+ * session manager (previous ID was NULL), if we did not issue a
+ * checkpoint to this client after the initial startup, remove the
+ * client's checkpoint file using the discard command.
+ */
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+
+ if (!client->restarted &&
+ !client->userIssuedCheckpoint &&
+ client->discardCommand)
+ {
+ execute_system_command (client->discardCommand);
+ XtFree (client->discardCommand);
+ client->discardCommand = NULL;
+ }
+ }
+
+ /*
+ * Unlock the old session.
+ */
+
+ if (!need_to_name_session)
+ UnlockSession (session_name);
+ }
+
+ if (name_changed)
+ {
+ if (session_name)
+ XtFree (session_name);
+
+ session_name = XtNewString (new_name);
+ }
+
+ LockSession (session_name, True);
+
+ sprintf (title, "xsm: %s", session_name);
+
+ XtVaSetValues (topLevel,
+ XtNtitle, title,
+ NULL);
+
+ set_session_save_file_name (session_name);
+
+
+ /*
+ * For each client, set the DiscardCommand ptr to NULL.
+ * This is so when we do a checkpoint with the new session
+ * name, we don't wipe out the checkpoint files needed by
+ * the previous session. We also set the userIssuedCheckpoint
+ * flag to false for each client in the new session.
+ */
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+
+ client->userIssuedCheckpoint = False;
+
+ if (client->discardCommand)
+ {
+ XtFree (client->discardCommand);
+ client->discardCommand = NULL;
+ }
+ }
+
+ need_to_name_session = False;
+}
+
+
+
+
+#define NAME_OK 0
+#define NAME_EMPTY 1
+#define NAME_EXISTS 2
+#define NAME_LOCKED 3
+
+static int
+GetSaveName (nameRet)
+
+String *nameRet;
+
+{
+ String new_name = NULL;
+ Bool name_changed;
+
+ /*
+ * Get the name of the session for the save
+ */
+
+ XtVaGetValues (saveName,
+ XtNstring, &new_name,
+ NULL);
+
+ *nameRet = new_name;
+
+ if (!new_name || *new_name == '\0')
+ return (NAME_EMPTY);
+
+ /*
+ * See if this is a new session. If not return.
+ */
+
+ name_changed = !session_name ||
+ (session_name && strcmp (session_name, new_name) != 0);
+
+ if (!need_to_name_session && !name_changed)
+ return (NAME_OK);
+
+
+ /*
+ * Make sure the session name is unique.
+ */
+
+ if (GetSessionNames (&sessionNameCount,
+ &sessionNamesShort, NULL, &sessionsLocked))
+ {
+ int i, no_good = 0, locked = 0;
+
+ for (i = 0; i < sessionNameCount; i++)
+ if (strcmp (new_name, sessionNamesShort[i]) == 0)
+ {
+ no_good = 1;
+ locked = sessionsLocked[i];
+ break;
+ }
+
+ FreeSessionNames (sessionNameCount,
+ sessionNamesShort, NULL, sessionsLocked);
+
+ if (no_good)
+ return (locked ? NAME_LOCKED : NAME_EXISTS);
+ }
+
+ MakeCurrentSession (new_name, name_changed);
+
+ return (NAME_OK);
+}
+
+
+static void
+GetSaveOptions (saveType, interactStyle, fast)
+
+int *saveType;
+int *interactStyle;
+Bool *fast;
+
+{
+ XtPointer ptr;
+
+ if (help_visible)
+ {
+ XtPopdown (helpPopup);
+ help_visible = 0;
+ }
+
+ ptr = XawToggleGetCurrent (saveTypeLocal /* just 1 of the group */);
+ *saveType = *((int *) ptr);
+
+ ptr = XawToggleGetCurrent (interactStyleNone /* just 1 of the group */);
+ *interactStyle = *((int *) ptr);
+
+ *fast = False;
+}
+
+
+
+DoSave (saveType, interactStyle, fast)
+
+int saveType;
+int interactStyle;
+Bool fast;
+
+{
+ ClientRec *client;
+ List *cl;
+ char *_saveType;
+ char *_shutdown;
+ char *_interactStyle;
+
+ if (saveType == SmSaveLocal)
+ _saveType = "Local";
+ else if (saveType == SmSaveGlobal)
+ _saveType = "Global";
+ else
+ _saveType = "Both";
+
+ if (wantShutdown)
+ _shutdown = "True";
+ else
+ _shutdown = "False";
+
+ if (interactStyle == SmInteractStyleNone)
+ _interactStyle = "None";
+ else if (interactStyle == SmInteractStyleErrors)
+ _interactStyle = "Errors";
+ else
+ _interactStyle = "Any";
+
+ SetSaveSensitivity (False);
+
+ saveInProgress = True;
+
+ shutdownCancelled = False;
+ phase2InProgress = False;
+
+ if (ListCount (RunningList) == 0)
+ FinishUpSave ();
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ SmsSaveYourself (client->smsConn,
+ saveType, wantShutdown, interactStyle, fast);
+
+ ListAddLast (WaitForSaveDoneList, (char *) client);
+
+ client->userIssuedCheckpoint = True;
+ client->receivedDiscardCommand = False;
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent SAVE YOURSELF [", client->clientId);
+ printf ("Save Type = %s, Shutdown = %s, ", _saveType, _shutdown);
+ printf ("Interact Style = %s, Fast = False]\n", _interactStyle);
+ }
+ }
+
+ if (verbose)
+ {
+ printf ("\n");
+ printf ("Sent SAVE YOURSELF to all clients. Waiting for\n");
+ printf ("SAVE YOURSELF DONE, INTERACT REQUEST, or\n");
+ printf ("SAVE YOURSELF PHASE 2 REQUEST from each client.\n");
+ printf ("\n");
+ }
+}
+
+
+
+static void
+SaveOkAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (saveOkButton, XtNcallback, NULL);
+}
+
+
+
+static void
+DelSaveWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (saveCancelButton, XtNcallback, NULL);
+}
+
+
+
+static void
+DelNameInUseWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (nameInUseCancelButton, XtNcallback, NULL);
+}
+
+
+
+static void
+DelBadSaveWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ if (XtIsManaged (badSaveCancelButton))
+ XtCallCallbacks (badSaveCancelButton, XtNcallback, NULL);
+ else
+ XtCallCallbacks (badSaveOkButton, XtNcallback, NULL);
+}
+
+
+
+static void
+DelSaveHelpWinAction (w, event, params, num_params)
+
+Widget w;
+XEvent *event;
+String *params;
+Cardinal *num_params;
+
+{
+ XtCallCallbacks (helpSaveOkButton, XtNcallback, NULL);
+}
+
+
+
+static void
+SaveOkXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ String name = NULL;
+ char label[256];
+ int status;
+ static int first_time = 1;
+ int saveType;
+ int interactStyle;
+ Bool fast;
+
+ if ((status = GetSaveName (&name)) != NAME_OK)
+ {
+#ifdef XKB
+ XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
+#else
+ XBell (XtDisplay (topLevel), 0);
+#endif
+
+ if (status == NAME_EXISTS || status == NAME_LOCKED)
+ {
+ name_in_use = name;
+
+ if (status == NAME_LOCKED)
+ {
+ name_locked = True;
+
+ sprintf (label, "Another session by the name '%s' is active.\nChoose another name for the session.", name);
+
+ XtUnmanageChild (nameInUseOverwriteButton);
+
+ XtVaSetValues (nameInUseCancelButton,
+ XtNlabel, "OK",
+ XtNfromHoriz, NULL,
+ NULL);
+ }
+ else
+ {
+ name_locked = False;
+
+ sprintf (label, "Another session by the name '%s' already exists.\nWould you like to overwrite it?", name);
+
+ XtManageChild (nameInUseOverwriteButton);
+
+ XtVaSetValues (nameInUseCancelButton,
+ XtNlabel, "Cancel",
+ XtNfromHoriz, nameInUseOverwriteButton,
+ NULL);
+ }
+
+ XtVaSetValues (nameInUseLabel,
+ XtNlabel, label,
+ NULL);
+
+ XtPopdown (savePopup);
+
+ PopupPopup (mainWindow, nameInUsePopup,
+ True, first_time, 25, 100, "DelNameInUseWinAction()");
+
+ if (first_time)
+ first_time = 0;
+ }
+
+ return;
+ }
+
+ GetSaveOptions (&saveType, &interactStyle, &fast);
+ DoSave (saveType, interactStyle, fast);
+}
+
+
+
+void
+LetClientInteract (cl)
+
+List *cl;
+
+{
+ ClientRec *client = (ClientRec *) cl->thing;
+
+ SmsInteract (client->smsConn);
+
+ ListSearchAndFreeOne (WaitForInteractList, (char *) client);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent INTERACT\n", client->clientId);
+ }
+}
+
+
+
+void
+StartPhase2 ()
+
+{
+ List *cl;
+
+ if (verbose)
+ {
+ printf ("\n");
+ printf ("Starting PHASE 2 of SAVE YOURSELF\n");
+ printf ("\n");
+ }
+
+ for (cl = ListFirst (WaitForPhase2List); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+
+ SmsSaveYourselfPhase2 (client->smsConn);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent SAVE YOURSELF PHASE 2",
+ client->clientId);
+ }
+ }
+
+ ListFreeAllButHead (WaitForPhase2List);
+
+ phase2InProgress = True;
+}
+
+
+void
+FinishUpSave ()
+
+{
+ ClientRec *client;
+ List *cl;
+
+ if (verbose)
+ {
+ printf ("\n");
+ printf ("All clients issued SAVE YOURSELF DONE\n");
+ printf ("\n");
+ }
+
+ saveInProgress = False;
+ phase2InProgress = False;
+
+ /*
+ * Now execute discard commands
+ */
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ if (!client->receivedDiscardCommand)
+ continue;
+
+ if (client->discardCommand)
+ {
+ execute_system_command (client->discardCommand);
+ XtFree (client->discardCommand);
+ client->discardCommand = NULL;
+ }
+
+ if (client->saveDiscardCommand)
+ {
+ client->discardCommand = client->saveDiscardCommand;
+ client->saveDiscardCommand = NULL;
+ }
+ }
+
+
+ /*
+ * Write the save file
+ */
+
+ WriteSave (sm_id);
+
+
+ if (wantShutdown && shutdownCancelled)
+ {
+ shutdownCancelled = False;
+ }
+ else if (wantShutdown)
+ {
+ if (ListCount (RunningList) == 0)
+ EndSession (0);
+
+ shutdownInProgress = True;
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ SmsDie (client->smsConn);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent DIE\n", client->clientId);
+ }
+ }
+ }
+ else
+ {
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ SmsSaveComplete (client->smsConn);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent SAVE COMPLETE\n",
+ client->clientId);
+ }
+ }
+ }
+
+ if (!shutdownInProgress)
+ {
+ XtPopdown (savePopup);
+ SetAllSensitive (1);
+ if (checkpoint_from_signal)
+ checkpoint_from_signal = False;
+ }
+}
+
+
+
+static void
+SaveCancelXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XtPopdown (savePopup);
+
+ if (help_visible)
+ {
+ XtPopdown (helpPopup);
+ help_visible = 0;
+ }
+
+ SetAllSensitive (1);
+}
+
+
+
+/*
+ * Add toggle button
+ */
+
+static Widget
+AddToggle (widgetName, parent, state, radioGroup, radioData,
+ fromHoriz, fromVert)
+
+char *widgetName;
+Widget parent;
+int state;
+Widget radioGroup;
+XtPointer radioData;
+Widget fromHoriz;
+Widget fromVert;
+
+{
+ Widget toggle;
+
+ toggle = XtVaCreateManagedWidget (
+ widgetName, toggleWidgetClass, parent,
+ XtNstate, state,
+ XtNradioGroup, radioGroup,
+ XtNradioData, radioData,
+ XtNfromHoriz, fromHoriz,
+ XtNfromVert, fromVert,
+ NULL);
+
+ return (toggle);
+}
+
+
+
+void
+SetSaveSensitivity (on)
+
+Bool on;
+
+{
+ XtSetSensitive (savePopup, on);
+
+#if 0
+ /*
+ * When we turn of sensitivity in the save dialog, we want to keep
+ * the cancel button sensitive (so the user can cancel in case of
+ * a problem). Unfortunately, we can not turn off the sensitivity on
+ * the save popup, and then just turn on sensitivity for the cancel
+ * button. We must do each widget individually.
+ */
+
+ XtSetSensitive (saveTypeLabel, on);
+ XtSetSensitive (saveTypeGlobal, on);
+ XtSetSensitive (saveTypeLocal, on);
+ XtSetSensitive (saveTypeBoth, on);
+ XtSetSensitive (interactStyleLabel, on);
+ XtSetSensitive (interactStyleNone, on);
+ XtSetSensitive (interactStyleErrors, on);
+ XtSetSensitive (interactStyleAny, on);
+ XtSetSensitive (saveOkButton, on);
+#endif
+}
+
+
+
+void
+SavePopupStructureNotifyXtHandler (w, closure, event, continue_to_dispatch)
+
+Widget w;
+XtPointer closure;
+XEvent *event;
+Boolean *continue_to_dispatch;
+
+{
+ if (event->type == MapNotify)
+ {
+ /*
+ * Now that the Save Dialog is back up, we can do the save.
+ */
+
+ int saveType;
+ int interactStyle;
+ Bool fast;
+
+ if (name_locked)
+ {
+ /* Force shutdown */
+ }
+
+ DeleteSession (name_in_use);
+
+ MakeCurrentSession (name_in_use, True);
+
+ name_in_use = NULL;
+
+ GetSaveOptions (&saveType, &interactStyle, &fast);
+ DoSave (saveType, interactStyle, fast);
+
+ XtRemoveEventHandler (savePopup, StructureNotifyMask, False,
+ SavePopupStructureNotifyXtHandler, NULL);
+ }
+}
+
+
+
+static void
+NameInUseOverwriteXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ if (name_locked)
+ {
+ /* force shutdown not implemented yet */
+
+ return;
+ }
+
+ XtPopdown (nameInUsePopup);
+
+ /*
+ * We want to popup the Save dialog again. In order to avoid a race
+ * condition with the BadSave handler trying to pop down the Save Dialog,
+ * we wait for the MapNotify on the Save dialog, and then do the save.
+ */
+
+ XtAddEventHandler (savePopup, StructureNotifyMask, False,
+ SavePopupStructureNotifyXtHandler, NULL);
+
+ XtPopup (savePopup, XtGrabNone);
+}
+
+
+
+static void
+NameInUseCancelXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XtPopdown (nameInUsePopup);
+ XtPopup (savePopup, XtGrabNone);
+
+ name_in_use = NULL;
+}
+
+
+
+static void
+BadSaveOkXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ ListFreeAllButHead (FailedSaveList);
+ XtPopdown (badSavePopup);
+ FinishUpSave ();
+}
+
+
+
+static void
+BadSaveCancelXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ ListFreeAllButHead (FailedSaveList);
+ XtPopdown (badSavePopup);
+
+ if (wantShutdown)
+ {
+ List *cl;
+
+ shutdownCancelled = True;
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+
+ SmsShutdownCancelled (client->smsConn);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent SHUTDOWN CANCELLED\n",
+ client->clientId);
+ }
+ }
+ }
+
+ FinishUpSave ();
+}
+
+
+
+static void
+BadSaveListXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+
+}
+
+
+
+static void
+HelpSaveXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ static int first_time = 1;
+
+ if (help_visible)
+ {
+ /* Make sure it is visible */
+
+ XMapRaised (XtDisplay (topLevel), XtWindow (helpPopup));
+ }
+ else
+ {
+ PopupPopup (savePopup, helpPopup,
+ True, first_time, 50, 50, "DelSaveHelpWinAction()");
+
+ help_visible = 1;
+
+ if (first_time)
+ first_time = 0;
+ }
+}
+
+
+
+static void
+HelpSaveOkXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ XtPopdown (helpPopup);
+ help_visible = 0;
+}
+
+
+
+void
+create_save_popup ()
+
+{
+ XtTranslations translations;
+
+ static XtActionsRec actions[] = {
+ {"SaveOkAction", SaveOkAction},
+ {"DelSaveWinAction", DelSaveWinAction},
+ {"DelNameInUseWinAction", DelNameInUseWinAction},
+ {"DelBadSaveWinAction", DelBadSaveWinAction},
+ {"DelSaveHelpWinAction", DelSaveHelpWinAction}
+ };
+
+
+ /*
+ * Pop up for Save Yourself button.
+ */
+
+ savePopup = XtVaCreatePopupShell (
+ "savePopup", transientShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+ saveForm = XtCreateManagedWidget (
+ "saveForm", formWidgetClass, savePopup, NULL, 0);
+
+ saveMessageLabel = XtVaCreateManagedWidget (
+ "saveMessageLabel", labelWidgetClass, saveForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNborderWidth, 0,
+ NULL);
+
+ saveName = XtVaCreateManagedWidget (
+ "saveName", asciiTextWidgetClass, saveForm,
+ XtNfromVert, NULL,
+ XtNeditType, XawtextEdit,
+ XtNresizable, True,
+ XtNresize, XawtextResizeWidth,
+ NULL);
+
+ saveTypeLabel = XtVaCreateManagedWidget (
+ "saveTypeLabel", labelWidgetClass, saveForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, saveMessageLabel,
+ XtNborderWidth, 0,
+ XtNvertDistance, 20,
+ NULL);
+
+ saveTypeLocal = AddToggle (
+ "saveTypeLocal", /* widgetName */
+ saveForm, /* parent */
+ 0, /* state */
+ NULL, /* radioGroup */
+ (XtPointer) &saveTypeData[0], /* radioData */
+ saveTypeLabel, /* fromHoriz */
+ saveMessageLabel /* fromVert */
+ );
+
+ saveTypeGlobal = AddToggle (
+ "saveTypeGlobal", /* widgetName */
+ saveForm, /* parent */
+ 0, /* state */
+ saveTypeLocal, /* radioGroup */
+ (XtPointer) &saveTypeData[1], /* radioData */
+ saveTypeLocal, /* fromHoriz */
+ saveMessageLabel /* fromVert */
+ );
+
+ saveTypeBoth = AddToggle (
+ "saveTypeBoth", /* widgetName */
+ saveForm, /* parent */
+ 1, /* state */
+ saveTypeLocal, /* radioGroup */
+ (XtPointer) &saveTypeData[2], /* radioData */
+ saveTypeGlobal, /* fromHoriz */
+ saveMessageLabel /* fromVert */
+ );
+
+
+ XtVaSetValues (saveName, XtNfromHoriz, saveTypeLabel, NULL);
+ XtVaSetValues (saveTypeLocal, XtNvertDistance, 20, NULL);
+ XtVaSetValues (saveTypeGlobal, XtNvertDistance, 20, NULL);
+ XtVaSetValues (saveTypeBoth, XtNvertDistance, 20, NULL);
+
+ interactStyleLabel = XtVaCreateManagedWidget (
+ "interactStyleLabel", labelWidgetClass, saveForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, saveTypeLabel,
+ XtNborderWidth, 0,
+ NULL);
+
+ interactStyleNone = AddToggle (
+ "interactStyleNone", /* widgetName */
+ saveForm, /* parent */
+ 0, /* state */
+ NULL, /* radioGroup */
+ (XtPointer) &interactStyleData[0], /* radioData */
+ saveTypeLabel, /* fromHoriz */
+ saveTypeLabel /* fromVert */
+ );
+
+ interactStyleErrors = AddToggle (
+ "interactStyleErrors", /* widgetName */
+ saveForm, /* parent */
+ 0, /* state */
+ interactStyleNone, /* radioGroup */
+ (XtPointer) &interactStyleData[1], /* radioData */
+ interactStyleNone, /* fromHoriz */
+ saveTypeLabel /* fromVert */
+ );
+
+ interactStyleAny = AddToggle (
+ "interactStyleAny", /* widgetName */
+ saveForm, /* parent */
+ 1, /* state */
+ interactStyleNone, /* radioGroup */
+ (XtPointer) &interactStyleData[2], /* radioData */
+ interactStyleErrors, /* fromHoriz */
+ saveTypeLabel /* fromVert */
+ );
+
+
+ saveOkButton = XtVaCreateManagedWidget (
+ "saveOkButton", commandWidgetClass, saveForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, interactStyleLabel,
+ XtNvertDistance, 20,
+ XtNresizable, True,
+ NULL);
+
+ XtAddCallback (saveOkButton, XtNcallback, SaveOkXtProc, 0);
+
+
+ helpSaveButton = XtVaCreateManagedWidget (
+ "helpSaveButton", commandWidgetClass, saveForm,
+ XtNfromHoriz, saveOkButton,
+ XtNfromVert, interactStyleLabel,
+ XtNvertDistance, 20,
+ NULL);
+
+ XtAddCallback (helpSaveButton, XtNcallback, HelpSaveXtProc, 0);
+
+
+ saveCancelButton = XtVaCreateManagedWidget (
+ "saveCancelButton", commandWidgetClass, saveForm,
+ XtNfromHoriz, helpSaveButton,
+ XtNfromVert, interactStyleLabel,
+ XtNvertDistance, 20,
+ NULL);
+
+ XtAddCallback (saveCancelButton, XtNcallback, SaveCancelXtProc, 0);
+
+ XtSetKeyboardFocus (saveForm, saveName);
+
+ XtAppAddActions (appContext, actions, XtNumber (actions));
+
+ translations = XtParseTranslationTable
+ ("<Key>Return: SaveOkAction()\n");
+ XtOverrideTranslations(saveName, translations);
+
+ XtInstallAllAccelerators (saveForm, saveForm);
+
+
+ /*
+ * Pop up when user tries to save the session under an
+ * already used name.
+ */
+
+ nameInUsePopup = XtVaCreatePopupShell (
+ "nameInUsePopup", transientShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+
+ nameInUseForm = XtVaCreateManagedWidget (
+ "nameInUseForm", formWidgetClass, nameInUsePopup,
+ NULL);
+
+ nameInUseLabel = XtVaCreateManagedWidget (
+ "nameInUseLabel", labelWidgetClass, nameInUseForm,
+ XtNresizable, True,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNborderWidth, 0,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ NULL);
+
+ nameInUseOverwriteButton = XtVaCreateManagedWidget (
+ "nameInUseOverwriteButton", commandWidgetClass, nameInUseForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, nameInUseLabel,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ XtAddCallback (nameInUseOverwriteButton, XtNcallback,
+ NameInUseOverwriteXtProc, 0);
+
+
+ nameInUseCancelButton = XtVaCreateManagedWidget (
+ "nameInUseCancelButton", commandWidgetClass, nameInUseForm,
+ XtNresizable, True,
+ XtNfromHoriz, nameInUseOverwriteButton,
+ XtNfromVert, nameInUseLabel,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ XtAddCallback (nameInUseCancelButton, XtNcallback,
+ NameInUseCancelXtProc, 0);
+
+
+ /*
+ * Pop up for help.
+ */
+
+ helpPopup = XtVaCreatePopupShell (
+ "helpPopup", transientShellWidgetClass, topLevel,
+ NULL);
+
+
+ helpForm = XtVaCreateManagedWidget (
+ "helpForm", formWidgetClass, helpPopup,
+ NULL);
+
+ helpSaveText = XtVaCreateManagedWidget (
+ "helpSaveText", labelWidgetClass, helpForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ NULL);
+
+ helpSaveOkButton = XtVaCreateManagedWidget (
+ "helpSaveOkButton", commandWidgetClass, helpForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, helpSaveText,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ XtNvertDistance, 20,
+ NULL);
+
+ XtAddCallback (helpSaveOkButton, XtNcallback,
+ HelpSaveOkXtProc, 0);
+
+
+ /*
+ * Pop up when not all clients returned SaveSuccess
+ */
+
+ badSavePopup = XtVaCreatePopupShell (
+ "badSavePopup", transientShellWidgetClass, topLevel,
+ XtNallowShellResize, True,
+ NULL);
+
+
+ badSaveForm = XtVaCreateManagedWidget (
+ "badSaveForm", formWidgetClass, badSavePopup,
+ NULL);
+
+ badSaveLabel = XtVaCreateManagedWidget (
+ "badSaveLabel", labelWidgetClass, badSaveForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, NULL,
+ XtNborderWidth, 0,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainTop,
+ NULL);
+
+ badSaveListWidget = XtVaCreateManagedWidget (
+ "badSaveListWidget", listWidgetClass, badSaveForm,
+ XtNresizable, True,
+ XtNdefaultColumns, 1,
+ XtNforceColumns, True,
+ XtNfromHoriz, NULL,
+ XtNfromVert, badSaveLabel,
+ XtNtop, XawChainTop,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ XtAddCallback (badSaveListWidget, XtNcallback, BadSaveListXtProc, 0);
+
+ badSaveOkButton = XtVaCreateManagedWidget (
+ "badSaveOkButton", commandWidgetClass, badSaveForm,
+ XtNfromHoriz, NULL,
+ XtNfromVert, badSaveListWidget,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ XtAddCallback (badSaveOkButton, XtNcallback, BadSaveOkXtProc, 0);
+
+
+ badSaveCancelButton = XtVaCreateManagedWidget (
+ "badSaveCancelButton", commandWidgetClass, badSaveForm,
+ XtNfromHoriz, badSaveOkButton,
+ XtNfromVert, badSaveListWidget,
+ XtNtop, XawChainBottom,
+ XtNbottom, XawChainBottom,
+ NULL);
+
+ XtAddCallback (badSaveCancelButton, XtNcallback, BadSaveCancelXtProc, 0);
+
+ XtInstallAllAccelerators (badSaveForm, badSaveForm);
+}
+
+
+
+void
+PopupSaveDialog ()
+
+{
+ static int first_time = 1;
+
+ XtSetSensitive (mainWindow, 0);
+ XtSetSensitive (clientInfoPopup, 0);
+ XtSetSensitive (clientPropPopup, 0);
+
+ XawToggleSetCurrent (saveTypeBoth,
+ (XtPointer) &saveTypeData[2]);
+ XawToggleSetCurrent (interactStyleAny,
+ (XtPointer) &interactStyleData[2]);
+
+ XtVaSetValues (savePopup,
+ XtNtitle, wantShutdown ? "Shutdown" : "Checkpoint",
+ NULL);
+
+ XtVaSetValues (saveName,
+ XtNstring, need_to_name_session ? "" : session_name,
+ NULL);
+
+ XtVaSetValues (saveOkButton,
+ XtNlabel, wantShutdown ? "Shutdown" : "Checkpoint",
+ NULL);
+
+ PopupPopup (mainWindow, savePopup,
+ True, first_time, 25, 100, "DelSaveWinAction()");
+
+ if (first_time)
+ first_time = 0;
+}
+
+
+
+
+void
+CheckPointXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ wantShutdown = False;
+ PopupSaveDialog ();
+}
+
+
+
+
+void
+ShutdownSaveXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ wantShutdown = True;
+ PopupSaveDialog ();
+}
+
+
+
+void
+PopupBadSave ()
+
+{
+ ClientRec *client;
+ char *progName, *hostname, *tmp1, *tmp2;
+ String clientInfo;
+ int maxlen1, maxlen2;
+ char extraBuf1[80], extraBuf2[80];
+ char *restart_service_prop;
+ List *cl, *pl;
+ int i, k;
+ static int first_time = 1;
+
+ if (failedNames)
+ {
+ /*
+ * Free the previous list of names. Xaw doesn't make a copy of
+ * our list, so we need to keep it around.
+ */
+
+ for (i = 0; i < numFailedNames; i++)
+ XtFree (failedNames[i]);
+
+ XtFree ((char *) failedNames);
+
+ failedNames = NULL;
+ }
+
+ maxlen1 = maxlen2 = 0;
+ numFailedNames = 0;
+
+ for (cl = ListFirst (FailedSaveList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ progName = NULL;
+ restart_service_prop = NULL;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+ List *vl = ListFirst (pprop->values);
+ PropValue *pval = (PropValue *) vl->thing;
+
+ if (strcmp (pprop->name, SmProgram) == 0)
+ {
+ progName = (char *) GetProgramName ((char *) pval->value);
+
+ if ((int) strlen (progName) > maxlen1)
+ maxlen1 = strlen (progName);
+ }
+ else if (strcmp (pprop->name, "_XC_RestartService") == 0)
+ {
+ restart_service_prop = (char *) pval->value;
+ }
+ }
+
+ if (!progName)
+ continue;
+
+ if (restart_service_prop)
+ tmp1 = restart_service_prop;
+ else if (client->clientHostname)
+ tmp1 = client->clientHostname;
+ else
+ continue;
+
+ if ((tmp2 = (char *) strchr (tmp1, '/')) == NULL)
+ hostname = tmp1;
+ else
+ hostname = tmp2 + 1;
+
+ if ((int) strlen (hostname) > maxlen2)
+ maxlen2 = strlen (hostname);
+
+ numFailedNames++;
+ }
+
+ failedNames = (String *) XtMalloc (
+ numFailedNames * sizeof (String));
+
+ i = 0;
+ for (cl = ListFirst (FailedSaveList); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+ int extra1, extra2;
+
+ progName = NULL;
+ restart_service_prop = NULL;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+ List *vl = ListFirst (pprop->values);
+ PropValue *pval = (PropValue *) vl->thing;
+
+ if (strcmp (pprop->name, SmProgram) == 0)
+ {
+ progName = (char *) GetProgramName ((char *) pval->value);
+ }
+ else if (strcmp (pprop->name, "_XC_RestartService") == 0)
+ {
+ restart_service_prop = (char *) pval->value;
+ }
+ }
+
+ if (!progName)
+ continue;
+
+ if (restart_service_prop)
+ tmp1 = restart_service_prop;
+ else if (client->clientHostname)
+ tmp1 = client->clientHostname;
+ else
+ continue;
+
+ if ((tmp2 = (char *) strchr (tmp1, '/')) == NULL)
+ hostname = tmp1;
+ else
+ hostname = tmp2 + 1;
+
+ extra1 = maxlen1 - strlen (progName) + 5;
+ extra2 = maxlen2 - strlen (hostname);
+
+ clientInfo = (String) XtMalloc (strlen (progName) +
+ extra1 + extra2 + 3 + strlen (hostname) + 1);
+
+ for (k = 0; k < extra1; k++)
+ extraBuf1[k] = ' ';
+ extraBuf1[extra1] = '\0';
+
+ for (k = 0; k < extra2; k++)
+ extraBuf2[k] = ' ';
+ extraBuf2[extra2] = '\0';
+
+ sprintf (clientInfo, "%s%s (%s%s)", progName, extraBuf1,
+ hostname, extraBuf2);
+
+ failedNames[i++] = clientInfo;
+
+ if (client->freeAfterBadSavePopup)
+ {
+ FreeClient (client, True /* free props */);
+ }
+ }
+
+ XawListChange (badSaveListWidget,
+ failedNames, numFailedNames, 0, True);
+
+ XtPopdown (savePopup);
+
+ if (wantShutdown && !shutdownCancelled)
+ XtManageChild (badSaveCancelButton);
+ else
+ XtUnmanageChild (badSaveCancelButton);
+
+ PopupPopup (mainWindow, badSavePopup,
+ True, first_time, 25, 100, "DelBadSaveWinAction()");
+
+ if (first_time)
+ first_time = 0;
+}
+
+
+
+void
+ShutdownDontSaveXtProc (w, client_data, callData)
+
+Widget w;
+XtPointer client_data;
+XtPointer callData;
+
+{
+ List *cl;
+ ClientRec *client;
+
+ if (ListCount (RunningList) == 0)
+ EndSession (0);
+
+ /*
+ * For any client that was not restarted by the session
+ * manager (previous ID was NULL), if we did not issue a
+ * checkpoint to this client, remove the client's checkpoint
+ * file using the discard command.
+ */
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ if (!client->restarted &&
+ !client->userIssuedCheckpoint &&
+ client->discardCommand)
+ {
+ execute_system_command (client->discardCommand);
+ XtFree (client->discardCommand);
+ client->discardCommand = NULL;
+ }
+ }
+
+ shutdownInProgress = True;
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ SmsDie (client->smsConn);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent DIE\n", client->clientId);
+ }
+ }
+}
diff --git a/save.h b/save.h
new file mode 100644
index 0000000..5014137
--- /dev/null
+++ b/save.h
@@ -0,0 +1,35 @@
+/* $Xorg: save.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern void create_save_popup ();
+extern void SetSaveSensitivity ();
+extern void PopupSaveDialog ();
+extern void CheckPointXtProc ();
+extern void ShutdownSaveXtProc ();
+extern void ShutdownDontSaveXtProc ();
+extern void LetClientInteract ();
+extern void StartPhase2 ();
+extern void FinishUpSave ();
diff --git a/saveutil.c b/saveutil.c
new file mode 100644
index 0000000..04ab6bf
--- /dev/null
+++ b/saveutil.c
@@ -0,0 +1,557 @@
+/* $Xorg: saveutil.c,v 1.5 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include "xsm.h"
+
+char session_save_file[PATH_MAX];
+Bool getline();
+
+extern Widget manualRestartCommands;
+
+
+void
+set_session_save_file_name (session_name)
+
+char *session_name;
+
+{
+ char *p;
+
+ p = (char *) getenv ("SM_SAVE_DIR");
+ if (!p)
+ {
+ p = (char *) getenv ("HOME");
+ if (!p)
+ p = ".";
+ }
+
+ strcpy (session_save_file, p);
+ strcat (session_save_file, "/.XSM-");
+ strcat (session_save_file, session_name);
+}
+
+
+
+int
+ReadSave(session_name, sm_id)
+
+char *session_name;
+char **sm_id;
+
+{
+ char *buf;
+ int buflen;
+ char *p;
+ PendingClient *c;
+ Prop *prop;
+ PropValue *val;
+ FILE *f;
+ int state, i;
+ int version_number;
+
+ f = fopen(session_save_file, "r");
+ if(!f) {
+ if (verbose)
+ printf("No session save file.\n");
+ *sm_id = NULL;
+ return 0;
+ }
+ if (verbose)
+ printf("Reading session save file...\n");
+
+ buf = NULL;
+ buflen = 0;
+
+ /* Read version # */
+ getline(&buf, &buflen, f);
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ version_number = atoi (buf);
+ if (version_number > SAVEFILE_VERSION)
+ {
+ if (verbose)
+ printf("Unsupported version number of session save file.\n");
+ *sm_id = NULL;
+ if (buf)
+ free (buf);
+ return 0;
+ }
+
+ /* Read SM's id */
+ getline(&buf, &buflen, f);
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ *sm_id = XtNewString(buf);
+
+ /* Read number of clients running in the last session */
+ if (version_number >= 2)
+ {
+ getline(&buf, &buflen, f);
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ num_clients_in_last_session = atoi (buf);
+ }
+
+ state = 0;
+ while(getline(&buf, &buflen, f)) {
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ for(p = buf; *p && isspace(*p); p++) /* LOOP */;
+ if(*p == '#') continue;
+
+ if(!*p)
+ {
+ if (version_number >= 3 &&
+ ListCount (PendingList) == num_clients_in_last_session)
+ {
+ state = 5;
+ break;
+ }
+ else
+ {
+ state = 0;
+ continue;
+ }
+ }
+
+ if(!isspace(buf[0])) {
+ switch(state) {
+ case 0:
+ c = (PendingClient *)XtMalloc(sizeof *c);
+ if(!c) nomem();
+
+ c->clientId = XtNewString(p);
+ c->clientHostname = NULL; /* set in next state */
+
+ c->props = ListInit();
+ if(!c->props) nomem();
+
+ if(!ListAddLast(PendingList, (char *)c)) nomem();
+
+ state = 1;
+ break;
+
+ case 1:
+ c->clientHostname = XtNewString(p);
+ state = 2;
+ break;
+
+ case 2:
+ case 4:
+ prop = (Prop *)XtMalloc(sizeof *prop);
+ if(!prop) nomem();
+
+ prop->name = XtNewString(p);
+ prop->values = ListInit();
+ if(!prop->values) nomem();
+
+ prop->type = NULL;
+
+ if(!ListAddLast(c->props, (char *)prop)) nomem();
+
+ state = 3;
+ break;
+
+ case 3:
+ prop->type = XtNewString(p);
+ state = 4;
+ break;
+
+ default:
+ fprintf(stderr, "state %d\n", state);
+ fprintf(stderr,
+ "Corrupt save file line ignored:\n%s\n", buf);
+ continue;
+ }
+ } else {
+ if (state != 4) {
+ fprintf(stderr, "Corrupt save file line ignored:\n%s\n", buf);
+ continue;
+ }
+ val = (PropValue *)XtMalloc(sizeof *val);
+ if(!val) nomem();
+
+ if (strcmp (prop->type, SmCARD8) == 0)
+ {
+ val->length = 1;
+ val->value = (XtPointer) XtMalloc (1);
+ *((char *)(val->value)) = atoi (p);
+ }
+ else
+ {
+ val->length = strlen(p);
+ val->value = XtNewString(p);
+ }
+
+ if(!ListAddLast(prop->values, (char *)val)) nomem();
+ }
+ }
+
+ /* Read commands for non-session aware clients */
+
+ if (state == 5)
+ {
+ String strbuf;
+ int bufsize = 0;
+
+ getline(&buf, &buflen, f);
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ non_session_aware_count = atoi (buf);
+
+ if (non_session_aware_count > 0)
+ {
+ non_session_aware_clients = (char **) malloc (
+ non_session_aware_count * sizeof (char *));
+
+ for (i = 0; i < non_session_aware_count; i++)
+ {
+ getline(&buf, &buflen, f);
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ non_session_aware_clients[i] = (char *) malloc (
+ strlen (buf) + 2);
+ strcpy (non_session_aware_clients[i], buf);
+ bufsize += (strlen (buf) + 1);
+ }
+
+ strbuf = (String) malloc (bufsize + 1);
+ strbuf[0] = '\0';
+
+ for (i = 0; i < non_session_aware_count; i++)
+ {
+ strcat (strbuf, non_session_aware_clients[i]);
+ strcat (strbuf, "\n");
+ }
+
+ XtVaSetValues (manualRestartCommands,
+ XtNstring, strbuf,
+ NULL);
+
+ free ((char *) strbuf);
+ }
+ }
+
+ fclose(f);
+
+ if (buf)
+ free (buf);
+
+ return 1;
+}
+
+
+
+static void
+SaveClient (f, client)
+
+FILE *f;
+ClientRec *client;
+
+{
+ List *pl;
+
+ fprintf (f, "%s\n", client->clientId);
+ fprintf (f, "%s\n", client->clientHostname);
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ {
+ Prop *pprop = (Prop *) pl->thing;
+ List *pj, *vl;
+ PropValue *pval;
+
+ fprintf (f, "%s\n", pprop->name);
+ fprintf (f, "%s\n", pprop->type);
+
+ if (strcmp (pprop->type, SmCARD8) == 0)
+ {
+ char *card8;
+ int value;
+
+ vl = ListFirst (pprop->values);
+ pval = (PropValue *) vl->thing;
+
+ card8 = pval->value;
+ value = *card8;
+ fprintf(f, "\t%d\n", value);
+ }
+ else
+ {
+ for (pj = ListFirst (pprop->values); pj; pj = ListNext (pj))
+ {
+ pval = (PropValue *) pj->thing;
+ fprintf (f, "\t%s\n", pval->value);
+ }
+ }
+ }
+
+ fprintf (f, "\n");
+}
+
+
+
+void
+WriteSave (sm_id)
+
+char *sm_id;
+
+{
+ ClientRec *client;
+ FILE *f;
+ List *cl;
+ String commands;
+ char *p, *c;
+ int count;
+
+ f = fopen (session_save_file, "w");
+
+ if (!f)
+ {
+ char msg[256];
+
+ sprintf (msg, "%s: Error creating session save file %s",
+ Argv[0], session_save_file);
+ add_log_text (msg);
+ perror (msg);
+ }
+ else
+ {
+ fprintf (f, "%d\n", SAVEFILE_VERSION);
+ fprintf (f, "%s\n", sm_id);
+
+ count = 0;
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ if (client->restartHint != SmRestartNever)
+ count++;
+ }
+ count += ListCount (RestartAnywayList);
+
+ fprintf (f, "%d\n", count);
+ if (count == 0)
+ fprintf (f, "\n");
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ if (client->restartHint == SmRestartNever)
+ continue;
+
+ SaveClient (f, client);
+ }
+
+ for (cl = ListFirst (RestartAnywayList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ SaveClient (f, client);
+ }
+
+
+ /* Save the non-session aware clients */
+
+ XtVaGetValues (manualRestartCommands,
+ XtNstring, &commands,
+ NULL);
+
+ p = c = commands;
+ count = 0;
+
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ if (p != c)
+ count++;
+ c = p + 1;
+ }
+ p++;
+ }
+ if (p != c)
+ count++;
+
+ fprintf (f, "%d\n", count);
+
+ p = c = commands;
+
+ while (*p)
+ {
+ if (*p == '\n')
+ {
+ if (p != c)
+ {
+ *p = '\0';
+ fprintf (f, "%s\n", c);
+ *p = '\n';
+ }
+ c = p + 1;
+ }
+ p++;
+ }
+
+ if (p != c)
+ fprintf (f, "%s\n", c);
+
+ fclose (f);
+ }
+}
+
+
+
+Status
+DeleteSession (session_name)
+
+char *session_name;
+
+{
+ char *buf;
+ int buflen;
+ char *p, *dir;
+ FILE *f;
+ int state;
+ int foundDiscard;
+ char filename[256];
+ int version_number;
+
+ dir = (char *) getenv ("SM_SAVE_DIR");
+ if (!dir)
+ {
+ dir = (char *) getenv ("HOME");
+ if (!dir)
+ dir = ".";
+ }
+
+ sprintf (filename, "%s/.XSM-%s", dir, session_name);
+
+ f = fopen(filename, "r");
+ if(!f) {
+ return (0);
+ }
+
+ buf = NULL;
+ buflen = 0;
+
+ /* Read version # */
+ getline(&buf, &buflen, f);
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ version_number = atoi (buf);
+ if (version_number > SAVEFILE_VERSION)
+ {
+ if (verbose)
+ printf("Can't delete session save file - incompatible version.\n");
+ if (buf)
+ free (buf);
+ return (0);
+ }
+
+ /* Skip SM's id */
+ getline(&buf, &buflen, f);
+
+ /* Skip number of clients running in the last session */
+ if (version_number >= 2)
+ getline(&buf, &buflen, f);
+
+ state = 0;
+ foundDiscard = 0;
+ while(getline(&buf, &buflen, f)) {
+ if((p = strchr(buf, '\n'))) *p = '\0';
+ for(p = buf; *p && isspace(*p); p++) /* LOOP */;
+ if(*p == '#') continue;
+
+ if(!*p) {
+ state = 0;
+ foundDiscard = 0;
+ continue;
+ }
+
+ if(!isspace(buf[0])) {
+ switch(state) {
+ case 0:
+ state = 1;
+ break;
+
+ case 1:
+ state = 2;
+ break;
+
+ case 2:
+ case 4:
+ if (strcmp (p, SmDiscardCommand) == 0)
+ foundDiscard = 1;
+ state = 3;
+ break;
+
+ case 3:
+ state = 4;
+ break;
+
+ default:
+ continue;
+ }
+ } else {
+ if (state != 4) {
+ continue;
+ }
+ if (foundDiscard)
+ {
+ execute_system_command (p); /* Discard Command */
+ foundDiscard = 0;
+ }
+ }
+ }
+
+ fclose(f);
+
+ if (buf)
+ free (buf);
+
+ return ((unlink (filename) == -1) ? 0 : 1);
+}
+
+
+
+Bool
+getline(pbuf, plen, f)
+char **pbuf;
+int *plen;
+FILE *f;
+{
+ int c;
+ int i;
+
+ i = 0;
+ while(1) {
+ if(i+2 > *plen) {
+ if(*plen) *plen *= 2;
+ else *plen = BUFSIZ;
+ if(*pbuf) *pbuf = (char *) realloc(*pbuf, *plen + 1);
+ else *pbuf = (char *) malloc(*plen + 1);
+ }
+ c = getc(f);
+ if(c == EOF) break;
+ (*pbuf)[i++] = c;
+ if(c == '\n') break;
+ }
+ (*pbuf)[i] = '\0';
+ return i;
+}
diff --git a/saveutil.h b/saveutil.h
new file mode 100644
index 0000000..4d8cc64
--- /dev/null
+++ b/saveutil.h
@@ -0,0 +1,29 @@
+/* $Xorg: saveutil.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern int ReadSave ();
+extern void WriteSave ();
+extern Status DeleteSession ();
diff --git a/signals.c b/signals.c
new file mode 100644
index 0000000..ec73792
--- /dev/null
+++ b/signals.c
@@ -0,0 +1,251 @@
+/* $Xorg: signals.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+
+#include <X11/SM/SMlib.h>
+
+#ifdef USG
+#ifndef __TYPES__
+#include <sys/types.h> /* forgot to protect it... */
+#define __TYPES__
+#endif /* __TYPES__ */
+#else
+#if defined(_POSIX_SOURCE) && defined(MOTOROLA)
+#undef _POSIX_SOURCE
+#include <sys/types.h>
+#define _POSIX_SOURCE
+#else
+#include <sys/types.h>
+#endif
+#endif /* USG */
+
+#ifdef X_POSIX_C_SOURCE
+#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
+#include <signal.h>
+#include <sys/wait.h>
+#undef _POSIX_C_SOURCE
+#else
+#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
+#include <signal.h>
+#include <sys/wait.h>
+#else
+#define _POSIX_SOURCE
+#include <signal.h>
+#include <sys/wait.h>
+#undef _POSIX_SOURCE
+#endif
+#endif
+
+#if defined(X_NOT_POSIX) && defined(SIGNALRETURNSINT)
+#define SIGVAL int
+#else
+#define SIGVAL void
+#endif
+
+#ifndef X_NOT_POSIX
+#define USE_POSIX_WAIT
+#endif
+
+#if defined(linux) || defined(SYSV)
+#define USE_SYSV_SIGNALS
+#endif
+
+#if defined(SCO) || defined(ISC)
+#undef SIGTSTP /* defined, but not the BSD way */
+#endif
+
+#if defined(X_NOT_POSIX) && defined(SYSV)
+#define SIGNALS_RESET_WHEN_CAUGHT
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int checkpoint_from_signal = 0;
+
+extern Bool wantShutdown;
+
+
+SIGVAL (*Signal (sig, handler))()
+ int sig;
+ SIGVAL (*handler)();
+{
+#ifndef X_NOT_POSIX
+ struct sigaction sigact, osigact;
+ sigact.sa_handler = handler;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction(sig, &sigact, &osigact);
+ return osigact.sa_handler;
+#else
+ return signal(sig, handler);
+#endif
+}
+
+
+void
+sig_child_handler ()
+
+{
+ int pid;
+
+#if !defined(USE_POSIX_WAIT) && (defined(USE_SYSV_SIGNALS) && \
+ (defined(CRAY) || !defined(SIGTSTP)))
+ wait (NULL);
+#endif
+
+#ifdef SIGNALS_RESET_WHEN_CAUGHT
+ Signal (SIGCHLD, sig_child_handler);
+#endif
+
+ /*
+ * The wait() above must come before re-establishing the signal handler.
+ * In between this time, a new child might have died. If we can do
+ * a non-blocking wait, we can check for this race condition. If we
+ * don't have non-blocking wait, we lose.
+ */
+
+ do
+ {
+#ifdef USE_POSIX_WAIT
+ pid = waitpid (-1, NULL, WNOHANG);
+#else
+#if defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP))
+ /* cannot do non-blocking wait */
+ pid = 0;
+#else
+ union wait status;
+
+ pid = wait3 (&status, WNOHANG, (struct rusage *)NULL);
+#endif
+#endif /* USE_POSIX_WAIT else */
+ }
+ while (pid > 0);
+}
+
+
+void
+sig_term_handler ()
+
+{
+ wantShutdown = 1;
+ checkpoint_from_signal = 1;
+ DoSave (SmSaveLocal, SmInteractStyleNone, 1 /* fast */);
+}
+
+
+void
+sig_usr1_handler ()
+
+{
+ wantShutdown = 0;
+ checkpoint_from_signal = 1;
+ DoSave (SmSaveLocal, SmInteractStyleNone, 0 /* fast */);
+}
+
+
+
+void
+register_signals ()
+
+{
+ /*
+ * Ignore SIGPIPE
+ */
+
+ Signal (SIGPIPE, SIG_IGN);
+
+
+ /*
+ * If child process dies, call our handler
+ */
+
+ Signal (SIGCHLD, sig_child_handler);
+
+
+ /*
+ * If we get a SIGTERM, do shutdown, fast, local, no interact
+ */
+
+ Signal (SIGTERM, sig_term_handler);
+
+
+ /*
+ * If we get a SIGUSR1, do checkpoint, local, no interact
+ */
+
+ Signal (SIGUSR1, sig_usr1_handler);
+}
+
+
+
+int
+execute_system_command (s)
+
+char *s;
+
+{
+ int stat;
+
+#ifdef X_NOT_POSIX
+ /*
+ * Non-POSIX system() uses wait(). We must disable our sig child
+ * handler because if it catches the signal, system() will block
+ * forever in wait().
+ */
+
+ int pid;
+
+ Signal (SIGCHLD, SIG_IGN);
+#endif
+
+ stat = system (s);
+
+#ifdef X_NOT_POSIX
+ /*
+ * Re-enable our sig child handler. We might have missed some signals,
+ * so do non-blocking waits until there are no signals left.
+ */
+
+ Signal (SIGCHLD, sig_child_handler);
+
+#if !(defined(USE_SYSV_SIGNALS) && (defined(CRAY) || !defined(SIGTSTP)))
+ do
+ {
+ union wait status;
+
+ pid = wait3 (&status, WNOHANG, (struct rusage *)NULL);
+ } while (pid > 0);
+#endif
+#endif /* X_NOT_POSIX */
+
+ return (stat);
+}
+
+
diff --git a/system.xsm b/system.xsm
new file mode 100644
index 0000000..7b9beff
--- /dev/null
+++ b/system.xsm
@@ -0,0 +1,4 @@
+! $Xorg: system.xsm,v 1.3 2000/08/17 19:55:06 cpqbld Exp $
+twm
+smproxy
+xterm
diff --git a/xsm.c b/xsm.c
new file mode 100644
index 0000000..ef4c7e2
--- /dev/null
+++ b/xsm.c
@@ -0,0 +1,1397 @@
+/* $Xorg: xsm.c,v 1.7 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+/*
+ * X Session Manager.
+ *
+ * Authors:
+ * Ralph Mor, X Consortium
+ * Jordan Brown, Quarterdeck Office Systems
+ */
+
+#include "xsm.h"
+#include "globals.c"
+#include "xtwatch.h"
+#include "prop.h"
+#include "choose.h"
+#include "mainwin.h"
+#include "info.h"
+#include "log.h"
+#include "save.h"
+#include "auth.h"
+#include "restart.h"
+#include "saveutil.h"
+#include "lock.h"
+
+#include <X11/Shell.h>
+#include <X11/Xatom.h>
+
+Atom wmStateAtom;
+Atom wmDeleteAtom;
+static char *cmd_line_display = NULL;
+
+/*
+ * Forward declarations
+ */
+
+Status StartSession ();
+void NewConnectionXtProc ();
+Status NewClientProc ();
+void InstallIOErrorHandler ();
+static void CloseListeners(void);
+
+/*
+ * Extern declarations
+ */
+
+extern Widget clientInfoPopup;
+extern Widget clientPropPopup;
+extern Widget clientInfoButton;
+extern Widget logButton;
+extern Widget checkPointButton;
+extern Widget shutdownButton;
+extern Widget clientListWidget;
+extern Widget savePopup;
+
+extern int checkpoint_from_signal;
+
+static IceListenObj *listenObjs;
+
+
+/*
+ * Main program
+ */
+
+main (argc, argv)
+
+int argc;
+char **argv;
+
+{
+ char *p;
+ char errormsg[256];
+ static char environment_name[] = "SESSION_MANAGER";
+ int success, found_command_line_name, i;
+
+ Argc = argc;
+ Argv = argv;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'd': /* -display */
+ if (++i >= argc) goto usage;
+ cmd_line_display = (char *) XtNewString (argv[i]);
+ continue;
+
+ case 's': /* -session */
+ if (++i >= argc) goto usage;
+ session_name = XtNewString (argv[i]);
+ continue;
+
+ case 'v': /* -verbose */
+ verbose = 1;
+ continue;
+ }
+ }
+
+ usage:
+ fprintf (stderr,
+ "usage: xsm [-display display] [-session session_name] [-verbose]\n");
+ exit (1);
+ }
+
+ topLevel = XtVaAppInitialize (&appContext, "XSm", NULL, 0,
+ &argc, argv, NULL,
+ XtNmappedWhenManaged, False,
+ XtNwindowRole, "xsm main window",
+ NULL);
+
+ wmStateAtom = XInternAtom (
+ XtDisplay (topLevel), "WM_STATE", False);
+ wmDeleteAtom = XInternAtom (
+ XtDisplay (topLevel), "WM_DELETE_WINDOW", False);
+
+ register_signals ();
+
+
+ /*
+ * Install an IO error handler. For an explanation,
+ * see the comments for InstallIOErrorHandler().
+ */
+
+ InstallIOErrorHandler ();
+
+
+ /*
+ * Init SM lib
+ */
+
+ if (!SmsInitialize ("SAMPLE-SM", "1.0",
+ NewClientProc, NULL,
+ HostBasedAuthProc, 256, errormsg))
+ {
+ fprintf (stderr, "%s\n", errormsg);
+ exit (1);
+ }
+
+ if (!IceListenForConnections (&numTransports, &listenObjs,
+ 256, errormsg))
+ {
+ fprintf (stderr, "%s\n", errormsg);
+ exit (1);
+ }
+
+ atexit(CloseListeners);
+
+ if (!SetAuthentication (numTransports, listenObjs, &authDataEntries))
+ {
+ fprintf (stderr, "Could not set authorization\n");
+ exit (1);
+ }
+
+ InitWatchProcs (appContext);
+
+ for (i = 0; i < numTransports; i++)
+ {
+ XtAppAddInput (appContext,
+ IceGetListenConnectionNumber (listenObjs[i]),
+ (XtPointer) XtInputReadMask,
+ NewConnectionXtProc, (XtPointer) listenObjs[i]);
+ }
+
+ /* the sizeof includes the \0, so we don't need to count the '=' */
+ networkIds = IceComposeNetworkIdList (numTransports, listenObjs);
+ p = (char *) XtMalloc((sizeof environment_name) + strlen(networkIds) + 1);
+ if(!p) nomem();
+ sprintf(p, "%s=%s", environment_name, networkIds);
+ putenv(p);
+
+ if (cmd_line_display)
+ {
+ /*
+ * If a display was passed on the command line, set the DISPLAY
+ * environment in this process so all applications started by
+ * the session manager will run on the specified display.
+ */
+
+ p = (char *) XtMalloc(8 + strlen(cmd_line_display) + 1);
+ sprintf(p, "DISPLAY=%s", cmd_line_display);
+ putenv(p);
+ }
+
+ if (verbose)
+ printf ("setenv %s %s\n", environment_name, networkIds);
+
+ create_choose_session_popup ();
+ create_main_window ();
+ create_client_info_popup ();
+ create_save_popup ();
+ create_log_popup ();
+
+
+ /*
+ * Initalize all lists
+ */
+
+ RunningList = ListInit();
+ if(!RunningList) nomem();
+
+ PendingList = ListInit();
+ if(!PendingList) nomem();
+
+ RestartAnywayList = ListInit();
+ if(!RestartAnywayList) nomem();
+
+ RestartImmedList = ListInit();
+ if(!RestartImmedList) nomem();
+
+ WaitForSaveDoneList = ListInit();
+ if (!WaitForSaveDoneList) nomem();
+
+ FailedSaveList = ListInit();
+ if (!FailedSaveList) nomem();
+
+ WaitForInteractList = ListInit();
+ if (!WaitForInteractList) nomem();
+
+ WaitForPhase2List = ListInit();
+ if (!WaitForPhase2List) nomem();
+
+
+ /*
+ * Get list of session names. If a session name was found on the
+ * command line, and it is in the list of session names we got, then
+ * use that session name. If there were no session names found, then
+ * use the default session name. Otherwise, present a list of session
+ * names for the user to choose from.
+ */
+
+ success = GetSessionNames (&sessionNameCount,
+ &sessionNamesShort, &sessionNamesLong, &sessionsLocked);
+
+ found_command_line_name = 0;
+ if (success && session_name)
+ {
+ for (i = 0; i < sessionNameCount; i++)
+ if (strcmp (session_name, sessionNamesShort[i]) == 0)
+ {
+ found_command_line_name = 1;
+
+ if (sessionsLocked[i])
+ {
+ fprintf (stderr, "Session '%s' is locked\n", session_name);
+ exit (1);
+ }
+
+ break;
+ }
+ }
+
+ if (!success || found_command_line_name)
+ {
+ FreeSessionNames (sessionNameCount,
+ sessionNamesShort, sessionNamesLong, sessionsLocked);
+
+ if (!found_command_line_name)
+ session_name = XtNewString (DEFAULT_SESSION_NAME);
+
+ if (!StartSession (session_name, !found_command_line_name))
+ UnableToLockSession (session_name);
+ }
+ else
+ {
+ ChooseSession ();
+ }
+
+
+ /*
+ * Main loop
+ */
+
+ XtAppMainLoop (appContext);
+}
+
+
+
+void
+PropertyChangeXtHandler (w, closure, event, continue_to_dispatch)
+
+Widget w;
+XtPointer closure;
+XEvent *event;
+Boolean *continue_to_dispatch;
+
+{
+ if (w == topLevel && event->type == PropertyNotify &&
+ event->xproperty.atom == wmStateAtom)
+ {
+ XtRemoveEventHandler (topLevel, PropertyChangeMask, False,
+ PropertyChangeXtHandler, NULL);
+
+ /*
+ * Restart the rest of the session aware clients.
+ */
+
+ Restart (RESTART_REST_OF_CLIENTS);
+
+
+ /*
+ * Start apps that aren't session aware that were specified
+ * by the user.
+ */
+
+ StartNonSessionAwareApps ();
+ }
+}
+
+
+
+void
+SetWM_DELETE_WINDOW (widget, delAction)
+
+Widget widget;
+String delAction;
+
+{
+ char translation[64];
+
+ sprintf (translation, "<Message>WM_PROTOCOLS: %s", delAction);
+ XtOverrideTranslations (widget, XtParseTranslationTable (translation));
+
+ XSetWMProtocols (XtDisplay(widget), XtWindow (widget),
+ &wmDeleteAtom, 1);
+}
+
+
+
+void
+GetEnvironment ()
+
+{
+ static char envDISPLAY[]="DISPLAY";
+ static char envSESSION_MANAGER[]="SESSION_MANAGER";
+ static char envAUDIOSERVER[]="AUDIOSERVER";
+ char *p, *temp;
+
+ remote_allowed = 1;
+
+ display_env = NULL;
+ if((p = cmd_line_display) || (p = (char *) getenv(envDISPLAY))) {
+ display_env = (char *) XtMalloc(strlen(envDISPLAY)+1+strlen(p)+1);
+ if(!display_env) nomem();
+ sprintf(display_env, "%s=%s", envDISPLAY, p);
+
+ /*
+ * When we restart a remote client, we have to make sure the
+ * display environment we give it has the SM's hostname.
+ */
+
+ if ((temp = strchr (p, '/')) == 0)
+ temp = p;
+ else
+ temp++;
+
+ if (*temp != ':')
+ {
+ /* we have a host name */
+
+ non_local_display_env = (char *) XtMalloc (
+ strlen (display_env) + 1);
+ if (!non_local_display_env) nomem();
+
+ strcpy (non_local_display_env, display_env);
+ }
+ else
+ {
+ char hostnamebuf[256];
+
+ gethostname (hostnamebuf, sizeof hostnamebuf);
+ non_local_display_env = (char *) XtMalloc (
+ strlen (envDISPLAY) + 1 +
+ strlen (hostnamebuf) + strlen (temp) + 1);
+ if (!non_local_display_env) nomem();
+ sprintf(non_local_display_env, "%s=%s%s",
+ envDISPLAY, hostnamebuf, temp);
+ }
+ }
+
+ session_env = NULL;
+ if((p = (char *) getenv(envSESSION_MANAGER))) {
+ session_env = (char *) XtMalloc(
+ strlen(envSESSION_MANAGER)+1+strlen(p)+1);
+ if(!session_env) nomem();
+ sprintf(session_env, "%s=%s", envSESSION_MANAGER, p);
+
+ /*
+ * When we restart a remote client, we have to make sure the
+ * session environment does not have the SM's local connection port.
+ */
+
+ non_local_session_env = (char *) XtMalloc (strlen (session_env) + 1);
+ if (!non_local_session_env) nomem();
+ strcpy (non_local_session_env, session_env);
+
+ if ((temp = Strstr (non_local_session_env, "local/")) != NULL)
+ {
+ char *delim = strchr (temp, ',');
+ if (delim == NULL)
+ {
+ if (temp == non_local_session_env +
+ strlen (envSESSION_MANAGER) + 1)
+ {
+ *temp = '\0';
+ remote_allowed = 0;
+ }
+ else
+ *(temp - 1) = '\0';
+ }
+ else
+ {
+ int bytes = strlen (delim + 1);
+ memmove (temp, delim + 1, bytes);
+ *(temp + bytes) = '\0';
+ }
+ }
+ }
+
+ audio_env = NULL;
+ if((p = (char *) getenv(envAUDIOSERVER))) {
+ audio_env = (char *) XtMalloc(strlen(envAUDIOSERVER)+1+strlen(p)+1);
+ if(!audio_env) nomem();
+ sprintf(audio_env, "%s=%s", envAUDIOSERVER, p);
+ }
+}
+
+
+
+Status
+StartSession (name, use_default)
+
+char *name;
+Bool use_default;
+
+{
+ int database_read = 0;
+ Dimension width;
+ char title[256];
+
+
+ /*
+ * If we're not using the default session, lock it.
+ * If using the default session, it will be locked as
+ * soon as the user assigns the session a name.
+ */
+
+ if (!use_default && !LockSession (name, True))
+ return (0);
+
+
+ /*
+ * Get important environment variables.
+ */
+
+ GetEnvironment ();
+
+
+ /*
+ * Set the main window's title to the session name.
+ */
+
+ sprintf (title, "xsm: %s", name);
+
+ XtVaSetValues (topLevel,
+ XtNtitle, title, /* session name */
+ NULL);
+
+ XtRealizeWidget (topLevel);
+
+
+ /*
+ * Set WM_DELETE_WINDOW support on main window. If the user tries
+ * to delete the main window, the shutdown prompt will come up.
+ */
+
+ SetWM_DELETE_WINDOW (topLevel, "DelMainWinAction()");
+
+
+ /*
+ * Read the session save file. Make sure the session manager
+ * has an SM_CLIENT_ID, so that other managers (like the WM) can
+ * identify it.
+ */
+
+ set_session_save_file_name (name);
+
+ if (use_default)
+ need_to_name_session = True;
+ else
+ {
+ database_read = ReadSave (name, &sm_id);
+ need_to_name_session = !database_read;
+ }
+
+ if (!sm_id)
+ {
+ sm_id = SmsGenerateClientID (NULL);
+ if (!sm_id) return (0);
+ }
+ XChangeProperty (XtDisplay (topLevel), XtWindow (topLevel),
+ XInternAtom (XtDisplay (topLevel), "SM_CLIENT_ID", False),
+ XA_STRING, 8, PropModeReplace,
+ (unsigned char *) sm_id, strlen (sm_id));
+
+
+ /*
+ * Adjust some label widths
+ */
+
+ XtVaGetValues (clientInfoButton,
+ XtNwidth, &width,
+ NULL);
+
+ XtVaSetValues (checkPointButton,
+ XtNwidth, width,
+ NULL);
+
+ XtVaGetValues (logButton,
+ XtNwidth, &width,
+ NULL);
+
+ XtVaSetValues (shutdownButton,
+ XtNwidth, width,
+ NULL);
+
+
+ XtMapWidget (topLevel);
+
+
+ if (!database_read)
+ {
+ /*
+ * Start default apps (e.g. twm, smproxy)
+ */
+
+ StartDefaultApps ();
+ }
+ else
+ {
+ /*
+ * Restart window manager first. When the session manager
+ * gets a WM_STATE stored on its top level window, we know
+ * the window manager is running. At that time, we can start
+ * the rest of the applications.
+ */
+
+ XtAddEventHandler (topLevel, PropertyChangeMask, False,
+ PropertyChangeXtHandler, NULL);
+
+ if (!Restart (RESTART_MANAGERS))
+ {
+ XtRemoveEventHandler (topLevel, PropertyChangeMask, False,
+ PropertyChangeXtHandler, NULL);
+
+ /*
+ * Restart the rest of the session aware clients.
+ */
+
+ Restart (RESTART_REST_OF_CLIENTS);
+
+ /*
+ * Start apps that aren't session aware that were specified
+ * by the user.
+ */
+
+ StartNonSessionAwareApps ();
+ }
+ }
+
+ return (1);
+}
+
+
+
+EndSession (status)
+
+int status;
+
+{
+ if (verbose)
+ printf ("\nSESSION MANAGER GOING AWAY!\n");
+
+ FreeAuthenticationData (numTransports, authDataEntries);
+
+ if (session_name)
+ {
+ UnlockSession (session_name);
+ XtFree (session_name);
+ }
+
+ if (display_env)
+ XtFree (display_env);
+ if (session_env)
+ XtFree (session_env);
+ if (cmd_line_display)
+ XtFree (cmd_line_display);
+ if (non_local_display_env)
+ XtFree (non_local_display_env);
+ if (non_local_session_env)
+ XtFree (non_local_session_env);
+ if (audio_env)
+ XtFree (audio_env);
+ if (networkIds)
+ free (networkIds);
+
+ exit (status);
+}
+
+
+
+void
+FreeClient (client, freeProps)
+
+ClientRec *client;
+Bool freeProps;
+
+{
+ if (freeProps)
+ {
+ List *pl;
+
+ for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
+ FreeProp ((Prop *) pl->thing);
+
+ ListFreeAll (client->props);
+ }
+
+ if (client->clientId)
+ free (client->clientId); /* malloc'd by SMlib */
+ if (client->clientHostname)
+ free (client->clientHostname); /* malloc'd by SMlib */
+
+ if (client->discardCommand)
+ XtFree (client->discardCommand);
+ if (client->saveDiscardCommand)
+ XtFree (client->saveDiscardCommand);
+
+ XtFree ((char *) client);
+}
+
+
+
+/*
+ * Session Manager callbacks
+ */
+
+Status
+RegisterClientProc (smsConn, managerData, previousId)
+
+SmsConn smsConn;
+SmPointer managerData;
+char *previousId;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+ char *id;
+ List *cl;
+ int send_save;
+
+ if (verbose)
+ {
+ printf (
+ "On IceConn fd = %d, received REGISTER CLIENT [Previous Id = %s]\n",
+ IceConnectionNumber (client->ice_conn),
+ previousId ? previousId : "NULL");
+ printf ("\n");
+ }
+
+ if (!previousId)
+ {
+ id = SmsGenerateClientID (smsConn);
+ send_save = 1;
+ }
+ else
+ {
+ int found_match = 0;
+ send_save = 1;
+
+ for (cl = ListFirst (PendingList); cl; cl = ListNext (cl))
+ {
+ PendingClient *pendClient = (PendingClient *) cl->thing;
+
+ if (!strcmp (pendClient->clientId, previousId))
+ {
+ SetInitialProperties (client, pendClient->props);
+ XtFree (pendClient->clientId);
+ XtFree (pendClient->clientHostname);
+ XtFree ((char *) pendClient);
+ ListFreeOne (cl);
+ found_match = 1;
+ send_save = 0;
+ break;
+ }
+ }
+
+ if (!found_match)
+ {
+ for (cl = ListFirst (RestartAnywayList); cl; cl = ListNext (cl))
+ {
+ ClientRec *rClient = (ClientRec *) cl->thing;
+
+ if (!strcmp (rClient->clientId, previousId))
+ {
+ SetInitialProperties (client, rClient->props);
+ FreeClient (rClient, False /* don't free props */);
+ ListFreeOne (cl);
+ found_match = 1;
+ send_save = 0;
+ break;
+ }
+ }
+ }
+
+ if (!found_match)
+ {
+ for (cl = ListFirst (RestartImmedList); cl; cl = ListNext (cl))
+ {
+ ClientRec *rClient = (ClientRec *) cl->thing;
+
+ if (!strcmp (rClient->clientId, previousId))
+ {
+ SetInitialProperties (client, rClient->props);
+ FreeClient (rClient, False /* don't free props */);
+ ListFreeOne (cl);
+ found_match = 1;
+ send_save = 0;
+ break;
+ }
+ }
+ }
+
+ if (!found_match)
+ {
+ /*
+ * previous-id was bogus: return bad status and the client
+ * should re-register with a NULL previous-id
+ */
+
+ free (previousId);
+ return (0);
+ }
+ else
+ {
+ id = previousId;
+ }
+ }
+
+ SmsRegisterClientReply (smsConn, id);
+
+ if (verbose)
+ {
+ printf (
+ "On IceConn fd = %d, sent REGISTER CLIENT REPLY [Client Id = %s]\n",
+ IceConnectionNumber (client->ice_conn), id);
+ printf ("\n");
+ }
+
+ client->clientId = id;
+ client->clientHostname = SmsClientHostName (smsConn);
+ client->restarted = (previousId != NULL);
+
+ if (send_save)
+ {
+ SmsSaveYourself (smsConn, SmSaveLocal,
+ False, SmInteractStyleNone, False);
+ }
+ else if (client_info_visible)
+ {
+ /* We already have all required client info */
+
+ UpdateClientList ();
+ XawListHighlight (clientListWidget, current_client_selected);
+ }
+
+ return (1);
+}
+
+
+
+static Bool
+OkToEnterInteractPhase ()
+
+{
+ return ((ListCount (WaitForInteractList) +
+ ListCount (WaitForPhase2List)) == ListCount (WaitForSaveDoneList));
+}
+
+
+
+void
+InteractRequestProc (smsConn, managerData, dialogType)
+
+SmsConn smsConn;
+SmPointer managerData;
+int dialogType;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, received INTERACT REQUEST [Dialog Type = ",
+ client->clientId);
+ if (dialogType == SmDialogError)
+ printf ("Error]\n");
+ else if (dialogType == SmDialogNormal)
+ printf ("Normal]\n");
+ else
+ printf ("Error in SMlib: should have checked for bad value]\n");
+ }
+
+ ListAddLast (WaitForInteractList, (char *) client);
+
+ if (OkToEnterInteractPhase ())
+ {
+ LetClientInteract (ListFirst (WaitForInteractList));
+ }
+}
+
+
+
+void
+InteractDoneProc (smsConn, managerData, cancelShutdown)
+ SmsConn smsConn;
+ SmPointer managerData;
+ Bool cancelShutdown;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+ List *cl;
+
+ if (verbose)
+ {
+ printf (
+ "Client Id = %s, received INTERACT DONE [Cancel Shutdown = %s]\n",
+ client->clientId, cancelShutdown ? "True" : "False");
+ }
+
+ if (cancelShutdown)
+ {
+ ListFreeAllButHead (WaitForInteractList);
+ ListFreeAllButHead (WaitForPhase2List);
+ }
+
+ if (cancelShutdown)
+ {
+ if (shutdownCancelled)
+ {
+ /* Shutdown was already cancelled */
+ return;
+ }
+
+ shutdownCancelled = True;
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ client = (ClientRec *) cl->thing;
+
+ SmsShutdownCancelled (client->smsConn);
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, sent SHUTDOWN CANCELLED\n",
+ client->clientId);
+ }
+ }
+ }
+ else
+ {
+ if ((cl = ListFirst (WaitForInteractList)) != NULL)
+ {
+ LetClientInteract (cl);
+ }
+ else
+ {
+ if (verbose)
+ {
+ printf ("\n");
+ printf ("Done interacting with all clients.\n");
+ printf ("\n");
+ }
+
+ if (ListCount (WaitForPhase2List) > 0)
+ {
+ StartPhase2 ();
+ }
+ }
+ }
+}
+
+
+
+void
+SaveYourselfReqProc (smsConn, managerData, saveType,
+ shutdown, interactStyle, fast, global)
+
+SmsConn smsConn;
+SmPointer managerData;
+int saveType;
+Bool shutdown;
+int interactStyle;
+Bool fast;
+Bool global;
+
+{
+ if (verbose)
+ printf("SAVE YOURSELF REQUEST not supported!\n");
+}
+
+
+
+static Bool
+OkToEnterPhase2 ()
+
+{
+ return (ListCount (WaitForPhase2List) == ListCount (WaitForSaveDoneList));
+}
+
+
+
+void
+SaveYourselfPhase2ReqProc (smsConn, managerData)
+
+SmsConn smsConn;
+SmPointer managerData;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+
+ if (verbose)
+ {
+ printf ("Client Id = %s, received SAVE YOURSELF PHASE 2 REQUEST\n",
+ client->clientId);
+ }
+
+ if (!saveInProgress)
+ {
+ /*
+ * If we are not in the middle of a checkpoint (ie. we just
+ * started the client and sent the initial save yourself), just
+ * send the save yourself phase2 now.
+ */
+
+ SmsSaveYourselfPhase2 (client->smsConn);
+ }
+ else
+ {
+ ListAddLast (WaitForPhase2List, (char *) client);
+
+ if (ListCount (WaitForInteractList) > 0 && OkToEnterInteractPhase ())
+ {
+ LetClientInteract (ListFirst (WaitForInteractList));
+ }
+ else if (OkToEnterPhase2 ())
+ {
+ StartPhase2 ();
+ }
+ }
+}
+
+
+
+void
+SaveYourselfDoneProc (smsConn, managerData, success)
+ SmsConn smsConn;
+ SmPointer managerData;
+ Bool success;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+
+ if (verbose)
+ {
+ printf("Client Id = %s, received SAVE YOURSELF DONE [Success = %s]\n",
+ client->clientId, success ? "True" : "False");
+ }
+
+ if (!ListSearchAndFreeOne (WaitForSaveDoneList, (char *) client))
+ return;
+
+ if (!success)
+ {
+ ListAddLast (FailedSaveList, (char *) client);
+ }
+
+ if (ListCount (WaitForSaveDoneList) == 0)
+ {
+ if (ListCount (FailedSaveList) > 0 && !checkpoint_from_signal)
+ PopupBadSave ();
+ else
+ FinishUpSave ();
+ }
+ else if (ListCount (WaitForInteractList) > 0 && OkToEnterInteractPhase ())
+ {
+ LetClientInteract (ListFirst (WaitForInteractList));
+ }
+ else if (ListCount (WaitForPhase2List) > 0 && OkToEnterPhase2 ())
+ {
+ StartPhase2 ();
+ }
+}
+
+
+
+void
+CloseDownClient (client)
+
+ClientRec *client;
+
+{
+ int index_deleted;
+
+ if (verbose) {
+ printf ("ICE Connection closed, IceConn fd = %d\n",
+ IceConnectionNumber (client->ice_conn));
+ printf ("\n");
+ }
+
+ SmsCleanUp (client->smsConn);
+ IceSetShutdownNegotiation (client->ice_conn, False);
+ IceCloseConnection (client->ice_conn);
+
+ client->ice_conn = NULL;
+ client->smsConn = NULL;
+
+ if (!shutdownInProgress && client_info_visible)
+ {
+ for (index_deleted = 0;
+ index_deleted < numClientListNames; index_deleted++)
+ {
+ if (clientListRecs[index_deleted] == client)
+ break;
+ }
+ }
+
+ ListSearchAndFreeOne (RunningList, (char *) client);
+
+ if (saveInProgress)
+ {
+ Status delStatus = ListSearchAndFreeOne (
+ WaitForSaveDoneList, (char *) client);
+
+ if (delStatus)
+ {
+ ListAddLast (FailedSaveList, (char *) client);
+ client->freeAfterBadSavePopup = True;
+ }
+
+ ListSearchAndFreeOne (WaitForInteractList, (char *) client);
+ ListSearchAndFreeOne (WaitForPhase2List, (char *) client);
+
+ if (delStatus && ListCount (WaitForSaveDoneList) == 0)
+ {
+ if (ListCount (FailedSaveList) > 0 && !checkpoint_from_signal)
+ PopupBadSave ();
+ else
+ FinishUpSave ();
+ }
+ else if (ListCount (WaitForInteractList) > 0 &&
+ OkToEnterInteractPhase ())
+ {
+ LetClientInteract (ListFirst (WaitForInteractList));
+ }
+ else if (!phase2InProgress &&
+ ListCount (WaitForPhase2List) > 0 && OkToEnterPhase2 ())
+ {
+ StartPhase2 ();
+ }
+ }
+
+ if (client->restartHint == SmRestartImmediately && !shutdownInProgress)
+ {
+ Clone (client, True /* use saved state */);
+
+ ListAddLast (RestartImmedList, (char *) client);
+ }
+ else if (client->restartHint == SmRestartAnyway)
+ {
+ ListAddLast (RestartAnywayList, (char *) client);
+ }
+ else if (!client->freeAfterBadSavePopup)
+ {
+ FreeClient (client, True /* free props */);
+ }
+
+ if (shutdownInProgress)
+ {
+ if (ListCount (RunningList) == 0)
+ EndSession (0);
+ }
+ else if (client_info_visible)
+ {
+ UpdateClientList ();
+
+ if (current_client_selected == index_deleted)
+ {
+ if (current_client_selected == numClientListNames)
+ current_client_selected--;
+
+ if (current_client_selected >= 0)
+ {
+ XawListHighlight (clientListWidget, current_client_selected);
+ ShowHint (clientListRecs[current_client_selected]);
+ if (client_prop_visible)
+ {
+ DisplayProps (clientListRecs[current_client_selected]);
+ }
+ }
+ }
+ else
+ {
+ if (index_deleted < current_client_selected)
+ current_client_selected--;
+ XawListHighlight (clientListWidget, current_client_selected);
+ }
+ }
+}
+
+
+
+
+void
+CloseConnectionProc (smsConn, managerData, count, reasonMsgs)
+ SmsConn smsConn;
+ SmPointer managerData;
+ int count;
+ char **reasonMsgs;
+
+{
+ ClientRec *client = (ClientRec *) managerData;
+
+ if (verbose)
+ {
+ int i;
+
+ printf ("Client Id = %s, received CONNECTION CLOSED\n",
+ client->clientId);
+
+ for (i = 0; i < count; i++)
+ printf (" Reason string %d: %s\n", i + 1, reasonMsgs[i]);
+ printf ("\n");
+ }
+
+ SmFreeReasons (count, reasonMsgs);
+
+ CloseDownClient (client);
+}
+
+
+
+Status
+NewClientProc (smsConn, managerData, maskRet, callbacksRet, failureReasonRet)
+
+SmsConn smsConn;
+SmPointer managerData;
+unsigned long *maskRet;
+SmsCallbacks *callbacksRet;
+char **failureReasonRet;
+
+{
+ ClientRec *newClient = (ClientRec *) XtMalloc (sizeof (ClientRec));
+
+ *maskRet = 0;
+
+ if (!newClient)
+ {
+ char *str = "Memory allocation failed";
+
+ if ((*failureReasonRet = (char *) XtMalloc (strlen (str) + 1)) != NULL)
+ strcpy (*failureReasonRet, str);
+
+ return (0);
+ }
+
+ newClient->smsConn = smsConn;
+ newClient->ice_conn = SmsGetIceConnection (smsConn);
+ newClient->clientId = NULL;
+ newClient->clientHostname = NULL;
+ newClient->restarted = False; /* wait till RegisterClient for true value */
+ newClient->userIssuedCheckpoint = False;
+ newClient->receivedDiscardCommand = False;
+ newClient->freeAfterBadSavePopup = False;
+ newClient->props = ListInit ();
+ newClient->discardCommand = NULL;
+ newClient->saveDiscardCommand = NULL;
+ newClient->restartHint = SmRestartIfRunning;
+
+ ListAddLast (RunningList, (char *) newClient);
+
+ if (verbose) {
+ printf("On IceConn fd = %d, client set up session mngmt protocol\n\n",
+ IceConnectionNumber (newClient->ice_conn));
+ }
+
+ /*
+ * Set up session manager callbacks.
+ */
+
+ *maskRet |= SmsRegisterClientProcMask;
+ callbacksRet->register_client.callback = RegisterClientProc;
+ callbacksRet->register_client.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsInteractRequestProcMask;
+ callbacksRet->interact_request.callback = InteractRequestProc;
+ callbacksRet->interact_request.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsInteractDoneProcMask;
+ callbacksRet->interact_done.callback = InteractDoneProc;
+ callbacksRet->interact_done.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsSaveYourselfRequestProcMask;
+ callbacksRet->save_yourself_request.callback = SaveYourselfReqProc;
+ callbacksRet->save_yourself_request.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsSaveYourselfP2RequestProcMask;
+ callbacksRet->save_yourself_phase2_request.callback =
+ SaveYourselfPhase2ReqProc;
+ callbacksRet->save_yourself_phase2_request.manager_data =
+ (SmPointer) newClient;
+
+ *maskRet |= SmsSaveYourselfDoneProcMask;
+ callbacksRet->save_yourself_done.callback = SaveYourselfDoneProc;
+ callbacksRet->save_yourself_done.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsCloseConnectionProcMask;
+ callbacksRet->close_connection.callback = CloseConnectionProc;
+ callbacksRet->close_connection.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsSetPropertiesProcMask;
+ callbacksRet->set_properties.callback = SetPropertiesProc;
+ callbacksRet->set_properties.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsDeletePropertiesProcMask;
+ callbacksRet->delete_properties.callback = DeletePropertiesProc;
+ callbacksRet->delete_properties.manager_data = (SmPointer) newClient;
+
+ *maskRet |= SmsGetPropertiesProcMask;
+ callbacksRet->get_properties.callback = GetPropertiesProc;
+ callbacksRet->get_properties.manager_data = (SmPointer) newClient;
+
+ return (1);
+}
+
+
+
+/*
+ * Xt callback invoked when a client attempts to connect.
+ */
+
+void
+NewConnectionXtProc (client_data, source, id)
+
+XtPointer client_data;
+int *source;
+XtInputId *id;
+
+{
+ IceConn ice_conn;
+ char *connstr;
+ IceAcceptStatus status;
+
+ if (shutdownInProgress)
+ {
+ /*
+ * Don't accept new connections if we are in the middle
+ * of a shutdown.
+ */
+
+ return;
+ }
+
+ ice_conn = IceAcceptConnection((IceListenObj) client_data, &status);
+ if (! ice_conn) {
+ if (verbose)
+ printf ("IceAcceptConnection failed\n");
+ } else {
+ IceConnectStatus cstatus;
+
+ while ((cstatus = IceConnectionStatus (ice_conn))==IceConnectPending) {
+ XtAppProcessEvent (appContext, XtIMAll);
+ }
+
+ if (cstatus == IceConnectAccepted) {
+ if (verbose) {
+ printf ("ICE Connection opened by client, IceConn fd = %d, ",
+ IceConnectionNumber (ice_conn));
+ connstr = IceConnectionString (ice_conn);
+ printf ("Accept at networkId %s\n", connstr);
+ free (connstr);
+ printf ("\n");
+ }
+ } else {
+ if (verbose)
+ {
+ if (cstatus == IceConnectIOError)
+ printf ("IO error opening ICE Connection!\n");
+ else
+ printf ("ICE Connection rejected!\n");
+ }
+
+ IceCloseConnection (ice_conn);
+ }
+ }
+}
+
+
+
+SetAllSensitive (on)
+
+Bool on;
+
+{
+ XtSetSensitive (mainWindow, on);
+ SetSaveSensitivity (on);
+ XtSetSensitive (clientInfoPopup, on);
+ XtSetSensitive (clientPropPopup, on);
+
+ if (on && current_client_selected >= 0)
+ XawListHighlight (clientListWidget, current_client_selected);
+}
+
+
+
+/*
+ * The real way to handle IO errors is to check the return status
+ * of IceProcessMessages. xsm properly does this.
+ *
+ * Unfortunately, a design flaw exists in the ICE library in which
+ * a default IO error handler is invoked if no IO error handler is
+ * installed. This default handler exits. We must avoid this.
+ *
+ * To get around this problem, we install an IO error handler that
+ * does a little magic. Since a previous IO handler might have been
+ * installed, when we install our IO error handler, we do a little
+ * trick to get both the previous IO error handler and the default
+ * IO error handler. When our IO error handler is called, if the
+ * previous handler is not the default handler, we call it. This
+ * way, everyone's IO error handler gets called except the stupid
+ * default one which does an exit!
+ */
+
+static IceIOErrorHandler prev_handler;
+
+void
+MyIoErrorHandler (ice_conn)
+
+IceConn ice_conn;
+
+{
+ if (prev_handler)
+ (*prev_handler) (ice_conn);
+}
+
+void
+InstallIOErrorHandler ()
+
+{
+ IceIOErrorHandler default_handler;
+
+ prev_handler = IceSetIOErrorHandler (NULL);
+ default_handler = IceSetIOErrorHandler (MyIoErrorHandler);
+ if (prev_handler == default_handler)
+ prev_handler = NULL;
+}
+
+static void
+CloseListeners(void)
+
+{
+ IceFreeListenObjs (numTransports, listenObjs);
+}
+
diff --git a/xsm.h b/xsm.h
new file mode 100644
index 0000000..74bad45
--- /dev/null
+++ b/xsm.h
@@ -0,0 +1,194 @@
+/* $Xorg: xsm.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+
+#ifndef X_NOT_POSIX
+#ifdef _POSIX_SOURCE
+#include <limits.h>
+#else
+#define _POSIX_SOURCE
+#include <limits.h>
+#undef _POSIX_SOURCE
+#endif
+#endif /* X_NOT_POSIX */
+#ifndef PATH_MAX
+#include <sys/param.h>
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+#endif /* PATH_MAX */
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#include <stdio.h>
+#undef _POSIX_SOURCE
+#else
+#include <stdio.h>
+#endif
+
+#include <ctype.h>
+#ifndef X_NOT_STDC_ENV
+#include <stdlib.h>
+#endif
+
+#include <X11/StringDefs.h>
+#include <X11/Intrinsic.h>
+
+#include <X11/SM/SMlib.h>
+
+#include "list.h"
+
+/*
+ * Each time the format of the sm's save file changes, bump up
+ * the version.
+ */
+
+#define SAVEFILE_VERSION 3
+
+#define DEFAULT_SESSION_NAME "Default"
+#define FAILSAFE_SESSION_NAME "Fail Safe"
+
+#define RESTART_MANAGERS 1
+#define RESTART_REST_OF_CLIENTS 2
+
+typedef struct _ClientRec {
+ SmsConn smsConn;
+ IceConn ice_conn;
+ char *clientId;
+ char *clientHostname;
+ List *props;
+ char *discardCommand;
+ char *saveDiscardCommand;
+
+ unsigned int restarted : 1;
+ unsigned int userIssuedCheckpoint : 1;
+ unsigned int restartHint : 2;
+ unsigned int receivedDiscardCommand : 1;
+ unsigned int freeAfterBadSavePopup : 1;
+
+} ClientRec;
+
+typedef struct _PendingClient {
+ char *clientId;
+ char *clientHostname;
+ List *props;
+} PendingClient;
+
+typedef struct _Prop {
+ char *name;
+ char *type;
+ List *values;
+} Prop;
+
+typedef struct _PropValue {
+ XtPointer value;
+ int length;
+} PropValue;
+
+
+extern int Argc;
+extern char **Argv;
+
+extern char *display_env, *non_local_display_env;
+extern char *session_env, *non_local_session_env;
+extern char *audio_env;
+
+extern Bool need_to_name_session;
+
+extern Bool remote_allowed;
+
+extern Bool verbose;
+
+extern char *sm_id;
+
+extern char *networkIds;
+extern char *session_name;
+
+extern List *RunningList;
+extern List *PendingList;
+extern List *RestartAnywayList;
+extern List *RestartImmedList;
+
+extern List *WaitForSaveDoneList;
+extern List *FailedSaveList;
+extern List *WaitForInteractList;
+extern List *WaitForPhase2List;
+
+extern Bool client_info_visible;
+extern Bool client_prop_visible;
+extern Bool client_log_visible;
+extern String *clientListNames;
+extern ClientRec **clientListRecs;
+extern int numClientListNames;
+extern int current_client_selected;
+
+extern Bool shutdownInProgress;
+extern Bool phase2InProgress;
+extern Bool saveInProgress;
+extern Bool shutdownCancelled;
+extern Bool wantShutdown;
+
+extern int sessionNameCount;
+extern String *sessionNamesShort;
+extern String *sessionNamesLong;
+extern Bool *sessionsLocked;
+
+extern int num_clients_in_last_session;
+
+extern char **non_session_aware_clients;
+extern int non_session_aware_count;
+
+extern XtAppContext appContext;
+extern Widget topLevel;
+extern Widget mainWindow;
+
+extern void fprintfhex ();
+
+#ifndef X_NOT_STDC_ENV
+#define Strstr strstr
+#else
+extern char *Strstr();
+#endif
+
+/* Fix ISC brain damage. When using gcc fdopen isn't declared in <stdio.h>. */
+#if defined(ISC) && __STDC__
+extern FILE *fdopen(int, char const *);
+#endif
+
+#if defined(sun) && defined(SVR4)
+extern int System();
+#define system(s) System(s)
+#endif
+
+#ifdef XKB
+#include <X11/extensions/XKBbells.h>
+#endif
diff --git a/xsm.man b/xsm.man
new file mode 100644
index 0000000..280621c
--- /dev/null
+++ b/xsm.man
@@ -0,0 +1,343 @@
+.\" $Xorg: xsm.man,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $
+.\" Copyright 1994,1998 The Open Group
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation.
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+.\" OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" Except as contained in this notice, the name of The Open Group shall
+.\" not be used in advertising or otherwise to promote the sale, use or
+.\" other dealings in this Software without prior written authorization
+.\" from The Open Group.
+.TH XSM 1 "Release 6.4" "X Version 11"
+.SH NAME
+xsm \- X Session Manager
+.SH SYNOPSIS
+.B xsm
+[-display display] [-session sessionName] [-verbose]
+.SH DESCRIPTION
+.PP
+\fIxsm\fP is a session manager. A session is a group of applications, each
+of which has a particular state. \fIxsm\fP allows you to create arbitrary
+sessions - for example, you might have a "light" session, a "development"
+session, or an "xterminal" session. Each session can have its own set of
+applications. Within a session, you can perform a "checkpoint" to save
+application state, or a "shutdown" to save state and exit the session. When
+you log back in to the system, you can load a specific session, and you can
+delete sessions you no longer want to keep.
+.br
+.sp
+Some session managers simply allow you to manually specify a list of
+applications to be started in a session. \fIxsm\fP is more powerful
+because it lets you run applications and have them automatically become
+part of the session. On a simple level, \fIxsm\fP is useful because it
+gives you this ability to easily define which applications are in a session.
+The true power of \fIxsm\fP, however, can be taken advantage of when more
+and more applications learn to save and restore their state.
+.SH OPTIONS
+.TP 8
+.B \-display \fIdisplay\fP
+Causes \fIxsm\fP to connect to the specified X display.
+.TP 8
+.B \-session \fIsessionName\fP
+Causes \fIxsm\fP to load the specified session, bypassing the session menu.
+.TP 8
+.B \-verbose
+Turns on debugging information.
+.SH SETUP
+.SS .xsession file
+Using \fIxsm\fP requires a change to your \fI.xsession\fP file:
+.PP
+The last program executed by your \fI.xsession\fP file should
+be \fIxsm\fP. With this configuration, when the user chooses to shut
+down the session using \fIxsm\fP, the session will truly be over.
+.PP
+Since the goal of the session manager is to restart clients when
+logging into a session, your .xsession file, in general, should not directly
+start up applications. Rather, the applications should be started within
+a session. When \fIxsm\fP shuts down the session, \fIxsm\fP will know to
+restart these applications. Note however that there are some types of
+applications that are not "session aware". \fIxsm\fP allows you to
+manually add these applications to your session (see the section titled
+\fIClient List\fP).
+.PP
+.SS SM_SAVE_DIR environment variable
+If the \fISM_SAVE_DIR\fP environment variable is defined, \fIxsm\fP will
+save all configuration files in this directory. Otherwise, they will be
+stored in the user's home directory. Session aware applications are also
+encouraged to save their checkpoint files in the \fISM_SAVE_DIR\fP directory,
+although the user should not depend on this convention.
+.PP
+.SS Default Startup Applications
+The first time \fIxsm\fP is started, it will need to locate a list of
+applications to start up. For example, this list might include a window
+manager, a session management proxy, and an xterm. \fIxsm\fP will first
+look for the file \fI.xsmstartup\fP in the user's home directory. If that
+file does not exists, it will look for the \fIsystem.xsm\fP file that was
+set up at installation time. Note that \fIxsm\fP provides a "fail safe"
+option when the user chooses a session to start up. The fail safe option
+simply loads the default applications described above.
+.PP
+Each line in the startup file should contain a command to start an application.
+A sample startup file might look this:
+.PP
+<start of file>
+.br
+twm
+.br
+smproxy
+.br
+xterm
+.br
+<end of file>
+.PP
+.SH STARTING A SESSION
+When \fIxsm\fP starts up, it first checks to see if the user previously
+saved any sessions. If no saved sessions exist, \fIxsm\fP starts up a set
+of default applications (as described above in the section titled
+\fIDefault Startup Applications\fP). If at least one session exists, a
+session menu is presented. The \fB[-session sessionName]\fR option forces the
+specified session to be loaded, bypassing the session menu.
+.SS The session menu
+The session menu presents the user with a list of sessions to choose from.
+The user can change the currently selected session with the mouse, or by
+using the up and down arrows on the keyboard. Note that sessions which are
+locked (i.e. running on a different display) can not be loaded or deleted.
+.PP
+The following operations can be performed from the session menu:
+.PP
+.TP 22
+.B Load Session
+Pressing this button will load the currently selected session. Alternatively,
+hitting the Return key will also load the currently selected session, or the
+user can double click a session from the list.
+.TP 22
+.B Delete Session
+This operation will delete the currently selected session, along with all
+of the application checkpoint files associated with the session. After
+pressing this button, the user will be asked to press the button a second
+time in order to confirm the operation.
+.TP 22
+.B Default/Fail Safe
+\fIxsm\fP will start up a set of default applications (as described above
+in the section titled \fIDefault Startup Applications\fP). This is useful
+when the user wants to start a fresh session, or if the session configuration
+files were corrupted and the user wants a "fail safe" session.
+.TP 22
+.B Cancel
+Pressing this button will cause \fIxsm\fP to exit. It can also be used to
+cancel a "Delete Session" operation.
+.PP
+.SH CONTROLLING A SESSION
+After \fIxsm\fP determines which session to load, it brings up its main
+window, then starts up all applications that are part of
+the session. The title bar for the session manager's main window will
+contain the name of the session that was loaded.
+.PP
+The following options are available from \fIxsm\fP's main window:
+.TP 18
+.B Client List
+Pressing this button brings up a window containing a list of all clients that
+are in the current session. For each client, the host machine that the client
+is running on is presented. As clients are added and removed from the session,
+this list is updated to reflect the changes. The user is able to control how
+these clients are restarted (see below).
+.br
+.sp
+By pressing the \fBView Properties\fR
+button, the user can view the session management properties associated with
+the currently selected client.
+.br
+.sp
+By pressing the \fBClone\fR button, the user can start a copy of the selected
+application.
+.br
+.sp
+By pressing the \fBKill Client\fR button, the user can remove a client from
+the session.
+.br
+.sp
+By selecting a restart hint from the \fBRestart Hint\fR menu, the user can
+control the restarting of a client. The following hints are available:
+.br
+.sp
+.B \-
+The \fBRestart If Running\fR hint indicates that the client should be
+restarted in the next session if it is connected to the session manager at
+the end of the current session.
+.br
+.sp
+.B \-
+The \fBRestart Anyway\fR hint indicates that the client should be restarted
+in the next session even if it exits before the current session is terminated.
+.br
+.sp
+.B \-
+The \fBRestart Immediately\fR hint is similar to the \fBRestart Anyway\fR hint,
+but in addition, the client is meant to run continuously. If the client exits,
+the session manager will try to restart it in the current session.
+.br
+.sp
+.B \-
+The \fBRestart Never\fR hint indicates that the client should not be restarted
+in the next session.
+.br
+.sp
+Note that all X applications may not be "session aware". Applications that
+are not session aware are ones that do not support the X Session Management
+Protocol or they can not be detected by the Session Management Proxy (see the
+section titled \fITHE PROXY\fP). \fIxsm\fP allows the user to manually add
+such applications to the session. The bottom of the \fIClient List\fP window
+contains a text entry field in which application commands can be typed in.
+Each command should go on its own line. This information will be saved with
+the session at checkpoint or shutdown time. When the session is restarted,
+\fIxsm\fP will restart these applications in addition to the regular "session
+aware" applications.
+.br
+.sp
+Pressing the \fBDone\fR button removes the \fBClient List\fR window.
+.TP 18
+.B Session Log...
+The Session Log window presents useful information about the session. For
+example, when a session is restarted, all of the restart commands will be
+displayed in the log window.
+.TP 18
+.B Checkpoint
+By performing a checkpoint, all applications that are in the session are
+asked to save their state. Not every application will save its complete
+state, but at a minimum, the session manager is guaranteed that it will
+receive the command required to restart the application (along with all
+command line options). A window manager participating in the session
+should guarantee that the applications will come back up with the same
+window configurations.
+.br
+.sp
+If the session being checkpointed was never assigned a name, the user will
+be required to specify a session name. Otherwise, the user can perform the
+checkpoint using the current session name, or a new session name can be
+specified. If the session name specified already exists, the user will be
+given the opportunity to specify a different name or to overwrite the
+already existing session. Note that a session which is locked can not be
+overwritten.
+.br
+.sp
+When performing a checkpoint, the user must specify a \fBSave Type\fR
+which informs the applications in the session how much state they should save.
+.br
+.sp
+The \fBLocal\fR
+type indicates that the application should save enough information to restore
+the state as seen by the user. It should not affect the state as seen by
+other users. For example, an editor would create a temporary file containing
+the contents of its editing buffer, the location of the cursor, etc...
+.br
+.sp
+The \fBGlobal\fR
+type indicates that the application should commit all of its data to
+permanent, globally accessible storage. For example, the editor would
+simply save the edited file.
+.br
+.sp
+The \fBBoth\fR
+type indicates that the application should do both of these. For example,
+the editor would save the edited file, then create a temporary file with
+information such as the location of the cursor, etc...
+.br
+.sp
+In addition to the \fBSave Type\fR, the user must specify an
+\fBInteract Style\fR.
+.br
+.sp
+The \fBNone\fR type indicates that the application should not interact with
+the user while saving state.
+.br
+.sp
+The \fBErrors\fR type indicates that the application may interact with
+the user only if an error condition arises.
+.br
+.sp
+The \fBAny\fR type indicates that the application may interact with
+the user for any purpose. Note that \fIxsm\fP will only allow one
+application to interact with the user at a time.
+.br
+.sp
+.sp
+After the checkpoint is completed, \fIxsm\fP will, if necessary, display a
+window containing the list of applications which did not report a successful
+save of state.
+.TP 18
+.B Shutdown
+A shutdown provides all of the options found in a checkpoint, but in addition,
+can cause the session to exit. Note that if the interaction style is
+\fBErrors\fR or \fBAny\fR, the user may cancel the shutdown. The user may
+also cancel the shutdown if any of the applications report an
+unsuccessful save of state.
+.br
+.sp
+The user may choose to shutdown the session with our without performing a
+checkpoint.
+.PP
+.SH HOW \fIXSM\fP RESPONDS TO SIGNALS
+\fIxsm\fP will respond to a SIGTERM signal by performing a shutdown with
+the following options: fast, no interaction, save type local. This allows
+the user's session to be saved when the system is being shutdown. It can
+also be used to perform a remote shutdown of a session.
+.br
+.sp
+\fIxsm\fP will respond to a SIGUSR1 signal by performing a checkpoint with
+the following options: no interaction, save type local. This signal can be
+used to perform a remote checkpoint of a session.
+.PP
+.SH THE PROXY
+Since not all applications have been ported to support the X Session
+Management Protocol, a proxy service exists to allow "old" clients to
+work with the session manager. In order for the proxy to detect an
+application joining a session, one of the following must be true:
+.br
+.sp
+- The application maps a top level window containing the
+\fBWM_CLIENT_LEADER\fR property. This property provides a pointer to
+the client leader window which contains the \fBWM_CLASS\fR, \fBWM_NAME\fR,
+\fBWM_COMMAND\fR, and \fBWM_CLIENT_MACHINE\fR properties.
+.br
+.sp
+or ...
+.br
+.sp
+- The application maps a top level window which does not contain the
+\fBWM_CLIENT_LEADER\fR property. However, this top level window
+contains the \fBWM_CLASS\fR, \fBWM_NAME\fR, \fBWM_COMMAND\fR, and
+\fBWM_CLIENT_MACHINE\fR properties.
+.PP
+An application that support the \fBWM_SAVE_YOURSELF\fR protocol will receive
+a \fBWM_SAVE_YOURSELF\fR client message each time the session manager issues
+a checkpoint or shutdown. This allows the application to save state. If
+an application does not support the \fBWM_SAVE_YOURSELF\fR protocol, then
+the proxy will provide enough information to the session manager to restart
+the application (using \fBWM_COMMAND\fR), but no state will be restored.
+.PP
+.SH REMOTE APPLICATIONS
+\fIxsm\fP requires a remote execution protocol in order to restart
+applications on remote machines. Currently, \fIxsm\fP supports the
+\fIrstart\fP protocol. In order to restart an application on remote
+machine \fBX\fR, machine \fBX\fR must have \fIrstart\fP installed. In
+the future, additional remote execution protocols may be supported.
+.SH SEE ALSO
+smproxy(1), rstart(1)
+.SH AUTHORS
+Ralph Mor, X Consortium
+.br
+Jordan Brown, Quarterdeck Office Systems
diff --git a/xtwatch.c b/xtwatch.c
new file mode 100644
index 0000000..4a42e27
--- /dev/null
+++ b/xtwatch.c
@@ -0,0 +1,124 @@
+/* $Xorg: xtwatch.c,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+#include <X11/ICE/ICElib.h>
+#include <X11/Intrinsic.h>
+#include "xsm.h"
+
+extern void CloseDownClient ();
+
+
+
+Status
+InitWatchProcs (appContext)
+
+XtAppContext appContext;
+
+{
+ void _XtIceWatchProc ();
+
+ return (IceAddConnectionWatch (_XtIceWatchProc, (IcePointer) appContext));
+}
+
+
+void
+_XtIceWatchProc (ice_conn, client_data, opening, watch_data)
+
+IceConn ice_conn;
+IcePointer client_data;
+Bool opening;
+IcePointer *watch_data;
+
+{
+ if (opening)
+ {
+ XtAppContext appContext = (XtAppContext) client_data;
+ void _XtProcessIceMsgProc ();
+
+ *watch_data = (IcePointer) XtAppAddInput (
+ appContext,
+ IceConnectionNumber (ice_conn),
+ (XtPointer) XtInputReadMask,
+ _XtProcessIceMsgProc,
+ (XtPointer) ice_conn);
+ }
+ else
+ {
+ XtRemoveInput ((XtInputId) *watch_data);
+ }
+}
+
+
+void
+_XtProcessIceMsgProc (client_data, source, id)
+
+XtPointer client_data;
+int *source;
+XtInputId *id;
+
+{
+ IceConn ice_conn = (IceConn) client_data;
+ IceProcessMessagesStatus status;
+
+ status = IceProcessMessages (ice_conn, NULL, NULL);
+
+ if (status == IceProcessMessagesIOError)
+ {
+ List *cl;
+ int found = 0;
+
+ if (verbose)
+ {
+ printf ("IO error on connection (fd = %d)\n",
+ IceConnectionNumber (ice_conn));
+ printf ("\n");
+ }
+
+ for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
+ {
+ ClientRec *client = (ClientRec *) cl->thing;
+
+ if (client->ice_conn == ice_conn)
+ {
+ CloseDownClient (client);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ /*
+ * The client must have disconnected before it was added
+ * to the session manager's running list (i.e. before the
+ * NewClientProc callback was invoked).
+ */
+
+ IceSetShutdownNegotiation (ice_conn, False);
+ IceCloseConnection (ice_conn);
+ }
+ }
+}
diff --git a/xtwatch.h b/xtwatch.h
new file mode 100644
index 0000000..4ee3b00
--- /dev/null
+++ b/xtwatch.h
@@ -0,0 +1,27 @@
+/* $Xorg: xtwatch.h,v 1.4 2001/02/09 02:06:01 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1993, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+******************************************************************************/
+
+extern Status InitWatchProcs ();