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 = &prop;
 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 = &prop;
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 }