1 /*
2 * Copyright �� 2005 Novell, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Novell, Inc. not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior permission.
11 * Novell, Inc. makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *
15 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author: Radek Doulik <rodo@novell.com>
24 */
25
26
27 #include "core/session.h"
28 #include "core/screen.h"
29
30 #ifdef HAVE_CONFIG_H
31 # include <config.h>
32 #endif
33
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <poll.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <pwd.h>
41 #include <X11/SM/SMlib.h>
42 #include <X11/ICE/ICElib.h>
43
44 #include <boost/bind.hpp>
45
46 #define SM_DEBUG(x)
47
48 static SmcConn smcConnection;
49 static CompWatchFdHandle iceWatchFdHandle;
50 static bool connected = false;
51 static bool iceConnected = false;
52 static char *smClientId, *smPrevClientId;
53
54 static void iceInit (void);
55
56 static void
57 setStringListProperty (SmcConn connection,
58 const char *name,
59 const char **values,
60 int nValues)
61 {
62 SmProp prop, *pProp;
63
64 prop.name = (char *) name;
65 prop.type = const_cast<char *> (SmLISTofARRAY8);
66
67 prop.vals = (SmPropValue *) malloc (nValues * sizeof (SmPropValue));
68 if (!prop.vals)
69 return;
70
71 for (int i = 0; i < nValues; i++)
72 {
73 prop.vals[i].value = (char *) values[i];
74 prop.vals[i].length = strlen (values[i]);
75 }
76
77 prop.num_vals = nValues;
78
79 pProp = ∝
80
81 SmcSetProperties (connection, 1, &pProp);
82
83 free (prop.vals);
84 }
85
86 static void
87 setCloneRestartCommands (SmcConn connection)
88 {
CID 12557 - UNINIT
Declaring variable "args" without initializer.
89 const char **args;
90
91 /* at maximum, we pass our old arguments + our new client id
92 to the SM, so allocate for that case */
CID 12557 - UNINIT
Calling allocator "malloc(size_t)".
Assigning: "args" = "(char const **)malloc((programArgc + 2) * 8UL)", which is allocated but not initialized.
93 args = (const char **) malloc ((programArgc + 2) * sizeof (char *));
Condition "!args", taking false branch
94 if (!args)
95 return;
96
97 int i, count = 0;
98
Condition "i < programArgc", taking true branch
Jumped back to beginning of loop
Condition "i < programArgc", taking true branch
Jumped back to beginning of loop
Condition "i < programArgc", taking false branch
99 for (i = 0; i < programArgc; i++)
100 {
Condition "strcmp(programArgv[i], "--sm-client-id") == 0", taking true branch
Condition "strcmp(programArgv[i], "--sm-client-id") == 0", taking true branch
101 if (strcmp (programArgv[i], "--sm-client-id") == 0)
102 i++; /* skip old client id, we'll add the new one later */
103 else if (strcmp (programArgv[i], "--replace") == 0)
104 continue; /* there's nothing to replace when starting session */
105 else
106 args[count++] = programArgv[i];
Jumping back to the beginning of the loop
Jumping back to the beginning of the loop
Reached end of loop
107 }
108
CID 12557 - UNINIT
Using uninitialized value "*args" when calling "setStringListProperty(SmcConn, char const *, char const **, int)".
109 setStringListProperty (connection, SmCloneCommand, args, count);
110
111 /* insert new client id at position 1 and 2;
112 position 0 is the executable name */
113 for (i = count - 1; i >= 1; i--)
114 args[i + 2] = args[i];
115 args[1] = "--sm-client-id";
116 args[2] = smClientId;
117 count += 2;
118
119 setStringListProperty (connection, SmRestartCommand, args, count);
120
121 free (args);
122 }
123
124 static void
125 setRestartStyle (SmcConn connection,
126 char hint)
127 {
128 SmProp prop, *pProp;
129 SmPropValue propVal;
130
131 prop.name = const_cast<char *> (SmRestartStyleHint);
132 prop.type = const_cast<char *> (SmCARD8);
133 prop.num_vals = 1;
134 prop.vals = &propVal;
135 propVal.value = &hint;
136 propVal.length = 1;
137
138 pProp = ∝
139
140 SmcSetProperties (connection, 1, &pProp);
141 }
142
143 static void
144 setProgramInfo (SmcConn connection,
145 pid_t pid,
146 uid_t uid)
147 {
148 SmProp progProp, pidProp, userProp;
149 SmPropValue progVal, pidVal, userVal;
150 SmProp *props[3];
151 char pidBuffer[32];
152 unsigned int count = 0;
153 struct passwd *pw;
154
155 progProp.name = const_cast<char *> (SmProgram);
156 progProp.type = const_cast<char *> (SmARRAY8);
157 progProp.num_vals = 1;
158 progProp.vals = &progVal;
159 progVal.value = (SmPointer) "compiz";
160 progVal.length = strlen ((char *) progVal.value);
161
162 props[count++] = &progProp;
163
164 snprintf (pidBuffer, sizeof (pidBuffer), "%d", pid);
165
166 pidProp.name = const_cast<char *> (SmProcessID);
167 pidProp.type = const_cast<char *> (SmARRAY8);
168 pidProp.num_vals = 1;
169 pidProp.vals = &pidVal;
170 pidVal.value = (SmPointer) pidBuffer;
171 pidVal.length = strlen (pidBuffer);
172
173 props[count++] = &pidProp;
174
175 pw = getpwuid (uid);
176 if (pw)
177 {
178 userProp.name = const_cast<char *> (SmUserID);
179 userProp.type = const_cast<char *> (SmARRAY8);
180 userProp.num_vals = 1;
181 userProp.vals = &userVal;
182 userVal.value = (SmPointer) pw->pw_name;
183 userVal.length = strlen (pw->pw_name);
184
185 props[count++] = &userProp;
186 }
187
188 SmcSetProperties (connection, count, props);
189 }
190
191 static void
192 saveYourselfCallback (SmcConn connection,
193 SmPointer client_data,
194 int saveType,
195 Bool shutdown,
196 int interact_Style,
197 Bool fast)
198 {
199 CompOption::Vector args;
200
201 args.push_back (CompOption ("save_type", CompOption::TypeInt));
202 args.push_back (CompOption ("shutdown", CompOption::TypeBool));
203 args.push_back (CompOption ("interact_style", CompOption::TypeInt));
204 args.push_back (CompOption ("fast", CompOption::TypeBool));
205
206 args[0].value ().set (saveType);
207 args[1].value ().set ((bool) shutdown);
208 args[2].value ().set (interact_Style);
209 args[3].value ().set ((bool) fast);
210
211 screen->sessionEvent (CompSession::EventSaveYourself, args);
212
213 setCloneRestartCommands (connection);
214 setRestartStyle (connection, SmRestartImmediately);
215 setProgramInfo (connection, getpid (), getuid ());
216 SmcSaveYourselfDone (connection, 1);
217 }
218
219 static void
220 dieCallback (SmcConn connection,
221 SmPointer clientData)
222 {
223 screen->sessionEvent (CompSession::EventDie, noOptions ());
224
225 CompSession::close ();
226 exit (0);
227 }
228
229 static void
230 saveCompleteCallback (SmcConn connection,
231 SmPointer clientData)
232 {
233 screen->sessionEvent (CompSession::EventSaveComplete, noOptions ());
234 }
235
236 static void
237 shutdownCancelledCallback (SmcConn connection,
238 SmPointer clientData)
239 {
240 screen->sessionEvent (CompSession::EventShutdownCancelled, noOptions ());
241 }
242
243 void
244 CompSession::init (char *prevClientId)
245 {
246 static SmcCallbacks callbacks;
247
248 if (getenv ("SESSION_MANAGER"))
249 {
250 char errorBuffer[1024];
251
252 iceInit ();
253
254 callbacks.save_yourself.callback = saveYourselfCallback;
255 callbacks.save_yourself.client_data = NULL;
256
257 callbacks.die.callback = dieCallback;
258 callbacks.die.client_data = NULL;
259
260 callbacks.save_complete.callback = saveCompleteCallback;
261 callbacks.save_complete.client_data = NULL;
262
263 callbacks.shutdown_cancelled.callback = shutdownCancelledCallback;
264 callbacks.shutdown_cancelled.client_data = NULL;
265
266 smcConnection = SmcOpenConnection (NULL,
267 NULL,
268 SmProtoMajor,
269 SmProtoMinor,
270 SmcSaveYourselfProcMask |
271 SmcDieProcMask |
272 SmcSaveCompleteProcMask |
273 SmcShutdownCancelledProcMask,
274 &callbacks,
275 prevClientId,
276 &smClientId,
277 sizeof (errorBuffer),
278 errorBuffer);
279 if (!smcConnection)
280 compLogMessage ("core", CompLogLevelWarn,
281 "SmcOpenConnection failed: %s",
282 errorBuffer);
283 else
284 {
285 connected = true;
286 if (prevClientId)
287 smPrevClientId = strdup (prevClientId);
288 setRestartStyle (smcConnection, SmRestartImmediately);
289
290 }
291 }
292 }
293
294 void
295 CompSession::close ()
296 {
297 if (connected)
298 {
299 setRestartStyle (smcConnection, SmRestartIfRunning);
300
301 if (SmcCloseConnection (smcConnection, 0, NULL) != SmcConnectionInUse)
302 connected = false;
303
304 if (smClientId)
305 {
306 free (smClientId);
307 smClientId = NULL;
308 }
309 if (smPrevClientId)
310 {
311 free (smPrevClientId);
312 smPrevClientId = NULL;
313 }
314 }
315 }
316
317 CompString
318 CompSession::getClientId (CompSession::ClientIdType type)
319 {
320 if (!connected)
321 return "";
322
323 switch (type) {
324 case CompSession::ClientId:
325 if (smClientId)
326 return smClientId;
327 case CompSession::PrevClientId:
328 if (smPrevClientId)
329 return smPrevClientId;
330 }
331
332 return "";
333 }
334 /* ice connection handling taken and updated from gnome-ice.c
335 * original gnome-ice.c code written by Tom Tromey <tromey@cygnus.com>
336 */
337
338 /* This is called when data is available on an ICE connection. */
339 static bool
340 iceProcessMessages (IceConn connection)
341 {
342 IceProcessMessagesStatus status;
343
344 SM_DEBUG (printf ("ICE connection process messages\n"));
345
346 status = IceProcessMessages (connection, NULL, NULL);
347
348 if (status == IceProcessMessagesIOError)
349 {
350 SM_DEBUG (printf ("ICE connection process messages"
351 " - error => shutting down the connection\n"));
352
353 IceSetShutdownNegotiation (connection, False);
354 IceCloseConnection (connection);
355 }
356
357 return 1;
358 }
359
360 /* This is called when a new ICE connection is made. It arranges for
361 the ICE connection to be handled via the event loop. */
362 static void
363 iceNewConnection (IceConn connection,
364 IcePointer clientData,
365 Bool opening,
366 IcePointer *watchData)
367 {
368 if (opening)
369 {
370 SM_DEBUG (printf ("ICE connection opening\n"));
371
372 /* Make sure we don't pass on these file descriptors to any
373 exec'ed children */
374 fcntl (IceConnectionNumber (connection), F_SETFD,
375 fcntl (IceConnectionNumber (connection),
376 F_GETFD,0) | FD_CLOEXEC);
377
378 iceWatchFdHandle = screen->addWatchFd (IceConnectionNumber (connection),
379 POLLIN | POLLPRI | POLLHUP | POLLERR,
380 boost::bind (iceProcessMessages, connection));
381
382 iceConnected = true;
383 }
384 else
385 {
386 SM_DEBUG (printf ("ICE connection closing\n"));
387
388 if (iceConnected)
389 {
390 screen->removeWatchFd (iceWatchFdHandle);
391
392 iceWatchFdHandle = 0;
393 iceConnected = false;
394 }
395 }
396 }
397
398 static IceIOErrorHandler oldIceHandler;
399
400 static void
401 iceErrorHandler (IceConn connection)
402 {
403 if (oldIceHandler)
404 (*oldIceHandler) (connection);
405 }
406
407 /* We call any handler installed before (or after) iceInit but
408 avoid calling the default libICE handler which does an exit() */
409 static void
410 iceInit (void)
411 {
412 static bool iceInitialized = false;
413
414 if (!iceInitialized)
415 {
416 IceIOErrorHandler defaultIceHandler;
417
418 oldIceHandler = IceSetIOErrorHandler (NULL);
419 defaultIceHandler = IceSetIOErrorHandler (iceErrorHandler);
420
421 if (oldIceHandler == defaultIceHandler)
422 oldIceHandler = NULL;
423
424 IceAddConnectionWatch (iceNewConnection, NULL);
425
426 iceInitialized = true;
427 }
428 }