sm_client.c revision 6bea0e4f
1/*
2
3Copyright 1993, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27/*
28 * Author: Ralph Mor, X Consortium
29 */
30
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34#include <X11/SM/SMlib.h>
35#include "SMlibint.h"
36
37int 	_SmcOpcode = 0;
38int 	_SmsOpcode = 0;
39
40SmsNewClientProc _SmsNewClientProc;
41SmPointer        _SmsNewClientData;
42
43SmcErrorHandler _SmcErrorHandler = _SmcDefaultErrorHandler;
44SmsErrorHandler _SmsErrorHandler = _SmsDefaultErrorHandler;
45
46
47static void
48set_callbacks(SmcConn smcConn, unsigned long mask, SmcCallbacks *callbacks);
49
50
51SmcConn
52SmcOpenConnection(char *networkIdsList, SmPointer context,
53		  int xsmpMajorRev, int xsmpMinorRev,
54		  unsigned long mask, SmcCallbacks *callbacks,
55		  const char *previousId, char **clientIdRet,
56		  int errorLength, char *errorStringRet)
57{
58    SmcConn			smcConn;
59    IceConn			iceConn;
60    char 			*ids;
61    IceProtocolSetupStatus	setupstat;
62    int				majorVersion;
63    int				minorVersion;
64    char			*vendor = NULL;
65    char			*release = NULL;
66    smRegisterClientMsg 	*pMsg;
67    char 			*pData;
68    unsigned int		extra, len;
69    IceReplyWaitInfo		replyWait;
70    _SmcRegisterClientReply	reply;
71    Bool			gotReply, ioErrorOccured;
72
73    const char *auth_names[] = {"MIT-MAGIC-COOKIE-1"};
74    IcePoAuthProc auth_procs[] = {_IcePoMagicCookie1Proc};
75    int auth_count = 1;
76
77    IcePoVersionRec versions[] = {
78        {SmProtoMajor, SmProtoMinor, _SmcProcessMessage}
79    };
80    int version_count = 1;
81
82
83    *clientIdRet = NULL;
84
85    if (errorStringRet && errorLength > 0)
86	*errorStringRet = '\0';
87
88    if (!_SmcOpcode)
89    {
90	/*
91	 * For now, there is only one version of XSMP, so we don't
92	 * have to check {xsmpMajorRev, xsmpMinorRev}.  In the future,
93	 * we will check against versions and generate the list
94	 * of versions the application actually supports.
95	 */
96
97	if ((_SmcOpcode = IceRegisterForProtocolSetup ("XSMP",
98	    SmVendorString, SmReleaseString, version_count, versions,
99            auth_count, auth_names, auth_procs, NULL)) < 0)
100	{
101	    if (errorStringRet && errorLength > 0) {
102		strncpy (errorStringRet,
103			 "Could not register XSMP protocol with ICE",
104			 errorLength);
105		errorStringRet[errorLength - 1] = '\0';
106	    }
107
108	    return (NULL);
109	}
110    }
111
112    if (networkIdsList == NULL || *networkIdsList == '\0')
113    {
114	if ((ids = getenv ("SESSION_MANAGER")) == NULL)
115	{
116	    if (errorStringRet && errorLength > 0) {
117		strncpy (errorStringRet,
118			 "SESSION_MANAGER environment variable not defined",
119			 errorLength);
120		errorStringRet[errorLength - 1] = '\0';
121	    }
122	    return (NULL);
123	}
124    }
125    else
126    {
127	ids = networkIdsList;
128    }
129
130    if ((iceConn = IceOpenConnection (
131	ids, context, 0, _SmcOpcode, errorLength, errorStringRet)) == NULL)
132    {
133	return (NULL);
134    }
135
136    if ((smcConn = malloc (sizeof (struct _SmcConn))) == NULL)
137    {
138	if (errorStringRet && errorLength > 0) {
139	    strncpy (errorStringRet, "Can't malloc", errorLength);
140	    errorStringRet[errorLength - 1] = '\0';
141	}
142	IceCloseConnection (iceConn);
143	return (NULL);
144    }
145
146    setupstat = IceProtocolSetup (iceConn, _SmcOpcode,
147	(IcePointer) smcConn,
148	False /* mustAuthenticate */,
149	&majorVersion, &minorVersion,
150	&vendor, &release, errorLength, errorStringRet);
151
152    if (setupstat == IceProtocolSetupFailure ||
153	setupstat == IceProtocolSetupIOError)
154    {
155	IceCloseConnection (iceConn);
156	free (smcConn);
157	return (NULL);
158    }
159    else if (setupstat == IceProtocolAlreadyActive)
160    {
161	/*
162	 * This case should never happen, because when we called
163	 * IceOpenConnection, we required that the ICE connection
164	 * may not already have XSMP active on it.
165	 */
166
167	free (smcConn);
168	if (errorStringRet && errorLength > 0) {
169	    strncpy (errorStringRet, "Internal error in IceOpenConnection",
170		     errorLength);
171	    errorStringRet[errorLength - 1] = '\0';
172	}
173	return (NULL);
174    }
175
176    smcConn->iceConn = iceConn;
177    smcConn->proto_major_version = majorVersion;
178    smcConn->proto_minor_version = minorVersion;
179    smcConn->vendor = vendor;
180    smcConn->release = release;
181    smcConn->client_id = NULL;
182
183    bzero ((char *) &smcConn->callbacks, sizeof (SmcCallbacks));
184    set_callbacks (smcConn, mask, callbacks);
185
186    smcConn->interact_waits = NULL;
187    smcConn->phase2_wait = NULL;
188    smcConn->prop_reply_waits = NULL;
189
190    smcConn->save_yourself_in_progress = False;
191    smcConn->shutdown_in_progress = False;
192
193
194    /*
195     * Now register the client
196     */
197
198    if (!previousId)
199	previousId = "";
200    len = strlen (previousId);
201    extra = ARRAY8_BYTES (len);
202
203    IceGetHeaderExtra (iceConn, _SmcOpcode, SM_RegisterClient,
204	SIZEOF (smRegisterClientMsg), WORD64COUNT (extra),
205	smRegisterClientMsg, pMsg, pData);
206
207    if (pData != NULL) {
208	STORE_ARRAY8 (pData, len, previousId);
209	IceFlush (iceConn);
210    }
211    else {
212	SEND_ARRAY8 (iceConn, len, previousId);
213    }
214
215    replyWait.sequence_of_request = IceLastSentSequenceNumber (iceConn);
216    replyWait.major_opcode_of_request = _SmcOpcode;
217    replyWait.minor_opcode_of_request = SM_RegisterClient;
218    replyWait.reply = (IcePointer) &reply;
219
220    gotReply = False;
221    ioErrorOccured = False;
222
223    while (!gotReply && !ioErrorOccured)
224    {
225	ioErrorOccured = (IceProcessMessages (
226	    iceConn, &replyWait, &gotReply) == IceProcessMessagesIOError);
227
228	if (ioErrorOccured)
229	{
230	    if (errorStringRet && errorLength > 0) {
231		strncpy (errorStringRet, "IO error occurred opening connection",
232			 errorLength);
233		errorStringRet[errorLength - 1] = '\0';
234	    }
235	    free (smcConn->vendor);
236	    free (smcConn->release);
237	    free (smcConn);
238
239	    return (NULL);
240	}
241	else if (gotReply)
242	{
243	    if (reply.status == 1)
244	    {
245		/*
246		 * The client successfully registered.
247		 */
248
249		*clientIdRet = reply.client_id;
250
251		smcConn->client_id = strdup (*clientIdRet);
252	    }
253	    else
254	    {
255		/*
256		 * Could not register the client because the previous ID
257		 * was bad.  So now we register the client with the
258		 * previous ID set to NULL.
259		 */
260
261		extra = ARRAY8_BYTES (0);
262
263		IceGetHeaderExtra (iceConn, _SmcOpcode, SM_RegisterClient,
264		    SIZEOF (smRegisterClientMsg), WORD64COUNT (extra),
265		    smRegisterClientMsg, pMsg, pData);
266
267		if (pData != NULL) {
268		    STORE_ARRAY8 (pData, 0, "");
269		    IceFlush (iceConn);
270		}
271		else {
272		    SEND_ARRAY8 (iceConn, 0, "");
273		}
274
275		replyWait.sequence_of_request =
276		    IceLastSentSequenceNumber (iceConn);
277
278		gotReply = False;
279	    }
280	}
281    }
282
283    return (smcConn);
284}
285
286
287
288SmcCloseStatus
289SmcCloseConnection(SmcConn smcConn, int count, char **reasonMsgs)
290{
291    IceConn			iceConn = smcConn->iceConn;
292    smCloseConnectionMsg 	*pMsg;
293    char 			*pData;
294    int				extra, i;
295    IceCloseStatus	        closeStatus;
296    SmcCloseStatus		statusRet;
297
298    extra = 8;
299
300    for (i = 0; i < count; i++)
301	extra += ARRAY8_BYTES (strlen (reasonMsgs[i]));
302
303    IceGetHeaderExtra (iceConn, _SmcOpcode, SM_CloseConnection,
304	SIZEOF (smCloseConnectionMsg), WORD64COUNT (extra),
305	smCloseConnectionMsg, pMsg, pData);
306
307    if (pData != NULL) {
308	STORE_CARD32 (pData, (CARD32) count);
309	STORE_CARD32 (pData, (CARD32) 0); /* padding */
310
311	for (i = 0; i < count; i++)
312	    STORE_ARRAY8 (pData, strlen (reasonMsgs[i]), reasonMsgs[i]);
313
314	IceFlush (iceConn);
315    } else {
316	CARD32 count_header[2] = {
317	    (CARD32) count,
318	    (CARD32) 0 /* padding */
319	};
320	IceWriteData32 (iceConn, 8, count_header);
321
322	for (i = 0; i < count; i++)
323	    SEND_ARRAY8 (iceConn, strlen (reasonMsgs[i]), reasonMsgs[i]);
324    }
325
326    IceProtocolShutdown (iceConn, _SmcOpcode);
327    IceSetShutdownNegotiation (iceConn, False);
328    closeStatus = IceCloseConnection (iceConn);
329
330    if (smcConn->vendor)
331	free (smcConn->vendor);
332
333    if (smcConn->release)
334	free (smcConn->release);
335
336    if (smcConn->client_id)
337	free (smcConn->client_id);
338
339    if (smcConn->interact_waits)
340    {
341	_SmcInteractWait *ptr = smcConn->interact_waits;
342	_SmcInteractWait *next;
343
344	while (ptr)
345	{
346	    next = ptr->next;
347	    free (ptr);
348	    ptr = next;
349	}
350    }
351
352    if (smcConn->phase2_wait)
353	free (smcConn->phase2_wait);
354
355    if (smcConn->prop_reply_waits)
356    {
357	_SmcPropReplyWait *ptr = smcConn->prop_reply_waits;
358	_SmcPropReplyWait *next;
359
360	while (ptr)
361	{
362	    next = ptr->next;
363	    free (ptr);
364	    ptr = next;
365	}
366
367    }
368
369    free (smcConn);
370
371    if (closeStatus == IceClosedNow)
372	statusRet = SmcClosedNow;
373    else if (closeStatus == IceClosedASAP)
374	statusRet = SmcClosedASAP;
375    else
376	statusRet = SmcConnectionInUse;
377
378    return (statusRet);
379}
380
381
382
383void
384SmcModifyCallbacks(SmcConn smcConn, unsigned long mask, SmcCallbacks *callbacks)
385{
386    set_callbacks (smcConn, mask, callbacks);
387}
388
389
390
391void
392SmcSetProperties(SmcConn smcConn, int numProps, SmProp **props)
393{
394    IceConn		iceConn = smcConn->iceConn;
395    smSetPropertiesMsg	*pMsg;
396    char		*pBuf;
397    char		*pStart;
398    unsigned int	bytes;
399
400    IceGetHeader (iceConn, _SmcOpcode, SM_SetProperties,
401	SIZEOF (smSetPropertiesMsg), smSetPropertiesMsg, pMsg);
402
403    LISTOF_PROP_BYTES (numProps, props, bytes);
404    pMsg->length += WORD64COUNT (bytes);
405
406    pBuf = pStart = IceAllocScratch (iceConn, bytes);
407    memset(pStart, 0, bytes);
408
409    STORE_LISTOF_PROPERTY (pBuf, numProps, props);
410
411    IceWriteData (iceConn, bytes, pStart);
412    IceFlush (iceConn);
413}
414
415
416
417void
418SmcDeleteProperties(SmcConn smcConn, int numProps, char **propNames)
419{
420    IceConn			iceConn = smcConn->iceConn;
421    smDeletePropertiesMsg 	*pMsg;
422    char 			*pData;
423    int				extra, i;
424
425    extra = 8;
426
427    for (i = 0; i < numProps; i++)
428	extra += ARRAY8_BYTES (strlen (propNames[i]));
429
430    IceGetHeaderExtra (iceConn, _SmcOpcode, SM_DeleteProperties,
431	SIZEOF (smDeletePropertiesMsg), WORD64COUNT (extra),
432	smDeletePropertiesMsg, pMsg, pData);
433
434    if (pData != NULL) {
435	STORE_CARD32 (pData, (CARD32) numProps);
436	STORE_CARD32 (pData, (CARD32) 0); /* padding */
437
438	for (i = 0; i < numProps; i++)
439	    STORE_ARRAY8 (pData, strlen (propNames[i]), propNames[i]);
440
441	IceFlush (iceConn);
442    }
443    else {
444	CARD32 count_header[2] = {
445	    (CARD32) numProps,
446	    (CARD32) 0 /* padding */
447	};
448	IceWriteData32 (iceConn, 8, count_header);
449
450	for (i = 0; i < numProps; i++)
451	    SEND_ARRAY8 (iceConn, strlen (propNames[i]), propNames[i]);
452    }
453}
454
455
456
457Status
458SmcGetProperties(SmcConn smcConn, SmcPropReplyProc propReplyProc,
459		 SmPointer clientData)
460{
461    IceConn		iceConn = smcConn->iceConn;
462    _SmcPropReplyWait 	*wait, *ptr;
463
464    if ((wait = malloc (sizeof (_SmcPropReplyWait))) == NULL)
465    {
466	return (0);
467    }
468
469    wait->prop_reply_proc = propReplyProc;
470    wait->client_data = clientData;
471    wait->next = NULL;
472
473    ptr = smcConn->prop_reply_waits;
474    while (ptr && ptr->next)
475	ptr = ptr->next;
476
477    if (ptr == NULL)
478	smcConn->prop_reply_waits = wait;
479    else
480	ptr->next = wait;
481
482    IceSimpleMessage (iceConn, _SmcOpcode, SM_GetProperties);
483    IceFlush (iceConn);
484
485    return (1);
486}
487
488
489
490Status
491SmcInteractRequest(SmcConn smcConn, int dialogType,
492		   SmcInteractProc interactProc, SmPointer clientData)
493{
494    IceConn			iceConn = smcConn->iceConn;
495    smInteractRequestMsg	*pMsg;
496    _SmcInteractWait 		*wait, *ptr;
497
498    if ((wait = malloc (sizeof (_SmcInteractWait))) == NULL)
499    {
500	return (0);
501    }
502
503    wait->interact_proc = interactProc;
504    wait->client_data = clientData;
505    wait->next = NULL;
506
507    ptr = smcConn->interact_waits;
508    while (ptr && ptr->next)
509	ptr = ptr->next;
510
511    if (ptr == NULL)
512	smcConn->interact_waits = wait;
513    else
514	ptr->next = wait;
515
516    IceGetHeader (iceConn, _SmcOpcode, SM_InteractRequest,
517	SIZEOF (smInteractRequestMsg), smInteractRequestMsg, pMsg);
518
519    pMsg->dialogType = dialogType;
520
521    IceFlush (iceConn);
522
523    return (1);
524}
525
526
527
528void
529SmcInteractDone(SmcConn smcConn, Bool cancelShutdown)
530{
531    IceConn		iceConn = smcConn->iceConn;
532    smInteractDoneMsg	*pMsg;
533
534    IceGetHeader (iceConn, _SmcOpcode, SM_InteractDone,
535	SIZEOF (smInteractDoneMsg), smInteractDoneMsg, pMsg);
536
537    pMsg->cancelShutdown = cancelShutdown;
538
539    IceFlush (iceConn);
540}
541
542
543
544void
545SmcRequestSaveYourself(SmcConn smcConn, int saveType, Bool shutdown,
546		       int interactStyle, Bool fast, Bool global)
547{
548    IceConn			iceConn = smcConn->iceConn;
549    smSaveYourselfRequestMsg	*pMsg;
550
551    IceGetHeader (iceConn, _SmcOpcode, SM_SaveYourselfRequest,
552	SIZEOF (smSaveYourselfRequestMsg), smSaveYourselfRequestMsg, pMsg);
553
554    pMsg->saveType = saveType;
555    pMsg->shutdown = shutdown;
556    pMsg->interactStyle = interactStyle;
557    pMsg->fast = fast;
558    pMsg->global = global;
559
560    IceFlush (iceConn);
561}
562
563
564
565Status
566SmcRequestSaveYourselfPhase2(SmcConn smcConn,
567			     SmcSaveYourselfPhase2Proc saveYourselfPhase2Proc,
568			     SmPointer clientData)
569{
570    IceConn		iceConn = smcConn->iceConn;
571    _SmcPhase2Wait 	*wait;
572
573    if (smcConn->phase2_wait)
574	wait = smcConn->phase2_wait;
575    else
576    {
577	if ((wait = malloc (sizeof (_SmcPhase2Wait))) == NULL)
578	{
579	    return (0);
580	}
581    }
582
583    wait->phase2_proc = saveYourselfPhase2Proc;
584    wait->client_data = clientData;
585
586    smcConn->phase2_wait = wait;
587
588    IceSimpleMessage (iceConn, _SmcOpcode, SM_SaveYourselfPhase2Request);
589    IceFlush (iceConn);
590
591    return (1);
592}
593
594
595
596void
597SmcSaveYourselfDone(SmcConn smcConn, Bool success)
598{
599    IceConn			iceConn = smcConn->iceConn;
600    smSaveYourselfDoneMsg	*pMsg;
601
602    IceGetHeader (iceConn, _SmcOpcode, SM_SaveYourselfDone,
603	SIZEOF (smSaveYourselfDoneMsg), smSaveYourselfDoneMsg, pMsg);
604
605    pMsg->success = success;
606
607    IceFlush (iceConn);
608}
609
610
611
612static void
613set_callbacks(SmcConn smcConn, unsigned long mask, SmcCallbacks *callbacks)
614{
615    if (mask & SmcSaveYourselfProcMask)
616    {
617	smcConn->callbacks.save_yourself.callback =
618	    callbacks->save_yourself.callback;
619	smcConn->callbacks.save_yourself.client_data =
620	    callbacks->save_yourself.client_data;
621    }
622
623    if (mask & SmcDieProcMask)
624    {
625	smcConn->callbacks.die.callback = callbacks->die.callback;
626	smcConn->callbacks.die.client_data = callbacks->die.client_data;
627    }
628
629    if (mask & SmcSaveCompleteProcMask)
630    {
631	smcConn->callbacks.save_complete.callback =
632	    callbacks->save_complete.callback;
633	smcConn->callbacks.save_complete.client_data =
634	    callbacks->save_complete.client_data;
635    }
636
637    if (mask & SmcShutdownCancelledProcMask)
638    {
639	smcConn->callbacks.shutdown_cancelled.callback =
640	    callbacks->shutdown_cancelled.callback;
641	smcConn->callbacks.shutdown_cancelled.client_data =
642	    callbacks->shutdown_cancelled.client_data;
643    }
644}
645