Better error handling when getting sizehints fails
[notion.git] / mod_sm / sm_session.c
blob23c643b877bb30795f78412936c56d91d650669a
1 /*
2 * ion/mod_sm/sm_session.c
4 * Copyright (c) Tuomo Valkonen 2004-2009.
5 *
6 * Based on the code of the 'sm' module for Ion1 by an unknown contributor.
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.
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>
22 #include <X11/Xlib.h>
23 #include <X11/SM/SMlib.h>
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"
34 static IceConn ice_sm_conn=NULL;
35 static SmcConn sm_conn=NULL;
36 static int sm_fd=-1;
38 static char *sm_client_id=NULL;
39 static char restart_hint=SmRestartImmediately;
41 static Bool sent_save_done=FALSE;
43 /* Function to be called when sm tells client save is complete */
44 static void (*save_complete_fn)();
46 void mod_sm_set_ion_id(const char *client_id)
48 if(sm_client_id)
49 free(sm_client_id);
51 if(client_id==NULL)
52 sm_client_id=NULL;
53 else
54 sm_client_id=scopy(client_id);
57 char *mod_sm_get_ion_id()
59 return sm_client_id;
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. */
67 static void sm_process_messages(int UNUSED(fd), void *UNUSED(data))
69 Bool ret;
71 if(IceProcessMessages(ice_sm_conn, NULL, &ret)==IceProcessMessagesIOError){
72 mod_sm_close();
76 /* Callback triggered when an Ice connection is
77 opened or closed. */
79 static void sm_ice_watch_fd(IceConn conn,
80 IcePointer UNUSED(client_data),
81 Bool opening,
82 IcePointer *UNUSED(watch_data))
84 if(opening){
85 if(sm_fd!=-1){ /* shouldn't happen */
86 warn(TR("Too many ICE connections."));
88 else{
89 sm_fd=IceConnectionNumber(conn);
90 cloexec_braindamage_fix(sm_fd);
91 mainloop_register_input_fd(sm_fd, NULL, &sm_process_messages);
94 else{
95 if (IceConnectionNumber(conn)==sm_fd){
96 mainloop_unregister_input_fd(sm_fd);
97 sm_fd=-1;
102 /* Store restart information and stuff in the session manager */
104 static void sm_set_some_properties()
106 SmPropValue program_val, userid_val;
107 SmProp program_prop, userid_prop, clone_prop;
108 SmProp *props[3];
110 props[0]=&program_prop;
111 props[1]=&userid_prop;
112 props[2]=&clone_prop;
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;
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;
128 clone_prop.name=SmCloneCommand;
129 clone_prop.type=SmLISTofARRAY8;
130 clone_prop.num_vals=1;
131 clone_prop.vals=&program_val;
133 SmcSetProperties(sm_conn,
134 sizeof(props)/sizeof(props[0]),
135 (SmProp **)&props);
138 static void sm_set_other_properties()
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;
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};
153 SmProp *props[2];
155 discard_prop.vals=discard_val;
156 restart_hint_prop.vals=&restart_hint_val;
158 props[0]=&restart_prop;
159 props[1]=&restart_hint_prop;
160 /*props[2]=&discard_prop;*/
162 sdir=extl_sessiondir();
163 cid=mod_sm_get_ion_id();
165 if(sdir==NULL || cid==NULL)
166 return;
168 restart_hint_val.value=&restart_hint;
169 restart_hint_val.length=1;
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]);
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;
198 SmcSetProperties(sm_conn,
199 sizeof(props)/sizeof(props[0]),
200 (SmProp **)&props);
202 free(restart_val);
205 static void sm_set_properties()
207 static bool init=TRUE;
209 if(init){
210 sm_set_some_properties();
211 init=FALSE;
214 sm_set_other_properties();
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. */
223 static void sm_save_yourself_phase2(SmcConn conn, SmPointer UNUSED(client_data))
225 Bool success;
227 if(!(success=ioncore_do_snapshot(TRUE)))
228 warn(TR("Failed to save session state"));
229 else
230 sm_set_properties();
232 SmcSaveYourselfDone(conn, success);
233 sent_save_done=TRUE;
236 /* Callback. Called when the client recieves a save yourself
237 message from the sm. */
239 static void sm_save_yourself(SmcConn UNUSED(conn),
240 SmPointer UNUSED(client_data),
241 int UNUSED(save_type),
242 Bool UNUSED(shutdown),
243 int UNUSED(interact_style),
244 Bool UNUSED(fast))
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;
256 /* Response to the shutdown cancelled message */
258 static void sm_shutdown_cancelled(SmcConn conn, SmPointer UNUSED(client_data))
260 save_complete_fn=NULL;
261 if(!sent_save_done){
262 SmcSaveYourselfDone(conn, False);
263 sent_save_done=True;
267 /* Callback */
269 static void sm_save_complete(SmcConn UNUSED(conn), SmPointer UNUSED(client_data))
271 if(save_complete_fn){
272 save_complete_fn();
273 save_complete_fn=NULL;
277 /* Callback */
279 static void sm_die(SmcConn conn, SmPointer UNUSED(client_data))
281 assert(conn==sm_conn);
282 ioncore_do_exit();
286 /* Connects to the sm and registers
287 callbacks for different messages */
289 bool mod_sm_init_session()
291 char error_str[256];
292 char *new_client_id=NULL;
293 SmcCallbacks smcall;
295 if(getenv("SESSION_MANAGER")==0){
296 warn(TR("SESSION_MANAGER environment variable not set."));
297 return FALSE;
300 if(IceAddConnectionWatch(&sm_ice_watch_fd, NULL) == 0){
301 warn(TR("Session Manager: IceAddConnectionWatch failed."));
302 return FALSE;
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;
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)
326 warn(TR("Unable to connect to the session manager."));
327 return FALSE;
330 mod_sm_set_ion_id(new_client_id);
331 free(new_client_id);
333 ice_sm_conn=SmcGetIceConnection(sm_conn);
335 return TRUE;
339 void mod_sm_close()
341 if(sm_conn!=NULL){
342 SmcCloseConnection(sm_conn, 0, NULL);
343 sm_conn=NULL;
346 ice_sm_conn=NULL;
348 if(sm_fd>=0){
349 mainloop_unregister_input_fd(sm_fd);
350 close(sm_fd);
351 sm_fd=-1;
354 if(sm_client_id!=NULL){
355 free(sm_client_id);
356 sm_client_id=NULL;
361 static void sm_exit()
363 sm_die(sm_conn, NULL);
367 static void sm_restart()
369 ioncore_do_restart();
373 void mod_sm_smhook(int what)
375 save_complete_fn=NULL;
377 /* pending check? */
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;