]> git.decadent.org.uk Git - ion3.git/blob - mod_sm/sm_session.c
087431154b8cdb20aa434f9fab1c8842dc1e9fb5
[ion3.git] / mod_sm / sm_session.c
1 /*
2  * ion/mod_sm/sm_session.c
3  *
4  * Copyright (c) Tuomo Valkonen 2004-2008. 
5  * 
6  * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
7  *
8  * This is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  */
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <libtu/misc.h>
21
22 #include <X11/Xlib.h>
23 #include <X11/SM/SMlib.h>
24
25 #include <libextl/readconfig.h>
26 #include <libmainloop/select.h>
27 #include <libmainloop/exec.h>
28 #include <ioncore/exec.h>
29 #include <ioncore/global.h>
30 #include <ioncore/ioncore.h>
31 #include "sm_session.h"
32
33
34 static IceConn ice_sm_conn=NULL;
35 static SmcConn sm_conn=NULL;
36 static int sm_fd=-1;
37
38 static char *sm_client_id=NULL;
39 static char restart_hint=SmRestartImmediately;
40
41 static Bool sent_save_done=FALSE;
42
43 /* Function to be called when sm tells client save is complete */
44 static void (*save_complete_fn)();
45
46 void mod_sm_set_ion_id(const char *client_id)
47 {
48     if(sm_client_id)
49         free(sm_client_id);
50     
51     if(client_id==NULL)
52         sm_client_id=NULL;
53     else
54         sm_client_id=scopy(client_id);
55 }
56
57 char *mod_sm_get_ion_id()
58 {
59     return sm_client_id;
60 }
61
62 /* Called when there's data to be read.
63  IcePcocessMessages determines message protocol, 
64  unpacks the message and sends it to the client via
65  registered callbacks. */
66
67 static void sm_process_messages(int fd, void *data)
68 {
69     Bool ret;
70     
71     if(IceProcessMessages(ice_sm_conn, NULL, &ret)==IceProcessMessagesIOError){
72         mod_sm_close();
73     }
74 }
75
76 /* Callback triggered when an Ice connection is
77  opened or closed. */
78
79 static void sm_ice_watch_fd(IceConn conn,
80                             IcePointer client_data,
81                             Bool opening,
82                             IcePointer *watch_data)
83 {
84     if(opening){
85         if(sm_fd!=-1){ /* shouldn't happen */
86             warn(TR("Too many ICE connections."));
87         }
88         else{
89             sm_fd=IceConnectionNumber(conn);
90             cloexec_braindamage_fix(sm_fd);
91             mainloop_register_input_fd(sm_fd, NULL, &sm_process_messages);
92         }
93     }
94     else{
95         if (IceConnectionNumber(conn)==sm_fd){
96             mainloop_unregister_input_fd(sm_fd);
97             sm_fd=-1;
98         }
99     }
100 }
101
102 /* Store restart information and stuff in the session manager */
103
104 static void sm_set_some_properties()
105 {
106     SmPropValue program_val, userid_val;
107     SmProp program_prop, userid_prop, clone_prop;
108     SmProp *props[3];
109     
110     props[0]=&program_prop;
111     props[1]=&userid_prop;
112     props[2]=&clone_prop;
113     
114     program_val.value=ioncore_g.argv[0];
115     program_val.length=strlen(program_val.value);
116     program_prop.name=SmProgram;        
117     program_prop.type=SmARRAY8;
118     program_prop.num_vals=1;
119     program_prop.vals=&program_val;
120     
121     userid_val.value=getenv("USER");
122     userid_val.length=strlen(userid_val.value);
123     userid_prop.name=SmUserID;
124     userid_prop.type=SmARRAY8;
125     userid_prop.num_vals=1;
126     userid_prop.vals=&userid_val;
127     
128     clone_prop.name=SmCloneCommand;     
129     clone_prop.type=SmLISTofARRAY8;
130     clone_prop.num_vals=1;
131     clone_prop.vals=&program_val;
132     
133     SmcSetProperties(sm_conn,
134                      sizeof(props)/sizeof(props[0]),
135                      (SmProp **)&props);
136 }
137
138 static void sm_set_other_properties()
139 {
140     char *restore="-session";
141     char *clientid="-smclientid";
142     char *rmprog="/bin/rm";
143     char *rmarg="-rf";
144     int nvals=0, i;
145     const char *sdir=NULL, *cid=NULL;
146     
147     SmPropValue discard_val[3];
148     SmProp discard_prop={ SmDiscardCommand, SmLISTofARRAY8, 3, NULL };
149     SmPropValue restart_hint_val, *restart_val=NULL;
150     SmProp restart_hint_prop={ SmRestartStyleHint, SmCARD8, 1, NULL};
151     SmProp restart_prop={ SmRestartCommand, SmLISTofARRAY8, 0, NULL};
152     
153     SmProp *props[2];
154     
155     discard_prop.vals=discard_val;
156     restart_hint_prop.vals=&restart_hint_val;
157     
158     props[0]=&restart_prop;
159     props[1]=&restart_hint_prop;
160     /*props[2]=&discard_prop;*/
161     
162     sdir=extl_sessiondir();
163     cid=mod_sm_get_ion_id();
164     
165     if(sdir==NULL || cid==NULL)
166         return;
167     
168     restart_hint_val.value=&restart_hint;
169     restart_hint_val.length=1;
170     
171     restart_val=(SmPropValue *)malloc((ioncore_g.argc+4)*sizeof(SmPropValue));
172     for(i=0; i<ioncore_g.argc; i++){
173         if(strcmp(ioncore_g.argv[i], restore)==0 ||
174            strcmp(ioncore_g.argv[i], clientid)==0){
175             i++;
176         }else{
177             restart_val[nvals].value=ioncore_g.argv[i];
178             restart_val[nvals++].length=strlen(ioncore_g.argv[i]);
179         }
180     }
181     restart_val[nvals].value=restore;
182     restart_val[nvals++].length=strlen(restore);
183     restart_val[nvals].value=(char*)sdir;
184     restart_val[nvals++].length=strlen(sdir);
185     restart_val[nvals].value=clientid;
186     restart_val[nvals++].length=strlen(clientid);
187     restart_val[nvals].value=(char*)cid;
188     restart_val[nvals++].length=strlen(cid);
189     restart_prop.num_vals=nvals;
190     restart_prop.vals=restart_val;
191     discard_val[0].length=strlen(rmprog);
192     discard_val[0].value=rmprog;
193     discard_val[1].length=strlen(rmarg);
194     discard_val[1].value=rmarg;
195     discard_val[2].length=strlen(sdir);
196     discard_val[2].value=(char*)sdir;
197
198     SmcSetProperties(sm_conn,
199                      sizeof(props)/sizeof(props[0]),
200                      (SmProp **)&props);
201     
202     free(restart_val);
203 }
204
205 static void sm_set_properties()
206 {
207     static bool init=TRUE;
208     
209     if(init){
210         sm_set_some_properties();
211         init=FALSE;
212     }
213     
214     sm_set_other_properties();
215 }
216
217
218 /* Callback for the save yourself phase 2 message.
219  This message is sent by the sm when other clients in the session are finished
220  saving state. This is requested in the save yourself callback by clients
221  like this one that manages other clients. */
222
223 static void sm_save_yourself_phase2(SmcConn conn, SmPointer client_data)
224 {
225     Bool success;
226
227     if(!(success=ioncore_do_snapshot()))
228         warn(TR("Failed to save session state"));
229     else
230         sm_set_properties();
231     
232     SmcSaveYourselfDone(conn, success);
233     sent_save_done=TRUE;    
234 }
235
236 /* Callback. Called when the client recieves a save yourself
237  message from the sm. */
238
239 static void sm_save_yourself(SmcConn conn,
240                              SmPointer client_data,
241                              int save_type,
242                              Bool shutdown,
243                              int interact_style,
244                              Bool fast)
245 {
246     if(!SmcRequestSaveYourselfPhase2(sm_conn, sm_save_yourself_phase2, NULL)){
247         warn(TR("Failed to request save-yourself-phase2 from "
248                 "session manager."));
249         SmcSaveYourselfDone(sm_conn, False);
250         sent_save_done=TRUE;
251     }else{
252         sent_save_done=FALSE;
253     }
254 }
255
256 /* Response to the shutdown cancelled message */
257
258 static void sm_shutdown_cancelled(SmcConn conn, SmPointer client_data)
259 {
260     save_complete_fn=NULL;
261     if(!sent_save_done){
262         SmcSaveYourselfDone(conn, False);
263         sent_save_done=True;
264     }
265 }
266
267 /* Callback */
268
269 static void sm_save_complete(SmcConn conn, SmPointer client_data)
270 {
271     if(save_complete_fn){
272         save_complete_fn();
273         save_complete_fn=NULL;
274     }
275 }
276
277 /* Callback */
278
279 static void sm_die(SmcConn conn, SmPointer client_data)
280 {
281     assert(conn==sm_conn);
282     ioncore_do_exit();
283 }
284
285
286 /* Connects to the sm and registers
287  callbacks for different messages */
288
289 bool mod_sm_init_session()
290 {
291     char error_str[256];
292     char *new_client_id=NULL;
293     SmcCallbacks smcall;
294     
295     if(getenv("SESSION_MANAGER")==0){
296         warn(TR("SESSION_MANAGER environment variable not set."));
297         return FALSE;
298     }
299     
300     if(IceAddConnectionWatch(&sm_ice_watch_fd, NULL) == 0){
301         warn(TR("Session Manager: IceAddConnectionWatch failed."));
302         return FALSE;
303     }
304     
305     memset(&smcall, 0, sizeof(smcall));
306     smcall.save_yourself.callback=&sm_save_yourself;
307     smcall.save_yourself.client_data=NULL;
308     smcall.die.callback=&sm_die;
309     smcall.die.client_data=NULL;
310     smcall.save_complete.callback=&sm_save_complete;
311     smcall.save_complete.client_data=NULL;
312     smcall.shutdown_cancelled.callback=&sm_shutdown_cancelled;
313     smcall.shutdown_cancelled.client_data=NULL;
314     
315     if((sm_conn=SmcOpenConnection(NULL, /* network ids */
316                                   NULL, /* context */
317                                   1, 0, /* protocol major, minor */
318                                   SmcSaveYourselfProcMask |
319                                   SmcSaveCompleteProcMask |
320                                   SmcShutdownCancelledProcMask |
321                                   SmcDieProcMask,
322                                   &smcall,
323                                   sm_client_id, &new_client_id,
324                                   sizeof(error_str), error_str)) == NULL)
325     {
326         warn(TR("Unable to connect to the session manager."));
327         return FALSE;
328     }
329     
330     mod_sm_set_ion_id(new_client_id);
331     free(new_client_id);
332     
333     ice_sm_conn=SmcGetIceConnection(sm_conn);
334     
335     return TRUE;
336 }
337
338
339 void mod_sm_close()
340 {
341     if(sm_conn!=NULL){
342         SmcCloseConnection(sm_conn, 0, NULL);
343         sm_conn=NULL;
344     }
345     
346     ice_sm_conn=NULL;
347     
348     if(sm_fd>=0){
349         mainloop_unregister_input_fd(sm_fd);
350         close(sm_fd);
351         sm_fd=-1;
352     }
353
354     if(sm_client_id!=NULL){
355         free(sm_client_id);
356         sm_client_id=NULL;
357     }
358 }
359
360
361 static void sm_exit()
362 {
363     sm_die(sm_conn, NULL);
364 }
365
366
367 static void sm_restart()
368 {
369     ioncore_do_restart();
370 }
371
372
373 void mod_sm_smhook(int what)
374 {
375     save_complete_fn=NULL;
376     
377     /* pending check? */
378     
379     switch(what){
380     case IONCORE_SM_RESIGN:
381         restart_hint=SmRestartIfRunning;
382         sm_set_properties();
383         /*SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
384                                SmInteractStyleAny, False, False);
385         save_complete_fn=&sm_exit;*/
386         ioncore_do_exit();
387         break;
388     case IONCORE_SM_SHUTDOWN:
389         restart_hint=SmRestartIfRunning;
390         SmcRequestSaveYourself(sm_conn, SmSaveBoth, True,
391                                SmInteractStyleAny, False, True);
392         break;
393     case IONCORE_SM_RESTART:
394         restart_hint=SmRestartImmediately;
395         SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
396                                SmInteractStyleAny, False, False);
397         save_complete_fn=&sm_exit;
398         break;
399     case IONCORE_SM_RESTART_OTHER:
400         restart_hint=SmRestartIfRunning;
401         SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
402                                SmInteractStyleAny, False, False);
403         save_complete_fn=&sm_restart;
404         break;
405     case IONCORE_SM_SNAPSHOT:
406         restart_hint=SmRestartImmediately;
407         SmcRequestSaveYourself(sm_conn, SmSaveBoth, False,
408                                SmInteractStyleAny, False, True);
409         break;
410     }
411 }
412
413