a162485571c129b2f54194b61ff4584a99f6571c
[oweals/cde.git] / SmXSMP.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * (c) Copyright 1995 Digital Equipment Corporation.
25  * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
26  * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
27  * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
28  * (c) Copyright 1993, 1994, 1995 Novell, Inc. 
29  * (c) Copyright 1995 FUJITSU LIMITED.
30  * (c) Copyright 1995 Hitachi.
31  *
32  * $TOG: SmXSMP.c /main/40 1999/01/18 15:42:07 samborn $
33  */
34
35 /*************************************<+>*************************************
36  *****************************************************************************
37  **
38  **  File:        SmXSMP.c
39  **
40  **  Project:     DT Session Manager (dtsession)
41  **
42  *****************************************************************************
43  *************************************<+>*************************************/
44
45 #include <stdio.h>
46 #include <unistd.h>
47 #include <string.h>
48
49 #include "SmXSMP.h"
50 #include "SmAuth.h"
51 #include "SmWatch.h"
52 #include "SmProp.h"
53 #include "Sm.h"
54 #include "SmUI.h"
55 #include "SmSave.h"
56 #include "SmRestore.h"
57 #include "SmGlobals.h"
58
59 #include <X11/Xlib.h>
60 #include <X11/Intrinsic.h>
61 #include <X11/Xatom.h>
62 #include <X11/SM/SMlib.h>
63
64 #include <Dt/MsgLog.h>
65 #include <Dt/Lock.h>
66 #include <Dt/Wsm.h>
67
68 #include <Tt/tt_c.h>
69
70 /*
71  * Private constants
72  */
73 #define ERRORMSGLEN                     256
74 #define GET_CLIENT_WORKSPACE_MSG        "GetWsmClients"
75
76
77 /*
78  * Private variables
79  */
80 static Boolean          authenticationInitialized = False;
81 static char             * networkIds;
82
83 /*
84  * Private functions
85  */
86 static void InitializeXSMPGlobals ();
87
88 static void PutSessionManagerOnRootWindow (
89         char                    *networkIds);
90
91 static void InitializeSaveState (
92         Boolean                 startUp);
93
94 static Boolean SendGetWsmClientsMessage ();
95
96 static int GetCurrentWorkspaceNumber ();
97
98 static void ProcessInteract (
99         ClientRecPtr            client,
100         Boolean                 getWsmClientOK);
101
102 static void CancelShutdown ();
103
104 static void FreeProps (
105         PropertyRecPtr          pProp);
106
107 static void ProcessSaveYourselfResponses ();
108
109
110 /*
111  * Public variables
112  */
113 XSMPSettings            smXSMP;
114 ClientRecPtr            connectedList;
115 Atom                    XaSmClientId;
116
117
118 /*
119  * Public functins
120  */
121 void InstallIOErrorHandler ();
122
123 Status NewClientProc (
124         SmsConn                 smsConn,
125         SmPointer               managerData,
126         unsigned long           *maskRet,
127         SmsCallbacks            *callbacksRet,
128         char                    **failureReasonRet);
129
130 void NewConnectionXtProc (
131         XtPointer               client_data,
132         int                     *source,
133         XtInputId               *id);
134
135 Status RegisterClientProc (
136         SmsConn                 smsConn,
137         SmPointer               managerData,
138         char                    *previousId);
139
140 void InteractRequestProc (
141         SmsConn                 smsConn,
142         SmPointer               managerData,
143         int                     dialogType);
144
145 void InteractDoneProc (
146         SmsConn                 smsConn,
147         SmPointer               managerData,
148         Bool                    cancelShutdown);
149
150 void SaveYourselfReqProc (
151         SmsConn                 smsConn,
152         SmPointer               managerData,
153         int                     saveType,
154         Bool                    shutdown,
155         int                     interactStyle,
156         Bool                    fast,
157         Bool                    global);
158
159 void SaveYourselfPhase2ReqProc (
160         SmsConn                 smsConn,
161         SmPointer               managerData);
162
163 void SaveYourselfDoneProc (
164         SmsConn                 smsConn,
165         SmPointer               managerData,
166         Bool                    success);
167
168 void CloseConnectionProc (
169         SmsConn                 smsConn,
170         SmPointer               managerData,
171         int                     count,
172         char                    **reasonMsgs);
173
174 void CompleteXSMPSave ();
175
176 void CloseDownClient (
177         ClientRecPtr            client );
178
179 /*
180  * List manipulation functions
181  */
182 void AddClient (
183         ClientRecPtr            newClient);
184
185 /*
186  * Functions
187  */
188
189 Boolean InitXSMP (void)
190 {
191         char            errorMsg[ERRORMSGLEN];
192         char            *env;
193         int             i;
194
195         InitializeXSMPGlobals ();
196
197         InstallIOErrorHandler ();
198
199         if (!SmsInitialize (SM_VENDOR_NAME, SM_RELEASE_NAME, 
200                                 NewClientProc, NULL,
201                                 HostBasedAuthProc, 
202                                 ERRORMSGLEN, errorMsg)) {
203                 DtMsgLogMessage (smGD.programName, DtMsgLogError, errorMsg);
204                 PostXSMPFailureDialog (XSMP_FAILURE_SMS_INITIALIZE, True);
205         }
206
207         if (!IceListenForConnections (&smXSMP.numTransports, 
208                                       &smXSMP.listenObjs, 
209                                       ERRORMSGLEN, errorMsg)) {
210                 DtMsgLogMessage (smGD.programName, DtMsgLogError, errorMsg);
211                 PostXSMPFailureDialog (XSMP_FAILURE_ICE_LISTEN, True);
212         }
213
214         if (!SetAuthentication (smXSMP.numTransports, 
215                                 smXSMP.listenObjs, 
216                                 &smXSMP.authDataEntries))
217                 PostXSMPFailureDialog (XSMP_FAILURE_AUTHENTICATION, False);
218
219         authenticationInitialized = True;
220
221         if (!InitWatchProcs (smGD.appCon))
222                 PostXSMPFailureDialog (XSMP_FAILURE_ICE_ADD_WATCH, False);
223
224         for (i = 0; i < smXSMP.numTransports; i++) {
225                 XtAppAddInput (smGD.appCon,
226                         IceGetListenConnectionNumber (smXSMP.listenObjs[i]),
227                         (XtPointer) XtInputReadMask,
228                         NewConnectionXtProc, (XtPointer) smXSMP.listenObjs[i]);
229         }
230
231         networkIds = IceComposeNetworkIdList (smXSMP.numTransports, 
232                                 smXSMP.listenObjs); 
233         if (!networkIds)
234                 PostXSMPFailureDialog (XSMP_FAILURE_ICE_COMPOSE_IDS, False);
235
236         env = (char *) XtMalloc (strlen (SM_SESSION_MANAGER) + 
237                                  strlen (networkIds) + 2);
238         if (!env) {
239                 free (networkIds);
240                 PostXSMPFailureDialog (XSMP_FAILURE_MALLOC, False);
241         } else {
242                 (void) sprintf (env, "%s=%s", SM_SESSION_MANAGER, networkIds);
243                 (void) putenv (env);
244         }
245
246         PutSessionManagerOnRootWindow (networkIds);
247
248         XaSmClientId = XInternAtom(smGD.display, SM_CLIENT_ID, False);
249
250 #ifdef DEBUG
251         printf ("%s\n", env);
252 #endif /* DEBUG */
253
254         return (True);
255 }
256
257
258 static void
259 PutSessionManagerOnRootWindow (
260         char                    *networkIds)
261 {
262         Atom                    sessionManager;
263
264         sessionManager = XInternAtom(smGD.display, SM_SESSION_MANAGER, False);
265
266         XChangeProperty(smGD.display, RootWindow(smGD.display, 0),
267                 sessionManager, XA_STRING, 8, PropModeReplace,
268                 (unsigned char *) networkIds, strlen((char *)networkIds));
269 }
270
271
272 static void
273 InitializeSaveState (
274         Boolean                 startUp)
275 {
276         smXSMP.saveState.global = False;
277         smXSMP.saveState.shutdown = False;
278         smXSMP.saveState.interactStyle = SmInteractStyleAny;
279         smXSMP.saveState.clientInteracting = False;
280         smXSMP.saveState.inProgress = False;
281         smXSMP.saveState.doneSuccess = True;
282         smXSMP.saveState.saveComplete = False;
283         smXSMP.saveState.interactCount = 0;
284         smXSMP.saveState.numClientIds = 0;
285         smXSMP.saveState.interactClient = NULL;
286         if (startUp) {
287                 smXSMP.saveState.clientIds = NULL;
288                 smXSMP.saveState.workspaceNums = NULL;
289         } else {
290                 if (smXSMP.saveState.clientIds) {
291                         free (smXSMP.saveState.clientIds);
292                         smXSMP.saveState.clientIds = NULL;
293                 }
294                 if (smXSMP.saveState.workspaceNums) {
295                         free (smXSMP.saveState.workspaceNums);
296                         smXSMP.saveState.workspaceNums = NULL;
297                 }
298         }
299 }
300
301 static void 
302 InitializeXSMPGlobals (void)
303 {
304         smXSMP.authDataEntries = NULL;
305
306         connectedList = NULL;   
307
308         smXSMP.xsmpDbList = NULL;
309         
310         smXSMP.dbVersion = SM_VENDOR_NAME;
311
312         smXSMP.dbSessionId = SM_RELEASE_NAME;
313
314         InitializeSaveState (True);
315 }
316
317
318 /*ARGSUSED*/
319 Status
320 NewClientProc (
321         SmsConn                 smsConn,
322         SmPointer               managerData,
323         unsigned long           *maskRet,
324         SmsCallbacks            *callbacksRet,
325         char                    **failureReasonRet)
326 {
327         ClientRecPtr            newClient;
328         
329 #ifdef DEBUG
330         (void) printf ("\nNewClientProc: IceConn fd = %d\n",
331                         IceConnectionNumber (SmsGetIceConnection (smsConn)));
332 #endif /* DEBUG */
333
334         newClient = (ClientRecPtr) XtMalloc (sizeof (ClientRec));
335         if (!newClient) {
336                 char            *str;
337
338                 str = strdup ((char *) GETMESSAGE (4, 5, 
339                         "Unable to malloc memory for operation."));
340                 if (str) {
341                         if ((*failureReasonRet = (char *) 
342                                 XtMalloc ( strlen (str) + 1)) != NULL) 
343                                 strcpy (*failureReasonRet, str);
344       
345                         DtMsgLogMessage (smGD.programName, DtMsgLogError, str);
346                         free (str);
347                 }
348
349                 return (0);
350         }
351
352         *maskRet = 0;
353
354         newClient->smConn = smsConn;
355         newClient->iceConn = SmsGetIceConnection (smsConn);
356         newClient->clientId = NULL;
357         newClient->clientHost = NULL;
358         newClient->screenNum = 0;
359         newClient->restartHint = SmRestartIfRunning;
360         newClient->props = NULL;
361         newClient->active = False;
362         newClient->saveYourselfDone = False;
363         newClient->saveYourselfP2Requested = False;
364         newClient->interactRequested = False;
365         newClient->next = NULL;
366
367         AddClient (newClient);
368
369         *maskRet |= SmsRegisterClientProcMask;
370         callbacksRet->register_client.callback = RegisterClientProc;
371         callbacksRet->register_client.manager_data = (SmPointer) newClient;
372
373         *maskRet |= SmsInteractRequestProcMask;
374         callbacksRet->interact_request.callback = InteractRequestProc;
375         callbacksRet->interact_request.manager_data = (SmPointer) newClient;
376
377         *maskRet |= SmsInteractDoneProcMask;
378         callbacksRet->interact_done.callback = InteractDoneProc;
379         callbacksRet->interact_done.manager_data = (SmPointer) newClient;
380
381         *maskRet |= SmsSaveYourselfRequestProcMask;
382         callbacksRet->save_yourself_request.callback = SaveYourselfReqProc;
383         callbacksRet->save_yourself_request.manager_data = 
384                 (SmPointer) newClient;
385
386         *maskRet |= SmsSaveYourselfP2RequestProcMask;
387         callbacksRet->save_yourself_phase2_request.callback =
388                 SaveYourselfPhase2ReqProc;
389         callbacksRet->save_yourself_phase2_request.manager_data =
390                 (SmPointer) newClient;
391
392         *maskRet |= SmsSaveYourselfDoneProcMask;
393         callbacksRet->save_yourself_done.callback = SaveYourselfDoneProc;
394         callbacksRet->save_yourself_done.manager_data  = (SmPointer) newClient;
395
396         *maskRet |= SmsCloseConnectionProcMask;
397         callbacksRet->close_connection.callback = CloseConnectionProc;
398         callbacksRet->close_connection.manager_data = (SmPointer) newClient;
399
400         *maskRet |= SmsSetPropertiesProcMask;
401         callbacksRet->set_properties.callback = SetPropertiesProc;
402         callbacksRet->set_properties.manager_data = (SmPointer) newClient;
403
404         *maskRet |= SmsDeletePropertiesProcMask;
405         callbacksRet->delete_properties.callback = DeletePropertiesProc;
406         callbacksRet->delete_properties.manager_data = (SmPointer) newClient;
407
408         *maskRet |= SmsGetPropertiesProcMask;
409         callbacksRet->get_properties.callback = GetPropertiesProc;
410         callbacksRet->get_properties.manager_data = (SmPointer) newClient;
411
412         return (True);
413 }
414
415
416 /*ARGSUSED*/
417 void
418 NewConnectionXtProc (
419         XtPointer               client_data,
420         int                     *source,
421         XtInputId               *id)
422 {
423         IceConn                 ice_conn;
424         IceAcceptStatus         status;
425
426 #ifdef DEBUG
427         (void) printf ("NewConnectionXtProc [fd = %d]\n", *source);
428 #endif /* DEBUG */
429
430         if (smXSMP.saveState.shutdown == True)
431         {
432                 /*
433                  * Don't accept new connections if we are in the middle
434                  * of a shutdown.
435                  */
436
437                 return;
438         }
439
440         ice_conn = IceAcceptConnection((IceListenObj) client_data, &status);
441
442         if (!ice_conn) {
443                 char            *message;
444
445                 message = strdup ((char *) GETMESSAGE (40, 20, 
446                                 "IceAcceptConnection failed."));
447                 
448                 if (message) {
449                         DtMsgLogMessage (smGD.programName, DtMsgLogError, 
450                                         message);
451                         free (message);
452                 }
453         } else {
454                 IceConnectStatus cstatus;
455
456                 while ((cstatus = IceConnectionStatus (ice_conn)) ==
457                                 IceConnectPending) {
458                         XtAppProcessEvent (smGD.appCon, XtIMAll);
459                 }
460
461                 if (cstatus == IceConnectAccepted) {
462 #ifdef DEBUG
463                         char                    *connstr;
464                         printf ("ICE Connection opened IceConn fd = %d, ",
465                                 IceConnectionNumber (ice_conn));
466                         connstr = IceConnectionString (ice_conn);
467                         printf ("\tAccept at networkId %s\n\n", connstr);
468                         free (connstr);
469 #else
470                         return;
471 #endif /* DEBUG */
472                 } else {
473 #ifdef DEBUG
474                         if (cstatus == IceConnectIOError)
475                                 printf ("IO error opening ICE Connection!\n");
476                         else
477                                 printf ("ICE Connection rejected!\n");
478 #endif /* DEBUG */
479                 }
480         }
481 }
482
483
484
485 Status 
486 RegisterClientProc (
487         SmsConn                 smsConn,
488         SmPointer               managerData,
489         char                    *previousId)
490 {
491         ClientRec               *client = (ClientRec *) managerData;
492         char                    *id = previousId;
493         Boolean                 sendSave = False;
494         char                    *pchar;
495 #ifdef DEBUG
496         int                     i;
497 #endif /* DEBUG */
498
499 #ifdef DEBUG
500         (void) printf ("Received REGISTER CLIENT [%d] - id = %s\n", 
501                         smsConn, previousId ? previousId : "New Client");
502 #endif /* DEBUG */
503
504         if (!previousId) {
505                 id = SmsGenerateClientID (smsConn);
506                 sendSave = True;
507         }
508         else {
509                 ClientRecPtr            pClientRec;
510                 XSMPClientDBRecPtr      pDbRec;
511                 Boolean                 found = False;
512
513                 for (pClientRec = connectedList; pClientRec != NULL; 
514                         pClientRec = pClientRec->next) {
515                         if (!strcmp (pClientRec->clientId, previousId)) {
516 #ifdef DEBUG
517                                 (void) printf ("\tAlready connected.\n");
518 #endif /* DEBUG */
519                                 if (!pClientRec->active)
520                                         /*
521                                          * A client that terminated is 
522                                          * re-using its id
523                                          */
524                                         found = True;
525                                 break;
526                         }
527                 }
528
529                 for (pDbRec = smXSMP.xsmpDbList; 
530                      pDbRec != NULL && found == False; 
531                      pDbRec = pDbRec->next) {
532                         if (!strcmp (pDbRec->clientId, previousId)) {
533 #ifdef DEBUG
534                                 (void) printf ("\tClient in DB.\n");
535 #endif /* DEBUG */
536                                 found = True;
537                         }
538                 }
539
540                 if (!found) {
541                         /*
542                          * The client is using an invalid id or
543                          * this clientID is already being used.
544                          * Reject the connection.
545                          */
546 #ifdef DEBUG
547                         (void) printf ("\tID is NOT valid.\n");
548 #endif /* DEBUG */
549                         free (previousId);
550                         return (0);
551                 }
552         }
553
554         client->clientId = strdup (id);
555         pchar = SmsClientHostName (smsConn);
556         if (pchar)
557                 client->clientHost = (strchr (pchar, '/')) + 1;
558         else
559                 client->clientHost = pchar;
560         client->active = True;
561
562         SmsRegisterClientReply (smsConn, id);
563
564         if (sendSave)
565                 SmsSaveYourself (smsConn, SmSaveLocal, False, 
566                                 SmInteractStyleNone, False);
567
568 #ifdef DEBUG
569         (void) printf ("CLIENTS REGISTERED:\n");
570
571         for (i = 1, client = connectedList; 
572              client != NULL; 
573              i++, client = client->next) {
574
575                 if (client->active)
576                         (void) printf ("\t[%2d] = %s\n", i, client->clientId);
577         }
578 #endif /* DEBUG */
579
580         return (1);
581 }
582
583
584 /*ARGSUSED*/
585 void 
586 InteractRequestProc (
587         SmsConn                 smsConn,
588         SmPointer               managerData,
589         int                     dialogType)
590 {
591         ClientRecPtr            client = (ClientRecPtr) managerData;
592         Boolean                 getWsmClientOK = True;
593
594 #ifdef DEBUG
595         (void) printf ("Received INTERACT REQUEST [%d]\n", smsConn);
596         if (dialogType == SmDialogError)
597                 (void) printf ("\tSmDialogError\n");
598         else if (dialogType == SmDialogNormal)
599                 (void) printf ("\tSmDialogNormal\n");
600         else
601                 (void) printf ("\tSMlib Error: should have checked for bad value\n");
602 #endif /* DEBUG */
603
604         client->interactRequested = True;
605
606         if (smXSMP.saveState.interactCount == 0) {
607                 /*
608                  * Only need to get the Wkspace list once for a save
609                  */
610                 smXSMP.saveState.interactCount++;
611
612                 if (!SendGetWsmClientsMessage ()) {
613                         char            *pch;
614
615                         pch = strdup ((char *) GETMESSAGE (40, 17, 
616                                 "An attempt to get a client list from the 'Window Manager' failed."));
617                         if (pch) {
618                                 DtMsgLogMessage (smGD.programName, 
619                                                  DtMsgLogWarning, pch);
620                                 free (pch);
621                         }
622                         getWsmClientOK = False;
623                 } else {
624                         /*
625                          * Cann't do anything else until the GetWsmClients
626                          * message handler is invoked.
627                          *
628                          * Must cache this client because it is needed in the 
629                          * GetWsmClients callback and ToolTalk apparently 
630                          * doesn't allow 'client_data' to be assigned to its
631                          * callback funtions.
632                          */
633                         smXSMP.saveState.interactClient = client;
634                         return;
635                 }
636         }
637
638         ProcessInteract (client, getWsmClientOK);
639 }
640
641
642 /*ARGSUSED*/
643 void 
644 InteractDoneProc (
645         SmsConn smsConn,
646         SmPointer               managerData,
647         Bool                    cancelShutdown)
648 {
649         ClientRecPtr            client = (ClientRecPtr) managerData;
650
651 #ifdef DEBUG
652         (void) printf ("Received INTERACT DONE [%d] - Cancel Shutdown = %s\n",
653                         smsConn, cancelShutdown ? "True" : "False");
654 #endif /* DEBUG */
655
656         client->interactRequested = False;
657         smXSMP.saveState.clientInteracting = False;
658
659         if (cancelShutdown)
660                 smXSMP.saveState.shutdownCanceled = True;
661
662         if (cancelShutdown &&
663             smXSMP.saveState.shutdown == True &&
664             (smXSMP.saveState.interactStyle == SmInteractStyleErrors ||
665             (smXSMP.saveState.interactStyle == SmInteractStyleAny))) {
666                 
667                 ClientRecPtr            pClientRec;
668                 char                    *pch;
669
670                 for (pClientRec = connectedList; pClientRec != NULL; 
671                         pClientRec = pClientRec->next) {
672
673                         SmsShutdownCancelled (pClientRec->smConn);
674 #ifdef DEBUG
675                         (void) printf ("Sent ShutdownCancelled to %d\n",
676                                         pClientRec->smConn);
677 #endif /* DEBUG */
678                 }
679
680                 pch = strdup ((char *) GETMESSAGE (40, 22, "A session shutdown was cancelled by the application '%s'."));
681                 if (pch) {
682                         DtMsgLogMessage (smGD.programName, 
683                                          DtMsgLogInformation,
684                                          pch, 
685                                          GetArrayPropertyValue (client, 
686                                                 SmProgram));
687                         free (pch);
688                 }
689
690                 return;
691         }
692
693         ProcessInteract (client, True);
694 }
695
696
697 /*ARGSUSED*/
698 void 
699 SaveYourselfReqProc (
700         SmsConn                 smsConn,
701         SmPointer               managerData,
702         int                     saveType,
703         Bool                    shutdown,
704         int                     interactStyle,
705         Bool                    fast,
706         Bool                    global)
707 {
708         ClientRecPtr            tmpClient;
709         ClientRecPtr            pClientRec;
710         Boolean                 notify = True;
711
712 #ifdef DEBUG
713         (void) printf ("Received SAVE YOURSELF REQUEST [%d].\n", smsConn);
714         (void) printf ("\tglobal   = %s\n", global ? "True" : "False");
715         (void) printf ("\tshutdown = %s\n", shutdown ? "True" : "False");
716         (void) printf ("\tfast     = %s\n", fast ? "True" : "False");
717         (void) printf ("\tsaveType = ");
718         switch (saveType) {
719                 case SmSaveLocal:  printf ("SmSaveLocal\n"); break;
720                 case SmSaveGlobal: printf ("SmSaveGlobal\n"); break;
721                 case SmSaveBoth:   printf ("SmSaveBoth\n"); break;
722                 default:           printf ("save type NOT supported\n");
723         }
724         (void) printf ("\tinteractStyle = ");
725         switch (saveType) {
726                 case SmInteractStyleNone:  printf ("SmInteractStyleNone\n"); 
727                         break;
728                 case SmInteractStyleErrors: printf ("SmInteractStyleErrors\n"); 
729                         break;
730                 case SmInteractStyleAny:   printf ("SmInteractStyleAny\n"); 
731                         break;
732                 default:           printf ("interact style NOT supported\n");
733         }
734 #endif /* DEBUG */
735
736         if (smXSMP.saveState.inProgress) {
737                 char            *pch;
738
739                 pch = strdup ((char *) GETMESSAGE (40, 21, "The session will not be saved because a Save Session is in progress."));
740                 if (pch) {
741                         DtMsgLogMessage (smGD.programName, DtMsgLogError, pch);
742                         free (pch);
743                 }
744                 return;
745         }
746
747         if (!global) {
748                 /*
749                  * The client wants to be told to save itself but
750                  * no other clients should be notified.  [smsConn
751                  * will be NULL if this non-global save came from
752                  * a non-XSMP client (e.g via a ToolTalk message).
753                  */
754                 if (smsConn) {
755                         SmsSaveYourself (smsConn, saveType, shutdown,
756                                         interactStyle, fast);
757 #ifdef DEBUG
758                         (void) printf ("\tSent SaveYourself to %d\n", smsConn);
759 #endif /* DEBUG */
760                 }
761                 return;
762         }
763
764         smXSMP.saveState.inProgress = True;
765         smXSMP.saveState.shutdown = shutdown;
766         smXSMP.saveState.shutdownCanceled = False;
767         smXSMP.saveState.interactStyle = interactStyle;
768         smXSMP.saveState.global = global;
769
770         /*
771          * Before notifying the clients, setup a directory
772          * for them to save their state.
773          */
774         if (smGD.homeSave || (smGD.sessionType == HOME_SESSION && 
775                          smSettings.startState == DtSM_HOME_STATE)) {
776                 if (smXSMP.saveState.shutdown)
777                         /*
778                          * Leave the old session dir in place.  It
779                          * will only be used by XSMP apps.
780                          */
781                         notify = False;
782                 else
783                         SetupSaveState (True, DtSM_HOME_STATE);
784
785         } else {
786                 if (smGD.sessionType == CURRENT_SESSION ||
787                         smGD.sessionType == DEFAULT_SESSION)
788                         SetupSaveState (False, DtSM_CURRENT_STATE);
789                 else if (smGD.sessionType == HOME_SESSION && 
790                          smSettings.startState == DtSM_CURRENT_STATE)
791                         SetupSaveState (False, DtSM_HOME_STATE);
792                 else
793                         SetupSaveState (False, DtSM_HOME_STATE);
794         }
795
796         /*
797          * Before the XSMP clients are saved, the ICCC apps must be 
798          * sent a WM_SAVE_YOURSELF message.  This needs to be done because some
799          * apps do not update their geometry information until they
800          * get this message.  If an ICCC app doesn't update their geometry,
801          * then an XSMP-based Window Manager will not have the appropriate
802          * geometry information and the app will not be restore in the
803          * appropriate location.
804         */
805         if (notify) {
806                 ShowWaitState(True);
807                 NotifyProxyClients ();
808                 ShowWaitState(False);
809         }
810
811         for (pClientRec = connectedList; pClientRec != NULL; 
812                 pClientRec = pClientRec->next) {
813
814                 if (pClientRec->active) {
815                         SmsSaveYourself (pClientRec->smConn, saveType, 
816                                         shutdown, interactStyle, fast);
817 #ifdef DEBUG
818                         (void) printf ("\tSent saveyourself to %d\n",
819                                 pClientRec->smConn);
820 #endif /* DEBUG */
821                 }
822         }
823
824         /*
825          * If all of the clients are P2 clients, then process
826          * the save now because these clients won't send a 
827          * SaveYourselfDone msg until after they have processed
828          * a P2 message.
829          */
830         tmpClient = NULL;
831
832         for (pClientRec = connectedList; pClientRec != NULL; 
833                 pClientRec = pClientRec->next) {
834
835                 if (pClientRec->active) {
836                         if (pClientRec->saveYourselfP2Requested)
837                                 tmpClient = pClientRec;
838                         else
839                                 return;
840                 }
841         }
842
843         if (tmpClient) {
844                 SmsSaveYourselfPhase2 (tmpClient->smConn);
845 #ifdef DEBUG
846                 (void) printf ("\tSent SaveYourselfPhase2 to %d\n", 
847                                 pClientRec->smConn);
848 #endif /* DEBUG */
849         }
850 }
851
852
853 /*ARGSUSED*/
854 void 
855 SaveYourselfPhase2ReqProc (
856         SmsConn                 smsConn,
857         SmPointer               managerData)
858 {
859         ClientRecPtr   client = (ClientRecPtr) managerData;
860
861 #ifdef DEBUG
862         (void) printf ("Received SAVE YOURSELF PHASE 2 REQUEST [%d]\n", 
863                         smsConn);
864 #endif /* DEBUG */
865
866         client->saveYourselfP2Requested = True;
867
868         /*
869          * A client may have sent this message in response to
870          * the SM's start-up SaveYourself message.  So if
871          * a session isn't currently being saved, return.
872          */
873         if (!smXSMP.saveState.inProgress) {
874                 /*
875                  * The client is responding to the start-up SaveYourself
876                  * message - this isn't a user-initiated save.
877                  */
878                 
879                 SmsSaveComplete (smsConn);
880                 return;
881         }
882
883         ProcessSaveYourselfResponses ();
884 }
885
886
887 /*ARGSUSED*/
888 void 
889 SaveYourselfDoneProc (
890         SmsConn                 smsConn,
891         SmPointer               managerData,
892         Bool                    success)
893 {
894         ClientRecPtr            pClientRec = (ClientRec *) managerData;
895
896 #ifdef DEBUG
897         (void) printf ("Received SAVE YOURSELF DONE [%d] - Success = %s\n",
898                         smsConn, success ? "True" : "False");
899 #endif /* DEBUG */
900
901         if (!smXSMP.saveState.inProgress) {
902                 /*
903                  * The client is responding to the start-up SaveYourself
904                  * message - this isn't a user-initiated save.
905                  */
906                 SmsSaveComplete (smsConn);
907                 return;
908         }
909
910         /*
911          * Cache success if it is a failure - it will be needed later
912          */
913         if (success == False)
914                 smXSMP.saveState.doneSuccess = False;
915         
916         pClientRec->saveYourselfDone = True;
917
918         ProcessSaveYourselfResponses ();
919 }
920
921
922 static void
923 ProcessSaveYourselfResponses (void)
924 {
925         ClientRecPtr            pClientRec;
926         Boolean                 done = False;
927
928         /*
929          * If all clients are marked as saveYourselfDone, complete 
930          * the save.
931          */
932         for (done = True, pClientRec = connectedList; 
933                 pClientRec != NULL; pClientRec = pClientRec->next) {
934
935                 if (pClientRec->active && 
936                     pClientRec->saveYourselfDone == False) {
937                         done = False;
938                         break;
939                 }
940         }
941
942         if (done) {
943                 if (smXSMP.saveState.shutdownCanceled)
944                         CancelShutdown ();
945                 else
946                         CompleteXSMPSave ();
947                 return;
948         }
949         
950         /*
951          * If any client is marked as not having sent a 
952          * SaveYourselfDone message and it is has not been
953          * marked as having requested a SaveYourselfP2,
954          * then return.
955          */
956         for (pClientRec = connectedList; pClientRec != NULL; 
957                 pClientRec = pClientRec->next) {
958
959                 if (pClientRec->active && 
960                     pClientRec->saveYourselfDone == False &&
961                     pClientRec->saveYourselfP2Requested == False)
962                         return;
963         }
964
965         /*
966          * Only clients which requested a SaveYourselfP2 have not
967          * responded with a SaveYourselfDone message.
968          *
969          * Tell the P2 clients to save themselves.
970          */
971         for (pClientRec = connectedList; pClientRec != NULL; 
972                 pClientRec = pClientRec->next) {
973
974                 if (pClientRec->active) {
975                         if (pClientRec->saveYourselfP2Requested &&
976                            !pClientRec->saveYourselfDone) {
977 #ifdef DEBUG
978                                 (void) printf ("\tSent SaveYourselfPhase2 to %d\n", 
979                                                 pClientRec->smConn);
980 #endif /* DEBUG */
981                                 SmsSaveYourselfPhase2 (pClientRec->smConn);
982                         }
983                 }
984         }
985 }
986
987
988 /*ARGSUSED*/
989 void 
990 CloseConnectionProc (
991         SmsConn                 smsConn,
992         SmPointer               managerData,
993         int                     count,
994         char                    **reasonMsgs)
995 {
996         ClientRecPtr            pClientRec = (ClientRec *) managerData;
997         ClientRecPtr            tmp = pClientRec;
998
999 #ifdef DEBUG
1000         int                     i;
1001
1002         (void) printf ("Received CONNECTION CLOSED [%d]\n", smsConn);
1003
1004         for (i = 0; i < count; i++)
1005                 (void) printf ("\tReason [%2d]: %s\n", i+1, reasonMsgs[i]);
1006 #endif /* DEBUG */
1007
1008         if (count > 0) {
1009                 PostReasonsDialog (GetArrayPropertyValue (tmp, SmProgram), count, reasonMsgs, True);
1010                 SmFreeReasons (count, reasonMsgs);
1011         }
1012
1013         CloseDownClient (pClientRec);
1014 }
1015
1016 void
1017 CompleteXSMPSave (void)
1018 {
1019         ClientRecPtr            pClientRec;
1020
1021         /*
1022          * Save the XSMP clients' state and the Proxy clients' state 
1023          * and save the rest of the settings, resources, etc.
1024          */
1025         CompleteSave ();
1026
1027         /*
1028          * If this isn't a shutdown, tell the clients that the save
1029          * is complete.
1030          */
1031         if (!smXSMP.saveState.shutdown) {
1032                 for (pClientRec = connectedList; pClientRec != NULL; 
1033                         pClientRec = pClientRec->next) {
1034
1035                         if (pClientRec->active) {
1036                                 SmsSaveComplete (pClientRec->smConn);
1037 #ifdef DEBUG
1038                                 printf ("SENT SmsSaveComplete to: %d\n",
1039                                         pClientRec->smConn);
1040 #endif
1041                         }
1042                 }
1043         }
1044
1045         if (!smXSMP.saveState.shutdown) {
1046                 for (pClientRec = connectedList; pClientRec != NULL; 
1047                      pClientRec = pClientRec->next) {
1048
1049                         if (pClientRec->active) {
1050                                 pClientRec->saveYourselfDone = False;
1051                                 pClientRec->saveYourselfP2Requested = False;
1052                                 pClientRec->interactRequested = False;
1053                         }
1054                 }
1055
1056                 InitializeSaveState (False);
1057
1058         } else {
1059
1060                 int             restartHint;
1061
1062                 for (pClientRec = connectedList; pClientRec != NULL; 
1063                         pClientRec = pClientRec->next) {
1064
1065                         if (pClientRec->active) {
1066                                 SmsDie (pClientRec->smConn);
1067 #ifdef DEBUG
1068                                 printf ("SENT SmsDie to: %d\n", 
1069                                         pClientRec->smConn);
1070 #endif
1071                         }
1072
1073                         if (!GetCardPropertyValue (pClientRec, 
1074                                                    SmRestartStyleHint, 
1075                                                    &restartHint))
1076                                 restartHint = pClientRec->restartHint;
1077
1078                         if (restartHint == SmRestartAnyway)
1079                                 ExecuteCommandProperty (SmShutdownCommand, 
1080                                                         pClientRec);
1081                 }
1082                 /*
1083                  * Cannot exit until all of the clients go away.
1084                  */
1085                 smXSMP.saveState.saveComplete = True;
1086                 return;
1087         }
1088 }
1089
1090 static
1091 void FreeProps (
1092         PropertyRecPtr          pProp)
1093 {
1094         PropertyRecPtr          tmp;
1095         PropertyRecPtr          trail;
1096         int                     i;
1097
1098         for (tmp = pProp; tmp != NULL; ) {
1099
1100                 for (i = 0; i < tmp->prop.num_vals; i++)
1101                         XtFree (tmp->prop.vals[i].value);
1102                 if (tmp->prop.num_vals > 0)
1103                         XtFree ((char *) tmp->prop.vals);
1104                 trail = tmp;
1105                 tmp = tmp->next;
1106                 XtFree ((char *) trail);
1107         }
1108 }
1109
1110
1111 void CloseDownClient (
1112         ClientRecPtr            pClientRec )
1113 {
1114         int                     restartHint;
1115         ClientRecPtr    tmp, trail;
1116         SmsConn oldConn;
1117
1118         SmsCleanUp (pClientRec->smConn);
1119         IceSetShutdownNegotiation (pClientRec->iceConn, False);
1120         IceCloseConnection (pClientRec->iceConn);
1121
1122         /* save connection information for later compare */
1123         oldConn = pClientRec->smConn;
1124
1125         pClientRec->iceConn = NULL;
1126         pClientRec->smConn = NULL;
1127         pClientRec->active = False;
1128
1129         if (!GetCardPropertyValue (pClientRec, SmRestartStyleHint, 
1130                                    &restartHint))
1131                 restartHint = pClientRec->restartHint;
1132
1133         if (!smXSMP.saveState.inProgress && restartHint == SmRestartImmediately)
1134                 ExecuteCommandProperty (SmRestartCommand, pClientRec);
1135
1136         if (restartHint == SmRestartAnyway)
1137                 ExecuteCommandProperty (SmResignCommand, pClientRec);
1138
1139         if (restartHint == SmRestartNever) {
1140                 /*
1141                  * Remove the client from the list
1142                  */
1143                 for (tmp = trail = connectedList; tmp != NULL; 
1144                         trail = tmp, tmp = tmp->next) {
1145                         if (tmp->smConn == oldConn) {
1146                                 FreeProps (pClientRec->props);
1147                                 if (tmp == connectedList)
1148                                         connectedList = tmp->next;
1149                                 trail->next = tmp->next;
1150                                 XtFree ((char *) tmp);
1151                                 break;
1152                         }
1153                 }
1154         }
1155
1156         /*
1157          * If a shutdown is occurring and all of the clients
1158          * are inactive, exit.
1159          */
1160         if (smXSMP.saveState.shutdown) { 
1161                 /* 
1162                  * Return if any clients are still active 
1163                  */ 
1164                 for (tmp = connectedList; tmp != NULL; tmp = tmp->next) { 
1165                         if (tmp->active) { 
1166                                 return; 
1167                         }
1168                 }
1169
1170                 /*
1171                  * All clients are inactive - its time to exit 
1172                  */
1173                 _DtReleaseLock (smGD.display, SM_RUNNING_LOCK);
1174                 SM_EXIT (0);
1175         } 
1176 }
1177
1178
1179 /*ARGSUSED*/
1180 static 
1181 Tt_callback_action GetWsmClientsHandler(
1182         Tt_message              message,
1183         Tt_pattern              pattern)
1184 {
1185         int                     lenClientIds;
1186         int                     num_args =  tt_message_args_count (message);
1187 #ifdef DEBUG
1188         char                    * pchar;        
1189         int                     i;
1190 #endif
1191
1192         if (num_args != 3) {
1193                 smXSMP.saveState.numClientIds = 0;
1194                 smXSMP.saveState.clientIds = NULL;
1195                 smXSMP.saveState.workspaceNums = NULL;
1196                 ProcessInteract (smXSMP.saveState.interactClient, False);
1197                 return (TT_CALLBACK_PROCESSED);
1198         }
1199
1200         /*
1201          * Before extracting the new values from this message, free any
1202          * old values.
1203          */
1204         if (smXSMP.saveState.clientIds) 
1205                 free (smXSMP.saveState.clientIds); 
1206         if (smXSMP.saveState.workspaceNums) 
1207                 free (smXSMP.saveState.workspaceNums);
1208
1209         tt_message_arg_ival (message, 0,
1210                         &smXSMP.saveState.numClientIds);
1211         tt_message_arg_bval (message, 1, 
1212                         (unsigned char **) &smXSMP.saveState.clientIds, 
1213                         &lenClientIds);
1214         tt_message_arg_bval (message, 2, 
1215                         (unsigned char **) &smXSMP.saveState.workspaceNums, 
1216                         &lenClientIds);
1217         tt_message_reply (message);
1218         tt_message_destroy (message);
1219
1220 #ifdef DEBUG
1221         (void) printf("GetWsmClientsHandler: num clients = %d\n", 
1222                         smXSMP.saveState.numClientIds);
1223         for (i = 0, pchar = smXSMP.saveState.clientIds; 
1224              i < smXSMP.saveState.numClientIds; 
1225              i++, pchar += strlen (pchar) + 1) {
1226                 (void) printf("\tclient [%2d]: workspace = %2d, id = %s\n", 
1227                                 i+1, smXSMP.saveState.workspaceNums[i], pchar);
1228         }
1229 #endif
1230
1231         ProcessInteract (smXSMP.saveState.interactClient, True);
1232
1233         return (TT_CALLBACK_PROCESSED);
1234 }
1235
1236
1237 static
1238 Boolean SendGetWsmClientsMessage (void)
1239 {
1240         Tt_message              message;
1241         Tt_status               status;
1242
1243         message = tt_message_create ();
1244         status = tt_ptr_error (message);
1245         if (status != TT_OK)
1246                 return (False);
1247
1248         tt_message_class_set (message, TT_REQUEST);
1249         tt_message_scope_set (message, TT_SESSION);
1250         tt_message_address_set (message, TT_PROCEDURE);
1251
1252         tt_message_session_set (message, tt_default_session());
1253         tt_message_op_set (message, GET_CLIENT_WORKSPACE_MSG);
1254         tt_message_callback_add (message, GetWsmClientsHandler);
1255
1256         /*
1257          * Three arguments are expected in the reply so apparently 
1258          * they must be accounted for now.
1259          */
1260         tt_message_iarg_add (message, TT_OUT, "integer", 0);
1261         tt_message_barg_add (message, TT_OUT, "stringlist", 0, 0);
1262         tt_message_barg_add (message, TT_OUT, "intlist", 0, 0);
1263
1264         status = tt_message_send (message);
1265         if (status != TT_OK)
1266                 return (False);
1267
1268         return (True);
1269 }
1270
1271
1272 static 
1273 int GetCurrentWorkspaceNumber (void)
1274 {
1275         Atom                    currentWorkspace;
1276         Atom                    *workspaceList;
1277         Window                  root;
1278         int                     rval;
1279         int                     numWorkspaces;
1280         int                     i;
1281
1282         root = XDefaultRootWindow (smGD.display);
1283
1284         rval = DtWsmGetCurrentWorkspace(smGD.display, root, &currentWorkspace);
1285         if (rval != Success)
1286                 return (-1);
1287
1288         rval = DtWsmGetWorkspaceList (smGD.display, root, &workspaceList, 
1289                         (int *) &numWorkspaces);
1290         if (rval != Success)
1291                 return (-1);
1292
1293         for (i = 0; i < numWorkspaces; i++) {
1294                 if (currentWorkspace == workspaceList[i])
1295                         return (currentWorkspace);
1296         }
1297
1298         return (-1);
1299 }
1300
1301
1302 static 
1303 void CancelShutdown (void)
1304 {
1305         ClientRecPtr            pClientRec;
1306         char                    *pch;
1307
1308         for (pClientRec = connectedList; pClientRec != NULL; 
1309                 pClientRec = pClientRec->next) {
1310
1311                 if (pClientRec->active) {
1312
1313                         ExecuteCommandProperty (SmDiscardCommand, pClientRec);
1314
1315                         pClientRec->saveYourselfDone = False;
1316                         pClientRec->saveYourselfP2Requested = False;
1317                         pClientRec->interactRequested = False;
1318                 }
1319         }
1320
1321         UndoSetSavePath ();
1322
1323         InitializeSaveState (False);
1324 }
1325
1326
1327 /*
1328  * This function can be invoked via different paths:
1329  *
1330  *  1. From the InteractRequest callback
1331  *  2. From the InteractDone callback
1332  *  3. From the GetWsmClients message callback
1333  */
1334 static
1335 void ProcessInteract (
1336         ClientRecPtr            client,
1337         Boolean                 getWsmClientOK)
1338 {
1339         int                     currentWorkspace;
1340         int                     i;
1341         ClientRecPtr            pClientRec;
1342         ClientRecPtr            tmp = NULL;
1343         char                    *pchar;
1344
1345         if (!getWsmClientOK) {
1346                 smXSMP.saveState.clientInteracting = True;
1347                 SmsInteract (client->smConn);
1348                 return;
1349         }
1350
1351         /*
1352          * If a client wants to interact and its workspace matches
1353          * the current workspace, then let it interact.
1354          */
1355         currentWorkspace = GetCurrentWorkspaceNumber ();      
1356
1357         for (pClientRec = connectedList; 
1358              pClientRec != NULL; 
1359              pClientRec = pClientRec->next) {
1360
1361                 if (pClientRec->interactRequested) {
1362                         tmp = pClientRec;
1363
1364                         for (i = 0, pchar = smXSMP.saveState.clientIds; 
1365                              i < smXSMP.saveState.numClientIds; 
1366                              i++, pchar += strlen (pchar) + 1) {
1367
1368                                 if ((currentWorkspace == 
1369                                         smXSMP.saveState.workspaceNums[i]) &&
1370                                     (!strcmp (pchar, pClientRec->clientId))) {
1371
1372                                         smXSMP.saveState.clientInteracting = 
1373                                                 True;
1374                                         SmsInteract (pClientRec->smConn);
1375                                         return;
1376                                 }
1377                         }
1378                 }
1379         }
1380
1381         /*
1382          * Tmp didn't meet all of the requirements but it does
1383          * want to interact so let it.
1384          */
1385         if (tmp) {
1386                 smXSMP.saveState.clientInteracting = True;
1387                 SmsInteract (tmp->smConn);
1388         }
1389 }
1390
1391
1392 /*
1393  * List manipulation functions
1394  */
1395 void AddClient (
1396         ClientRecPtr            newClient)
1397 {
1398         ClientRecPtr            pClient;
1399
1400         if (!connectedList) {
1401                 connectedList = newClient;
1402                 return;
1403         }
1404
1405         /*
1406          * Find the end of the list
1407          */
1408         for (pClient = connectedList; pClient->next != NULL; 
1409                 pClient = pClient->next);
1410
1411         pClient->next = newClient;
1412 }
1413
1414
1415 /*
1416  * The real way to handle IO errors is to check the return status
1417  * of IceProcessMessages.  dtsession properly does this.
1418  *
1419  * Unfortunately, a design flaw exists in the ICE library in which
1420  * a default IO error handler is invoked if no IO error handler is
1421  * installed.  This default handler exits.  We must avoid this.
1422  *
1423  * To get around this problem, we install an IO error handler that
1424  * does a little magic.  Since a previous IO handler might have been
1425  * installed, when we install our IO error handler, we do a little
1426  * trick to get both the previous IO error handler and the default
1427  * IO error handler.  When our IO error handler is called, if the
1428  * previous handler is not the default handler, we call it.  This
1429  * way, everyone's IO error handler gets called except the stupid
1430  * default one which does an exit!
1431  */
1432
1433 static IceIOErrorHandler prev_handler;
1434
1435 void
1436 MyIoErrorHandler (
1437         IceConn                 ice_conn)
1438 {
1439         if (prev_handler)
1440                 (*prev_handler) (ice_conn);
1441 }    
1442
1443 void
1444 InstallIOErrorHandler (void)
1445 {
1446         IceIOErrorHandler default_handler;
1447
1448         prev_handler = IceSetIOErrorHandler (NULL);
1449         default_handler = IceSetIOErrorHandler (MyIoErrorHandler);
1450         if (prev_handler == default_handler)
1451                 prev_handler = NULL;
1452 }
1453
1454
1455 void XSMPExit (void)
1456 {
1457         char            * pchar;
1458
1459         if (authenticationInitialized)
1460                 FreeAuthenticationData (smXSMP.numTransports, 
1461                                         smXSMP.authDataEntries);
1462
1463         /*
1464          * If the local socket file exists, remove it.
1465          *
1466          * Assume the format of networkIds is:
1467          *
1468          *    local/<host_name>:/<socket_file_name>,<other_stuff>
1469          */
1470         if (!strncmp (networkIds, "local/", 6)) {
1471                 if (pchar = strchr (networkIds, ':')) {
1472                         pchar++;
1473                         if (pchar && *pchar != '\000') {
1474                                 char            * pchar2;
1475
1476                                 if (pchar2 = strchr (pchar, ',')) {
1477                                         struct          stat buf;
1478                                         /*
1479                                          * This modifies networkIds but
1480                                          * that's OK because an exit is
1481                                          * about to happen.
1482                                          */
1483                                         *pchar2 = '\000';
1484                                         if ((stat (pchar, &buf)) == 0) {
1485                                                 (void) unlink (pchar);
1486                                         }
1487                                 }
1488                         }
1489                 }
1490         }
1491 }