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