sm_client.c revision 0a6b08f8
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 = (char *) 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    STORE_ARRAY8 (pData, len, previousId);
208
209    IceFlush (iceConn);
210
211    replyWait.sequence_of_request = IceLastSentSequenceNumber (iceConn);
212    replyWait.major_opcode_of_request = _SmcOpcode;
213    replyWait.minor_opcode_of_request = SM_RegisterClient;
214    replyWait.reply = (IcePointer) &reply;
215
216    gotReply = False;
217    ioErrorOccured = False;
218
219    while (!gotReply && !ioErrorOccured)
220    {
221	ioErrorOccured = (IceProcessMessages (
222	    iceConn, &replyWait, &gotReply) == IceProcessMessagesIOError);
223
224	if (ioErrorOccured)
225	{
226	    if (errorStringRet && errorLength > 0) {
227		strncpy (errorStringRet, "IO error occured opening connection",
228			 errorLength);
229		errorStringRet[errorLength - 1] = '\0';
230	    }
231	    free (smcConn->vendor);
232	    free (smcConn->release);
233	    free (smcConn);
234
235	    return (NULL);
236	}
237	else if (gotReply)
238	{
239	    if (reply.status == 1)
240	    {
241		/*
242		 * The client successfully registered.
243		 */
244
245		*clientIdRet = reply.client_id;
246
247		smcConn->client_id = strdup (*clientIdRet);
248	    }
249	    else
250	    {
251		/*
252		 * Could not register the client because the previous ID
253		 * was bad.  So now we register the client with the
254		 * previous ID set to NULL.
255		 */
256
257		extra = ARRAY8_BYTES (0);
258
259		IceGetHeaderExtra (iceConn, _SmcOpcode, SM_RegisterClient,
260		    SIZEOF (smRegisterClientMsg), WORD64COUNT (extra),
261		    smRegisterClientMsg, pMsg, pData);
262
263		STORE_ARRAY8 (pData, 0, "");
264
265		IceFlush (iceConn);
266
267		replyWait.sequence_of_request =
268		    IceLastSentSequenceNumber (iceConn);
269
270		gotReply = False;
271	    }
272	}
273    }
274
275    return (smcConn);
276}
277
278
279
280SmcCloseStatus
281SmcCloseConnection(SmcConn smcConn, int count, char **reasonMsgs)
282{
283    IceConn			iceConn = smcConn->iceConn;
284    smCloseConnectionMsg 	*pMsg;
285    char 			*pData;
286    int				extra, i;
287    IceCloseStatus	        closeStatus;
288    SmcCloseStatus		statusRet;
289
290    extra = 8;
291
292    for (i = 0; i < count; i++)
293	extra += ARRAY8_BYTES (strlen (reasonMsgs[i]));
294
295    IceGetHeaderExtra (iceConn, _SmcOpcode, SM_CloseConnection,
296	SIZEOF (smCloseConnectionMsg), WORD64COUNT (extra),
297	smCloseConnectionMsg, pMsg, pData);
298
299    STORE_CARD32 (pData, (CARD32) count);
300    pData += 4;
301
302    for (i = 0; i < count; i++)
303	STORE_ARRAY8 (pData, strlen (reasonMsgs[i]), reasonMsgs[i]);
304
305    IceFlush (iceConn);
306
307    IceProtocolShutdown (iceConn, _SmcOpcode);
308    IceSetShutdownNegotiation (iceConn, False);
309    closeStatus = IceCloseConnection (iceConn);
310
311    if (smcConn->vendor)
312	free (smcConn->vendor);
313
314    if (smcConn->release)
315	free (smcConn->release);
316
317    if (smcConn->client_id)
318	free (smcConn->client_id);
319
320    if (smcConn->prop_reply_waits)
321    {
322	_SmcPropReplyWait *ptr = smcConn->prop_reply_waits;
323	_SmcPropReplyWait *next;
324
325	while (ptr)
326	{
327	    next = ptr->next;
328	    free (ptr);
329	    ptr = next;
330	}
331
332    }
333
334    free (smcConn);
335
336    if (closeStatus == IceClosedNow)
337	statusRet = SmcClosedNow;
338    else if (closeStatus == IceClosedASAP)
339	statusRet = SmcClosedASAP;
340    else
341	statusRet = SmcConnectionInUse;
342
343    return (statusRet);
344}
345
346
347
348void
349SmcModifyCallbacks(SmcConn smcConn, unsigned long mask, SmcCallbacks *callbacks)
350{
351    set_callbacks (smcConn, mask, callbacks);
352}
353
354
355
356void
357SmcSetProperties(SmcConn smcConn, int numProps, SmProp **props)
358{
359    IceConn		iceConn = smcConn->iceConn;
360    smSetPropertiesMsg	*pMsg;
361    char		*pBuf;
362    char		*pStart;
363    unsigned int	bytes;
364
365    IceGetHeader (iceConn, _SmcOpcode, SM_SetProperties,
366	SIZEOF (smSetPropertiesMsg), smSetPropertiesMsg, pMsg);
367
368    LISTOF_PROP_BYTES (numProps, props, bytes);
369    pMsg->length += WORD64COUNT (bytes);
370
371    pBuf = pStart = IceAllocScratch (iceConn, bytes);
372    memset(pStart, 0, bytes);
373
374    STORE_LISTOF_PROPERTY (pBuf, numProps, props);
375
376    IceWriteData (iceConn, bytes, pStart);
377    IceFlush (iceConn);
378}
379
380
381
382void
383SmcDeleteProperties(SmcConn smcConn, int numProps, char **propNames)
384{
385    IceConn			iceConn = smcConn->iceConn;
386    smDeletePropertiesMsg 	*pMsg;
387    char 			*pData;
388    int				extra, i;
389
390    extra = 8;
391
392    for (i = 0; i < numProps; i++)
393	extra += ARRAY8_BYTES (strlen (propNames[i]));
394
395    IceGetHeaderExtra (iceConn, _SmcOpcode, SM_DeleteProperties,
396	SIZEOF (smDeletePropertiesMsg), WORD64COUNT (extra),
397	smDeletePropertiesMsg, pMsg, pData);
398
399    STORE_CARD32 (pData, numProps);
400    pData += 4;
401
402    for (i = 0; i < numProps; i++)
403	STORE_ARRAY8 (pData, strlen (propNames[i]), propNames[i]);
404
405    IceFlush (iceConn);
406}
407
408
409
410Status
411SmcGetProperties(SmcConn smcConn, SmcPropReplyProc propReplyProc,
412		 SmPointer clientData)
413{
414    IceConn		iceConn = smcConn->iceConn;
415    _SmcPropReplyWait 	*wait, *ptr;
416
417    if ((wait = malloc (sizeof (_SmcPropReplyWait))) == NULL)
418    {
419	return (0);
420    }
421
422    wait->prop_reply_proc = propReplyProc;
423    wait->client_data = clientData;
424    wait->next = NULL;
425
426    ptr = smcConn->prop_reply_waits;
427    while (ptr && ptr->next)
428	ptr = ptr->next;
429
430    if (ptr == NULL)
431	smcConn->prop_reply_waits = wait;
432    else
433	ptr->next = wait;
434
435    IceSimpleMessage (iceConn, _SmcOpcode, SM_GetProperties);
436    IceFlush (iceConn);
437
438    return (1);
439}
440
441
442
443Status
444SmcInteractRequest(SmcConn smcConn, int dialogType,
445		   SmcInteractProc interactProc, SmPointer clientData)
446{
447    IceConn			iceConn = smcConn->iceConn;
448    smInteractRequestMsg	*pMsg;
449    _SmcInteractWait 		*wait, *ptr;
450
451    if ((wait = malloc (sizeof (_SmcInteractWait))) == NULL)
452    {
453	return (0);
454    }
455
456    wait->interact_proc = interactProc;
457    wait->client_data = clientData;
458    wait->next = NULL;
459
460    ptr = smcConn->interact_waits;
461    while (ptr && ptr->next)
462	ptr = ptr->next;
463
464    if (ptr == NULL)
465	smcConn->interact_waits = wait;
466    else
467	ptr->next = wait;
468
469    IceGetHeader (iceConn, _SmcOpcode, SM_InteractRequest,
470	SIZEOF (smInteractRequestMsg), smInteractRequestMsg, pMsg);
471
472    pMsg->dialogType = dialogType;
473
474    IceFlush (iceConn);
475
476    return (1);
477}
478
479
480
481void
482SmcInteractDone(SmcConn smcConn, Bool cancelShutdown)
483{
484    IceConn		iceConn = smcConn->iceConn;
485    smInteractDoneMsg	*pMsg;
486
487    IceGetHeader (iceConn, _SmcOpcode, SM_InteractDone,
488	SIZEOF (smInteractDoneMsg), smInteractDoneMsg, pMsg);
489
490    pMsg->cancelShutdown = cancelShutdown;
491
492    IceFlush (iceConn);
493}
494
495
496
497void
498SmcRequestSaveYourself(SmcConn smcConn, int saveType, Bool shutdown,
499		       int interactStyle, Bool fast, Bool global)
500{
501    IceConn			iceConn = smcConn->iceConn;
502    smSaveYourselfRequestMsg	*pMsg;
503
504    IceGetHeader (iceConn, _SmcOpcode, SM_SaveYourselfRequest,
505	SIZEOF (smSaveYourselfRequestMsg), smSaveYourselfRequestMsg, pMsg);
506
507    pMsg->saveType = saveType;
508    pMsg->shutdown = shutdown;
509    pMsg->interactStyle = interactStyle;
510    pMsg->fast = fast;
511    pMsg->global = global;
512
513    IceFlush (iceConn);
514}
515
516
517
518Status
519SmcRequestSaveYourselfPhase2(SmcConn smcConn,
520			     SmcSaveYourselfPhase2Proc saveYourselfPhase2Proc,
521			     SmPointer clientData)
522{
523    IceConn		iceConn = smcConn->iceConn;
524    _SmcPhase2Wait 	*wait;
525
526    if (smcConn->phase2_wait)
527	wait = smcConn->phase2_wait;
528    else
529    {
530	if ((wait = malloc (sizeof (_SmcPhase2Wait))) == NULL)
531	{
532	    return (0);
533	}
534    }
535
536    wait->phase2_proc = saveYourselfPhase2Proc;
537    wait->client_data = clientData;
538
539    smcConn->phase2_wait = wait;
540
541    IceSimpleMessage (iceConn, _SmcOpcode, SM_SaveYourselfPhase2Request);
542    IceFlush (iceConn);
543
544    return (1);
545}
546
547
548
549void
550SmcSaveYourselfDone(SmcConn smcConn, Bool success)
551{
552    IceConn			iceConn = smcConn->iceConn;
553    smSaveYourselfDoneMsg	*pMsg;
554
555    IceGetHeader (iceConn, _SmcOpcode, SM_SaveYourselfDone,
556	SIZEOF (smSaveYourselfDoneMsg), smSaveYourselfDoneMsg, pMsg);
557
558    pMsg->success = success;
559
560    IceFlush (iceConn);
561}
562
563
564
565static void
566set_callbacks(SmcConn smcConn, unsigned long mask, SmcCallbacks *callbacks)
567{
568    if (mask & SmcSaveYourselfProcMask)
569    {
570	smcConn->callbacks.save_yourself.callback =
571	    callbacks->save_yourself.callback;
572	smcConn->callbacks.save_yourself.client_data =
573	    callbacks->save_yourself.client_data;
574    }
575
576    if (mask & SmcDieProcMask)
577    {
578	smcConn->callbacks.die.callback = callbacks->die.callback;
579	smcConn->callbacks.die.client_data = callbacks->die.client_data;
580    }
581
582    if (mask & SmcSaveCompleteProcMask)
583    {
584	smcConn->callbacks.save_complete.callback =
585	    callbacks->save_complete.callback;
586	smcConn->callbacks.save_complete.client_data =
587	    callbacks->save_complete.client_data;
588    }
589
590    if (mask & SmcShutdownCancelledProcMask)
591    {
592	smcConn->callbacks.shutdown_cancelled.callback =
593	    callbacks->shutdown_cancelled.callback;
594	smcConn->callbacks.shutdown_cancelled.client_data =
595	    callbacks->shutdown_cancelled.client_data;
596    }
597}
598