1 /* $Xorg: xsm.c,v 1.7 2001/02/09 02:06:01 xorgcvs Exp $ */
2 /******************************************************************************
3
4 Copyright 1993, 1998 The Open Group
5
6 Permission to use, copy, modify, distribute, and sell this software and its
7 documentation for any purpose is hereby granted without fee, provided that
8 the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation.
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 Except as contained in this notice, the name of The Open Group shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from The Open Group.
25 ******************************************************************************/
26 /* $XFree86: xc/programs/xsm/xsm.c,v 1.9 2001/12/14 20:02:27 dawes Exp $ */
27
28 /*
29 * X Session Manager.
30 *
31 * Authors:
32 * Ralph Mor, X Consortium
33 * Jordan Brown, Quarterdeck Office Systems
34 */
35
36 #include "xsm.h"
37 #include "xtwatch.h"
38 #include "prop.h"
39 #include "choose.h"
40 #include "mainwin.h"
41 #include "info.h"
42 #include "log.h"
43 #include "save.h"
44 #include "auth.h"
45 #include "restart.h"
46 #include "saveutil.h"
47 #include "lock.h"
48
49 #include <X11/Shell.h>
50 #include <X11/Xatom.h>
51 #include <X11/Xaw/List.h>
52
53 int Argc;
54 char **Argv;
55
56 List *RunningList;
57 List *PendingList;
58 List *RestartAnywayList;
59 List *RestartImmedList;
60
61 List *WaitForSaveDoneList;
62 static List *InitialSaveList;
63 List *FailedSaveList;
64 List *WaitForInteractList;
65 List *WaitForPhase2List;
66
67 Bool wantShutdown = False;
68 Bool shutdownInProgress = False;
69 Bool phase2InProgress = False;
70 Bool saveInProgress = False;
71 Bool shutdownCancelled = False;
72
73 Bool verbose = False;
74
75 char *sm_id = NULL;
76
77 char *networkIds = NULL;
78 char *session_name = NULL;
79
80 IceAuthDataEntry *authDataEntries = NULL;
81 int numTransports = 0;
82
83 Bool client_info_visible = False;
84 Bool client_prop_visible = False;
85 Bool client_log_visible = False;
86
87 String *clientListNames = NULL;
88 ClientRec **clientListRecs = NULL;
89 int numClientListNames = 0;
90
91 int current_client_selected;
92
93 int sessionNameCount = 0;
94 String *sessionNamesShort = NULL;
95 String *sessionNamesLong = NULL;
96 Bool *sessionsLocked = NULL;
97
98 int num_clients_in_last_session = -1;
99
100 char **non_session_aware_clients = NULL;
101 int non_session_aware_count = 0;
102
103 char *display_env = NULL, *non_local_display_env = NULL;
104 char *session_env = NULL, *non_local_session_env = NULL;
105 char *audio_env = NULL;
106
107 Bool need_to_name_session = False;
108
109 Bool remote_allowed;
110
111 XtAppContext appContext;
112 Widget topLevel;
113
114 XtSignalId sig_term_id, sig_usr1_id;
115
116 static Atom wmStateAtom;
117 static Atom wmDeleteAtom;
118 static char *cmd_line_display = NULL;
119
120 /*
121 * Forward declarations
122 */
123 static void PropertyChangeXtHandler(Widget w, XtPointer closure,
124 XEvent *event,
125 Boolean *continue_to_dispatch);
126 static void GetEnvironment(void);
127 static Status RegisterClientProc(SmsConn smsConn, SmPointer managerData,
128 char *previousId);
129 static Bool OkToEnterInteractPhase(void);
130 static void InteractRequestProc(SmsConn smsConn, SmPointer managerData,
131 int dialogType);
132 static void InteractDoneProc(SmsConn smsConn, SmPointer managerData,
133 Bool cancelShutdown);
134 static void SaveYourselfReqProc(SmsConn smsConn, SmPointer managerData,
135 int saveType, Bool shutdown,
136 int interactStyle, Bool fast, Bool global);
137 static Bool OkToEnterPhase2(void);
138 static void SaveYourselfPhase2ReqProc(SmsConn smsConn, SmPointer managerData);
139 static void SaveYourselfDoneProc(SmsConn smsConn, SmPointer managerData,
140 Bool success);
141 static void CloseConnectionProc(SmsConn smsConn, SmPointer managerData,
142 int count, char **reasonMsgs);
143 static Status NewClientProc(SmsConn smsConn, SmPointer managerData,
144 unsigned long *maskRet,
145 SmsCallbacks *callbacksRet,
146 char **failureReasonRet);
147 static void NewConnectionXtProc(XtPointer client_data, int *source,
148 XtInputId *id);
149 static void MyIoErrorHandler(IceConn ice_conn);
150 static void InstallIOErrorHandler(void);
151 static void CloseListeners(void);
152
153
154 static IceListenObj *listenObjs;
155
156
157 /*
158 * Main program
159 */
160 int
main(int argc,char * argv[])161 main(int argc, char *argv[])
162 {
163 char *p;
164 char errormsg[256];
165 static char environment_name[] = "SESSION_MANAGER";
166 int success, found_command_line_name, i;
167
168 Argc = argc;
169 Argv = argv;
170
171 for (i = 1; i < argc; i++)
172 {
173 if (argv[i][0] == '-')
174 {
175 switch (argv[i][1])
176 {
177 case 'd': /* -display */
178 if (++i >= argc) {
179 fprintf (stderr, "%s: -display requires an argument\n",
180 argv[0]);
181 goto usage;
182 }
183 cmd_line_display = (char *) XtNewString (argv[i]);
184 continue;
185
186 case 's': /* -session */
187 if (++i >= argc) {
188 fprintf (stderr, "%s: -session requires an argument\n",
189 argv[0]);
190 goto usage;
191 }
192 session_name = XtNewString (argv[i]);
193 continue;
194
195 case 'v': /* -verbose */
196 verbose = 1;
197 continue;
198 }
199 }
200
201 fprintf (stderr, "%s: unrecognized argument '%s'\n", argv[0], argv[i]);
202
203 usage:
204 fprintf (stderr,
205 "Usage: xsm [-display display] [-session sessionName] [-verbose]\n");
206 exit (1);
207 }
208
209 topLevel = XtVaAppInitialize (&appContext, "XSm", NULL, 0,
210 &argc, argv, NULL,
211 XtNmappedWhenManaged, False,
212 XtNwindowRole, "xsm main window",
213 NULL);
214
215 wmStateAtom = XInternAtom (
216 XtDisplay (topLevel), "WM_STATE", False);
217 wmDeleteAtom = XInternAtom (
218 XtDisplay (topLevel), "WM_DELETE_WINDOW", False);
219
220 register_signals (appContext);
221
222
223 /*
224 * Install an IO error handler. For an explanation,
225 * see the comments for InstallIOErrorHandler().
226 */
227
228 InstallIOErrorHandler ();
229
230
231 /*
232 * Init SM lib
233 */
234
235 if (!SmsInitialize ("SAMPLE-SM", "1.0",
236 NewClientProc, NULL,
237 HostBasedAuthProc, 256, errormsg))
238 {
239 fprintf (stderr, "%s\n", errormsg);
240 exit (1);
241 }
242
243 if (!IceListenForConnections (&numTransports, &listenObjs,
244 256, errormsg))
245 {
246 fprintf (stderr, "%s\n", errormsg);
247 exit (1);
248 }
249
250 atexit(CloseListeners);
251
252 if (!SetAuthentication (numTransports, listenObjs, &authDataEntries))
253 {
254 fprintf (stderr, "Could not set authorization\n");
255 exit (1);
256 }
257
258 InitWatchProcs (appContext);
259
260 for (i = 0; i < numTransports; i++)
261 {
262 XtAppAddInput (appContext,
263 IceGetListenConnectionNumber (listenObjs[i]),
264 (XtPointer) XtInputReadMask,
265 NewConnectionXtProc, (XtPointer) listenObjs[i]);
266 }
267
268 /* the sizeof includes the \0, so we don't need to count the '=' */
269 networkIds = IceComposeNetworkIdList (numTransports, listenObjs);
270 XtAsprintf(&p, "%s=%s", environment_name, networkIds);
271 putenv(p);
272
273 if (cmd_line_display)
274 {
275 /*
276 * If a display was passed on the command line, set the DISPLAY
277 * environment in this process so all applications started by
278 * the session manager will run on the specified display.
279 */
280
281 XtAsprintf(&p, "DISPLAY=%s", cmd_line_display);
282 putenv(p);
283 }
284
285 if (verbose)
286 printf ("setenv %s %s\n", environment_name, networkIds);
287
288 create_choose_session_popup ();
289 create_main_window ();
290 create_client_info_popup ();
291 create_save_popup ();
292 create_log_popup ();
293
294
295 /*
296 * Initalize all lists
297 */
298
299 RunningList = ListInit();
300 if(!RunningList) nomem();
301
302 PendingList = ListInit();
303 if(!PendingList) nomem();
304
305 RestartAnywayList = ListInit();
306 if(!RestartAnywayList) nomem();
307
308 RestartImmedList = ListInit();
309 if(!RestartImmedList) nomem();
310
311 WaitForSaveDoneList = ListInit();
312 if (!WaitForSaveDoneList) nomem();
313
314 InitialSaveList = ListInit();
315 if (!InitialSaveList) nomem();
316
317 FailedSaveList = ListInit();
318 if (!FailedSaveList) nomem();
319
320 WaitForInteractList = ListInit();
321 if (!WaitForInteractList) nomem();
322
323 WaitForPhase2List = ListInit();
324 if (!WaitForPhase2List) nomem();
325
326
327 /*
328 * Get list of session names. If a session name was found on the
329 * command line, and it is in the list of session names we got, then
330 * use that session name. If there were no session names found, then
331 * use the default session name. Otherwise, present a list of session
332 * names for the user to choose from.
333 */
334
335 success = GetSessionNames (&sessionNameCount,
336 &sessionNamesShort, &sessionNamesLong, &sessionsLocked);
337
338 found_command_line_name = 0;
339 if (success && session_name)
340 {
341 for (i = 0; i < sessionNameCount; i++)
342 if (strcmp (session_name, sessionNamesShort[i]) == 0)
343 {
344 found_command_line_name = 1;
345
346 if (sessionsLocked[i])
347 {
348 fprintf (stderr, "Session '%s' is locked\n", session_name);
349 exit (1);
350 }
351
352 break;
353 }
354 }
355
356 if (!success || found_command_line_name)
357 {
358 FreeSessionNames (sessionNameCount,
359 sessionNamesShort, sessionNamesLong, sessionsLocked);
360
361 if (!found_command_line_name)
362 session_name = XtNewString (DEFAULT_SESSION_NAME);
363
364 if (!StartSession (session_name, !found_command_line_name))
365 UnableToLockSession (session_name);
366 }
367 else
368 {
369 ChooseSession ();
370 }
371
372
373 /*
374 * Main loop
375 */
376
377 XtAppMainLoop (appContext);
378 exit(0);
379 }
380
381
382
383 static void
PropertyChangeXtHandler(Widget w,XtPointer closure,XEvent * event,Boolean * continue_to_dispatch)384 PropertyChangeXtHandler(Widget w, XtPointer closure, XEvent *event,
385 Boolean *continue_to_dispatch)
386 {
387 if (w == topLevel && event->type == PropertyNotify &&
388 event->xproperty.atom == wmStateAtom)
389 {
390 XtRemoveEventHandler (topLevel, PropertyChangeMask, False,
391 PropertyChangeXtHandler, NULL);
392
393 /*
394 * Restart the rest of the session aware clients.
395 */
396
397 Restart (RESTART_REST_OF_CLIENTS);
398
399
400 /*
401 * Start apps that aren't session aware that were specified
402 * by the user.
403 */
404
405 StartNonSessionAwareApps ();
406 }
407 }
408
409
410
411 void
SetWM_DELETE_WINDOW(Widget widget,const _XtString delAction)412 SetWM_DELETE_WINDOW(Widget widget, const _XtString delAction)
413 {
414 char translation[64];
415
416 snprintf (translation, sizeof(translation),
417 "<Message>WM_PROTOCOLS: %s", delAction);
418 XtOverrideTranslations (widget, XtParseTranslationTable (translation));
419
420 XSetWMProtocols (XtDisplay(widget), XtWindow (widget),
421 &wmDeleteAtom, 1);
422 }
423
424
425
426 static void
GetEnvironment(void)427 GetEnvironment(void)
428 {
429 static char envDISPLAY[]="DISPLAY";
430 static char envSESSION_MANAGER[]="SESSION_MANAGER";
431 static char envAUDIOSERVER[]="AUDIOSERVER";
432 char *p, *temp;
433
434 remote_allowed = 1;
435
436 display_env = NULL;
437 if((p = cmd_line_display) || (p = (char *) getenv(envDISPLAY))) {
438 XtAsprintf(&display_env, "%s=%s", envDISPLAY, p);
439
440 /*
441 * When we restart a remote client, we have to make sure the
442 * display environment we give it has the SM's hostname.
443 */
444
445 if ((temp = strchr (p, '/')) == NULL)
446 temp = p;
447 else
448 temp++;
449
450 if (*temp != ':')
451 {
452 /* we have a host name */
453
454 non_local_display_env = (char *) XtMalloc (
455 strlen (display_env) + 1);
456 if (!non_local_display_env) nomem();
457
458 strcpy (non_local_display_env, display_env);
459 }
460 else
461 {
462 char hostnamebuf[256];
463
464 gethostname (hostnamebuf, sizeof hostnamebuf);
465 XtAsprintf(&non_local_display_env, "%s=%s%s",
466 envDISPLAY, hostnamebuf, temp);
467 }
468 }
469
470 session_env = NULL;
471 if((p = (char *) getenv(envSESSION_MANAGER))) {
472 XtAsprintf(&session_env, "%s=%s", envSESSION_MANAGER, p);
473
474 /*
475 * When we restart a remote client, we have to make sure the
476 * session environment does not have the SM's local connection port.
477 */
478
479 non_local_session_env = (char *) XtMalloc (strlen (session_env) + 1);
480 if (!non_local_session_env) nomem();
481 strcpy (non_local_session_env, session_env);
482
483 if ((temp = Strstr (non_local_session_env, "local/")) != NULL)
484 {
485 char *delim = strchr (temp, ',');
486 if (delim == NULL)
487 {
488 if (temp == non_local_session_env +
489 strlen (envSESSION_MANAGER) + 1)
490 {
491 *temp = '\0';
492 remote_allowed = 0;
493 }
494 else
495 *(temp - 1) = '\0';
496 }
497 else
498 {
499 int bytes = strlen (delim + 1);
500 memmove (temp, delim + 1, bytes);
501 *(temp + bytes) = '\0';
502 }
503 }
504 }
505
506 audio_env = NULL;
507 if((p = (char *) getenv(envAUDIOSERVER))) {
508 XtAsprintf(&audio_env, "%s=%s", envAUDIOSERVER, p);
509 }
510 }
511
512
513
514 Status
StartSession(char * name,Bool use_default)515 StartSession(char *name, Bool use_default)
516 {
517 int database_read = 0;
518 Dimension width;
519 char title[256];
520
521
522 /*
523 * If we're not using the default session, lock it.
524 * If using the default session, it will be locked as
525 * soon as the user assigns the session a name.
526 */
527
528 if (!use_default && !LockSession (name, True))
529 return (0);
530
531
532 /*
533 * Get important environment variables.
534 */
535
536 GetEnvironment ();
537
538
539 /*
540 * Set the main window's title to the session name.
541 */
542
543 snprintf (title, sizeof(title), "xsm: %s", name);
544
545 XtVaSetValues (topLevel,
546 XtNtitle, title, /* session name */
547 NULL);
548
549 XtRealizeWidget (topLevel);
550
551
552 /*
553 * Set WM_DELETE_WINDOW support on main window. If the user tries
554 * to delete the main window, the shutdown prompt will come up.
555 */
556
557 SetWM_DELETE_WINDOW (topLevel, "DelMainWinAction()");
558
559
560 /*
561 * Read the session save file. Make sure the session manager
562 * has an SM_CLIENT_ID, so that other managers (like the WM) can
563 * identify it.
564 */
565
566 set_session_save_file_name (name);
567
568 if (use_default)
569 need_to_name_session = True;
570 else
571 {
572 database_read = ReadSave (name, &sm_id);
573 need_to_name_session = !database_read;
574 }
575
576 if (!sm_id)
577 {
578 sm_id = SmsGenerateClientID (NULL);
579 if (!sm_id) return (0);
580 }
581 XChangeProperty (XtDisplay (topLevel), XtWindow (topLevel),
582 XInternAtom (XtDisplay (topLevel), "SM_CLIENT_ID", False),
583 XA_STRING, 8, PropModeReplace,
584 (unsigned char *) sm_id, strlen (sm_id));
585
586
587 /*
588 * Adjust some label widths
589 */
590
591 XtVaGetValues (clientInfoButton,
592 XtNwidth, &width,
593 NULL);
594
595 XtVaSetValues (checkPointButton,
596 XtNwidth, width,
597 NULL);
598
599 XtVaGetValues (logButton,
600 XtNwidth, &width,
601 NULL);
602
603 XtVaSetValues (shutdownButton,
604 XtNwidth, width,
605 NULL);
606
607
608 XtMapWidget (topLevel);
609
610
611 if (!database_read)
612 {
613 /*
614 * Start default apps (e.g. twm, smproxy)
615 */
616
617 StartDefaultApps ();
618 }
619 else
620 {
621 /*
622 * Restart window manager first. When the session manager
623 * gets a WM_STATE stored on its top level window, we know
624 * the window manager is running. At that time, we can start
625 * the rest of the applications.
626 */
627
628 XtAddEventHandler (topLevel, PropertyChangeMask, False,
629 PropertyChangeXtHandler, NULL);
630
631 if (!Restart (RESTART_MANAGERS))
632 {
633 XtRemoveEventHandler (topLevel, PropertyChangeMask, False,
634 PropertyChangeXtHandler, NULL);
635
636 /*
637 * Restart the rest of the session aware clients.
638 */
639
640 Restart (RESTART_REST_OF_CLIENTS);
641
642 /*
643 * Start apps that aren't session aware that were specified
644 * by the user.
645 */
646
647 StartNonSessionAwareApps ();
648 }
649 }
650
651 return (1);
652 }
653
654
655
656 void
EndSession(int status)657 EndSession(int status)
658 {
659 if (verbose)
660 printf ("\nSESSION MANAGER GOING AWAY!\n");
661
662 FreeAuthenticationData (numTransports, authDataEntries);
663
664 if (session_name)
665 {
666 UnlockSession (session_name);
667 XtFree (session_name);
668 }
669
670 if (display_env)
671 XtFree (display_env);
672 if (session_env)
673 XtFree (session_env);
674 if (cmd_line_display)
675 XtFree (cmd_line_display);
676 if (non_local_display_env)
677 XtFree (non_local_display_env);
678 if (non_local_session_env)
679 XtFree (non_local_session_env);
680 if (audio_env)
681 XtFree (audio_env);
682 if (networkIds)
683 free (networkIds);
684
685 exit (status);
686 }
687
688
689
690 void
FreeClient(ClientRec * client,Bool freeProps)691 FreeClient(ClientRec *client, Bool freeProps)
692 {
693 if (freeProps)
694 {
695 List *pl;
696
697 for (pl = ListFirst (client->props); pl; pl = ListNext (pl))
698 FreeProp ((Prop *) pl->thing);
699
700 ListFreeAll (client->props);
701 }
702
703 if (client->clientId)
704 free (client->clientId); /* malloc'd by SMlib */
705 if (client->clientHostname)
706 free (client->clientHostname); /* malloc'd by SMlib */
707
708 if (client->discardCommand)
709 XtFree (client->discardCommand);
710 if (client->saveDiscardCommand)
711 XtFree (client->saveDiscardCommand);
712
713 XtFree ((char *) client);
714 }
715
716
717
718 /*
719 * Session Manager callbacks
720 */
721
722 static Status
RegisterClientProc(SmsConn smsConn,SmPointer managerData,char * previousId)723 RegisterClientProc(SmsConn smsConn, SmPointer managerData, char *previousId)
724 {
725 ClientRec *client = (ClientRec *) managerData;
726 char *id;
727 List *cl;
728 int send_save;
729
730 if (verbose)
731 {
732 printf (
733 "On IceConn fd = %d, received REGISTER CLIENT [Previous Id = %s]\n",
734 IceConnectionNumber (client->ice_conn),
735 previousId ? previousId : "NULL");
736 printf ("\n");
737 }
738
739 if (!previousId)
740 {
741 id = SmsGenerateClientID (smsConn);
742 send_save = 1;
743 }
744 else
745 {
746 int found_match = 0;
747 send_save = 1;
748
749 for (cl = ListFirst (PendingList); cl; cl = ListNext (cl))
750 {
751 PendingClient *pendClient = (PendingClient *) cl->thing;
752
753 if (!strcmp (pendClient->clientId, previousId))
754 {
755 SetInitialProperties (client, pendClient->props);
756 XtFree (pendClient->clientId);
757 XtFree (pendClient->clientHostname);
758 XtFree ((char *) pendClient);
759 ListFreeOne (cl);
760 found_match = 1;
761 send_save = 0;
762 break;
763 }
764 }
765
766 if (!found_match)
767 {
768 for (cl = ListFirst (RestartAnywayList); cl; cl = ListNext (cl))
769 {
770 ClientRec *rClient = (ClientRec *) cl->thing;
771
772 if (!strcmp (rClient->clientId, previousId))
773 {
774 SetInitialProperties (client, rClient->props);
775 FreeClient (rClient, False /* don't free props */);
776 ListFreeOne (cl);
777 found_match = 1;
778 send_save = 0;
779 break;
780 }
781 }
782 }
783
784 if (!found_match)
785 {
786 for (cl = ListFirst (RestartImmedList); cl; cl = ListNext (cl))
787 {
788 ClientRec *rClient = (ClientRec *) cl->thing;
789
790 if (!strcmp (rClient->clientId, previousId))
791 {
792 SetInitialProperties (client, rClient->props);
793 FreeClient (rClient, False /* don't free props */);
794 ListFreeOne (cl);
795 found_match = 1;
796 send_save = 0;
797 break;
798 }
799 }
800 }
801
802 if (!found_match)
803 {
804 /*
805 * previous-id was bogus: return bad status and the client
806 * should re-register with a NULL previous-id
807 */
808
809 free (previousId);
810 return (0);
811 }
812 else
813 {
814 id = previousId;
815 }
816 }
817
818 SmsRegisterClientReply (smsConn, id);
819
820 if (verbose)
821 {
822 printf (
823 "On IceConn fd = %d, sent REGISTER CLIENT REPLY [Client Id = %s]\n",
824 IceConnectionNumber (client->ice_conn), id);
825 printf ("\n");
826 }
827
828 client->clientId = id;
829 client->clientHostname = SmsClientHostName (smsConn);
830 client->restarted = (previousId != NULL);
831
832 if (send_save)
833 {
834 SmsSaveYourself (smsConn, SmSaveLocal,
835 False, SmInteractStyleNone, False);
836
837 ListAddLast (InitialSaveList, (char *) client);
838 }
839 else if (client_info_visible)
840 {
841 /* We already have all required client info */
842
843 UpdateClientList ();
844 XawListHighlight (clientListWidget, current_client_selected);
845 }
846
847 return (1);
848 }
849
850
851
852 static Bool
OkToEnterInteractPhase(void)853 OkToEnterInteractPhase(void)
854 {
855 return ((ListCount (WaitForInteractList) +
856 ListCount (WaitForPhase2List)) == ListCount (WaitForSaveDoneList));
857 }
858
859
860
861 static void
InteractRequestProc(SmsConn smsConn,SmPointer managerData,int dialogType)862 InteractRequestProc(SmsConn smsConn, SmPointer managerData, int dialogType)
863 {
864 ClientRec *client = (ClientRec *) managerData;
865
866 if (verbose)
867 {
868 printf ("Client Id = %s, received INTERACT REQUEST [Dialog Type = ",
869 client->clientId);
870 if (dialogType == SmDialogError)
871 printf ("Error]\n");
872 else if (dialogType == SmDialogNormal)
873 printf ("Normal]\n");
874 else
875 printf ("Error in SMlib: should have checked for bad value]\n");
876 }
877
878 ListAddLast (WaitForInteractList, (char *) client);
879
880 if (OkToEnterInteractPhase ())
881 {
882 LetClientInteract (ListFirst (WaitForInteractList));
883 }
884 }
885
886
887
888 static void
InteractDoneProc(SmsConn smsConn,SmPointer managerData,Bool cancelShutdown)889 InteractDoneProc(SmsConn smsConn, SmPointer managerData, Bool cancelShutdown)
890 {
891 ClientRec *client = (ClientRec *) managerData;
892 List *cl;
893
894 if (verbose)
895 {
896 printf (
897 "Client Id = %s, received INTERACT DONE [Cancel Shutdown = %s]\n",
898 client->clientId, cancelShutdown ? "True" : "False");
899 }
900
901 if (cancelShutdown)
902 {
903 ListFreeAllButHead (WaitForInteractList);
904 ListFreeAllButHead (WaitForPhase2List);
905 }
906
907 if (cancelShutdown)
908 {
909 if (shutdownCancelled)
910 {
911 /* Shutdown was already cancelled */
912 return;
913 }
914
915 shutdownCancelled = True;
916
917 for (cl = ListFirst (RunningList); cl; cl = ListNext (cl))
918 {
919 client = (ClientRec *) cl->thing;
920
921 SmsShutdownCancelled (client->smsConn);
922
923 if (verbose)
924 {
925 printf ("Client Id = %s, sent SHUTDOWN CANCELLED\n",
926 client->clientId);
927 }
928 }
929 }
930 else
931 {
932 if ((cl = ListFirst (WaitForInteractList)) != NULL)
933 {
934 LetClientInteract (cl);
935 }
936 else
937 {
938 if (verbose)
939 {
940 printf ("\n");
941 printf ("Done interacting with all clients.\n");
942 printf ("\n");
943 }
944
945 if (ListCount (WaitForPhase2List) > 0)
946 {
947 StartPhase2 ();
948 }
949 }
950 }
951 }
952
953
954
955 static void
SaveYourselfReqProc(SmsConn smsConn,SmPointer managerData,int saveType,Bool shutdown,int interactStyle,Bool fast,Bool global)956 SaveYourselfReqProc(SmsConn smsConn, SmPointer managerData, int saveType,
957 Bool shutdown, int interactStyle, Bool fast, Bool global)
958 {
959 if (verbose)
960 printf("SAVE YOURSELF REQUEST not supported!\n");
961 }
962
963
964
965 static Bool
OkToEnterPhase2(void)966 OkToEnterPhase2(void)
967
968 {
969 return (ListCount (WaitForPhase2List) == ListCount (WaitForSaveDoneList));
970 }
971
972
973
974 static void
SaveYourselfPhase2ReqProc(SmsConn smsConn,SmPointer managerData)975 SaveYourselfPhase2ReqProc(SmsConn smsConn, SmPointer managerData)
976 {
977 ClientRec *client = (ClientRec *) managerData;
978
979 if (verbose)
980 {
981 printf ("Client Id = %s, received SAVE YOURSELF PHASE 2 REQUEST\n",
982 client->clientId);
983 }
984
985 if (!saveInProgress)
986 {
987 /*
988 * If we are not in the middle of a checkpoint (ie. we just
989 * started the client and sent the initial save yourself), just
990 * send the save yourself phase2 now.
991 */
992
993 SmsSaveYourselfPhase2 (client->smsConn);
994 }
995 else
996 {
997 ListAddLast (WaitForPhase2List, (char *) client);
998
999 if (ListCount (WaitForInteractList) > 0 && OkToEnterInteractPhase ())
1000 {
1001 LetClientInteract (ListFirst (WaitForInteractList));
1002 }
1003 else if (OkToEnterPhase2 ())
1004 {
1005 StartPhase2 ();
1006 }
1007 }
1008 }
1009
1010
1011
1012 static void
SaveYourselfDoneProc(SmsConn smsConn,SmPointer managerData,Bool success)1013 SaveYourselfDoneProc(SmsConn smsConn, SmPointer managerData, Bool success)
1014 {
1015 ClientRec *client = (ClientRec *) managerData;
1016
1017 if (verbose)
1018 {
1019 printf("Client Id = %s, received SAVE YOURSELF DONE [Success = %s]\n",
1020 client->clientId, success ? "True" : "False");
1021 }
1022
1023 if (!ListSearchAndFreeOne (WaitForSaveDoneList, (char *) client))
1024 {
1025 if (ListSearchAndFreeOne (InitialSaveList, (char *) client))
1026 SmsSaveComplete (client->smsConn);
1027 return;
1028 }
1029
1030 if (!success)
1031 {
1032 ListAddLast (FailedSaveList, (char *) client);
1033 }
1034
1035 if (ListCount (WaitForSaveDoneList) == 0)
1036 {
1037 if (ListCount (FailedSaveList) > 0 && !checkpoint_from_signal)
1038 PopupBadSave ();
1039 else
1040 FinishUpSave ();
1041 }
1042 else if (ListCount (WaitForInteractList) > 0 && OkToEnterInteractPhase ())
1043 {
1044 LetClientInteract (ListFirst (WaitForInteractList));
1045 }
1046 else if (ListCount (WaitForPhase2List) > 0 && OkToEnterPhase2 ())
1047 {
1048 StartPhase2 ();
1049 }
1050 }
1051
1052
1053
1054 void
CloseDownClient(ClientRec * client)1055 CloseDownClient(ClientRec *client)
1056 {
1057 int index_deleted = 0;
1058
1059 if (verbose) {
1060 printf ("ICE Connection closed, IceConn fd = %d\n",
1061 IceConnectionNumber (client->ice_conn));
1062 printf ("\n");
1063 }
1064
1065 SmsCleanUp (client->smsConn);
1066 IceSetShutdownNegotiation (client->ice_conn, False);
1067 IceCloseConnection (client->ice_conn);
1068
1069 client->ice_conn = NULL;
1070 client->smsConn = NULL;
1071
1072 if (!shutdownInProgress && client_info_visible)
1073 {
1074 for (index_deleted = 0;
1075 index_deleted < numClientListNames; index_deleted++)
1076 {
1077 if (clientListRecs[index_deleted] == client)
1078 break;
1079 }
1080 }
1081
1082 ListSearchAndFreeOne (RunningList, (char *) client);
1083
1084 if (saveInProgress)
1085 {
1086 Status delStatus = ListSearchAndFreeOne (
1087 WaitForSaveDoneList, (char *) client);
1088
1089 if (delStatus)
1090 {
1091 ListAddLast (FailedSaveList, (char *) client);
1092 client->freeAfterBadSavePopup = True;
1093 }
1094
1095 ListSearchAndFreeOne (WaitForInteractList, (char *) client);
1096 ListSearchAndFreeOne (WaitForPhase2List, (char *) client);
1097
1098 if (delStatus && ListCount (WaitForSaveDoneList) == 0)
1099 {
1100 if (ListCount (FailedSaveList) > 0 && !checkpoint_from_signal)
1101 PopupBadSave ();
1102 else
1103 FinishUpSave ();
1104 }
1105 else if (ListCount (WaitForInteractList) > 0 &&
1106 OkToEnterInteractPhase ())
1107 {
1108 LetClientInteract (ListFirst (WaitForInteractList));
1109 }
1110 else if (!phase2InProgress &&
1111 ListCount (WaitForPhase2List) > 0 && OkToEnterPhase2 ())
1112 {
1113 StartPhase2 ();
1114 }
1115 }
1116
1117 if (client->restartHint == SmRestartImmediately && !shutdownInProgress)
1118 {
1119 Clone (client, True /* use saved state */);
1120
1121 ListAddLast (RestartImmedList, (char *) client);
1122 }
1123 else if (client->restartHint == SmRestartAnyway)
1124 {
1125 ListAddLast (RestartAnywayList, (char *) client);
1126 }
1127 else if (!client->freeAfterBadSavePopup)
1128 {
1129 FreeClient (client, True /* free props */);
1130 }
1131
1132 if (shutdownInProgress)
1133 {
1134 if (ListCount (RunningList) == 0)
1135 EndSession (0);
1136 }
1137 else if (client_info_visible)
1138 {
1139 UpdateClientList ();
1140
1141 if (current_client_selected == index_deleted)
1142 {
1143 if (current_client_selected == numClientListNames)
1144 current_client_selected--;
1145
1146 if (current_client_selected >= 0)
1147 {
1148 XawListHighlight (clientListWidget, current_client_selected);
1149 ShowHint (clientListRecs[current_client_selected]);
1150 if (client_prop_visible)
1151 {
1152 DisplayProps (clientListRecs[current_client_selected]);
1153 }
1154 }
1155 }
1156 else
1157 {
1158 if (index_deleted < current_client_selected)
1159 current_client_selected--;
1160 XawListHighlight (clientListWidget, current_client_selected);
1161 }
1162 }
1163 }
1164
1165
1166
1167
1168 static void
CloseConnectionProc(SmsConn smsConn,SmPointer managerData,int count,char ** reasonMsgs)1169 CloseConnectionProc(SmsConn smsConn, SmPointer managerData,
1170 int count, char **reasonMsgs)
1171 {
1172 ClientRec *client = (ClientRec *) managerData;
1173
1174 if (verbose)
1175 {
1176 int i;
1177
1178 printf ("Client Id = %s, received CONNECTION CLOSED\n",
1179 client->clientId);
1180
1181 for (i = 0; i < count; i++)
1182 printf (" Reason string %d: %s\n", i + 1, reasonMsgs[i]);
1183 printf ("\n");
1184 }
1185
1186 SmFreeReasons (count, reasonMsgs);
1187
1188 CloseDownClient (client);
1189 }
1190
1191
1192
1193 static Status
NewClientProc(SmsConn smsConn,SmPointer managerData,unsigned long * maskRet,SmsCallbacks * callbacksRet,char ** failureReasonRet)1194 NewClientProc(SmsConn smsConn, SmPointer managerData, unsigned long *maskRet,
1195 SmsCallbacks *callbacksRet, char **failureReasonRet)
1196 {
1197 ClientRec *newClient = (ClientRec *) XtMalloc (sizeof (ClientRec));
1198
1199 *maskRet = 0;
1200
1201 if (!newClient)
1202 {
1203 const char *str = "Memory allocation failed";
1204
1205 if ((*failureReasonRet = (char *) XtMalloc (strlen (str) + 1)) != NULL)
1206 strcpy (*failureReasonRet, str);
1207
1208 return (0);
1209 }
1210
1211 newClient->smsConn = smsConn;
1212 newClient->ice_conn = SmsGetIceConnection (smsConn);
1213 newClient->clientId = NULL;
1214 newClient->clientHostname = NULL;
1215 newClient->restarted = False; /* wait till RegisterClient for true value */
1216 newClient->userIssuedCheckpoint = False;
1217 newClient->receivedDiscardCommand = False;
1218 newClient->freeAfterBadSavePopup = False;
1219 newClient->props = ListInit ();
1220 newClient->discardCommand = NULL;
1221 newClient->saveDiscardCommand = NULL;
1222 newClient->restartHint = SmRestartIfRunning;
1223
1224 ListAddLast (RunningList, (char *) newClient);
1225
1226 if (verbose) {
1227 printf("On IceConn fd = %d, client set up session mngmt protocol\n\n",
1228 IceConnectionNumber (newClient->ice_conn));
1229 }
1230
1231 /*
1232 * Set up session manager callbacks.
1233 */
1234
1235 *maskRet |= SmsRegisterClientProcMask;
1236 callbacksRet->register_client.callback = RegisterClientProc;
1237 callbacksRet->register_client.manager_data = (SmPointer) newClient;
1238
1239 *maskRet |= SmsInteractRequestProcMask;
1240 callbacksRet->interact_request.callback = InteractRequestProc;
1241 callbacksRet->interact_request.manager_data = (SmPointer) newClient;
1242
1243 *maskRet |= SmsInteractDoneProcMask;
1244 callbacksRet->interact_done.callback = InteractDoneProc;
1245 callbacksRet->interact_done.manager_data = (SmPointer) newClient;
1246
1247 *maskRet |= SmsSaveYourselfRequestProcMask;
1248 callbacksRet->save_yourself_request.callback = SaveYourselfReqProc;
1249 callbacksRet->save_yourself_request.manager_data = (SmPointer) newClient;
1250
1251 *maskRet |= SmsSaveYourselfP2RequestProcMask;
1252 callbacksRet->save_yourself_phase2_request.callback =
1253 SaveYourselfPhase2ReqProc;
1254 callbacksRet->save_yourself_phase2_request.manager_data =
1255 (SmPointer) newClient;
1256
1257 *maskRet |= SmsSaveYourselfDoneProcMask;
1258 callbacksRet->save_yourself_done.callback = SaveYourselfDoneProc;
1259 callbacksRet->save_yourself_done.manager_data = (SmPointer) newClient;
1260
1261 *maskRet |= SmsCloseConnectionProcMask;
1262 callbacksRet->close_connection.callback = CloseConnectionProc;
1263 callbacksRet->close_connection.manager_data = (SmPointer) newClient;
1264
1265 *maskRet |= SmsSetPropertiesProcMask;
1266 callbacksRet->set_properties.callback = SetPropertiesProc;
1267 callbacksRet->set_properties.manager_data = (SmPointer) newClient;
1268
1269 *maskRet |= SmsDeletePropertiesProcMask;
1270 callbacksRet->delete_properties.callback = DeletePropertiesProc;
1271 callbacksRet->delete_properties.manager_data = (SmPointer) newClient;
1272
1273 *maskRet |= SmsGetPropertiesProcMask;
1274 callbacksRet->get_properties.callback = GetPropertiesProc;
1275 callbacksRet->get_properties.manager_data = (SmPointer) newClient;
1276
1277 return (1);
1278 }
1279
1280
1281
1282 /*
1283 * Xt callback invoked when a client attempts to connect.
1284 */
1285
1286 static void
NewConnectionXtProc(XtPointer client_data,int * source,XtInputId * id)1287 NewConnectionXtProc(XtPointer client_data, int *source, XtInputId *id)
1288 {
1289 IceConn ice_conn;
1290 char *connstr;
1291 IceAcceptStatus status;
1292
1293 if (shutdownInProgress)
1294 {
1295 /*
1296 * Don't accept new connections if we are in the middle
1297 * of a shutdown.
1298 */
1299
1300 return;
1301 }
1302
1303 ice_conn = IceAcceptConnection((IceListenObj) client_data, &status);
1304 if (! ice_conn) {
1305 if (verbose)
1306 printf ("IceAcceptConnection failed\n");
1307 } else {
1308 IceConnectStatus cstatus;
1309
1310 while ((cstatus = IceConnectionStatus (ice_conn))==IceConnectPending) {
1311 XtAppProcessEvent (appContext, XtIMAll);
1312 }
1313
1314 if (cstatus == IceConnectAccepted) {
1315 if (verbose) {
1316 printf ("ICE Connection opened by client, IceConn fd = %d, ",
1317 IceConnectionNumber (ice_conn));
1318 connstr = IceConnectionString (ice_conn);
1319 printf ("Accept at networkId %s\n", connstr);
1320 free (connstr);
1321 printf ("\n");
1322 }
1323 } else {
1324 if (verbose)
1325 {
1326 if (cstatus == IceConnectIOError)
1327 printf ("IO error opening ICE Connection!\n");
1328 else
1329 printf ("ICE Connection rejected!\n");
1330 }
1331
1332 IceCloseConnection (ice_conn);
1333 }
1334 }
1335 }
1336
1337
1338
1339 void
SetAllSensitive(Bool on)1340 SetAllSensitive(Bool on)
1341 {
1342 XtSetSensitive (mainWindow, on);
1343 SetSaveSensitivity (on);
1344 XtSetSensitive (clientInfoPopup, on);
1345 XtSetSensitive (clientPropPopup, on);
1346
1347 if (on && current_client_selected >= 0)
1348 XawListHighlight (clientListWidget, current_client_selected);
1349 }
1350
1351
1352
1353 /*
1354 * The real way to handle IO errors is to check the return status
1355 * of IceProcessMessages. xsm properly does this.
1356 *
1357 * Unfortunately, a design flaw exists in the ICE library in which
1358 * a default IO error handler is invoked if no IO error handler is
1359 * installed. This default handler exits. We must avoid this.
1360 *
1361 * To get around this problem, we install an IO error handler that
1362 * does a little magic. Since a previous IO handler might have been
1363 * installed, when we install our IO error handler, we do a little
1364 * trick to get both the previous IO error handler and the default
1365 * IO error handler. When our IO error handler is called, if the
1366 * previous handler is not the default handler, we call it. This
1367 * way, everyone's IO error handler gets called except the stupid
1368 * default one which does an exit!
1369 */
1370
1371 static IceIOErrorHandler prev_handler;
1372
1373 static void
MyIoErrorHandler(IceConn ice_conn)1374 MyIoErrorHandler(IceConn ice_conn)
1375 {
1376 if (prev_handler)
1377 (*prev_handler) (ice_conn);
1378 }
1379
1380 static void
InstallIOErrorHandler(void)1381 InstallIOErrorHandler(void)
1382
1383 {
1384 IceIOErrorHandler default_handler;
1385
1386 prev_handler = IceSetIOErrorHandler (NULL);
1387 default_handler = IceSetIOErrorHandler (MyIoErrorHandler);
1388 if (prev_handler == default_handler)
1389 prev_handler = NULL;
1390 }
1391
1392 static void
CloseListeners(void)1393 CloseListeners(void)
1394
1395 {
1396 IceFreeListenObjs (numTransports, listenObjs);
1397 }
1398
1399