/* * Copyright © 2005 Novell, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of * Novell, Inc. not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior permission. * Novell, Inc. makes no representations about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Radek Doulik */ #include "core/session.h" #include "core/screen.h" #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #define SM_DEBUG(x) static SmcConn smcConnection; static CompWatchFdHandle iceWatchFdHandle; static bool connected = false; static bool iceConnected = false; static char *smClientId, *smPrevClientId; static void iceInit (void); static void setStringListProperty (SmcConn connection, const char *name, const char **values, int nValues) { SmProp prop, *pProp; prop.name = (char *) name; prop.type = const_cast (SmLISTofARRAY8); prop.vals = (SmPropValue *) malloc (nValues * sizeof (SmPropValue)); if (!prop.vals) return; for (int i = 0; i < nValues; ++i) { prop.vals[i].value = (char *) values[i]; prop.vals[i].length = strlen (values[i]); } prop.num_vals = nValues; pProp = ∝ SmcSetProperties (connection, 1, &pProp); free (prop.vals); } static void setCloneRestartCommands (SmcConn connection) { /* at maximum, we pass our old arguments + our new client id to the SM, so allocate for that case */ const char **args = (const char **) malloc ((programArgc + 2) * sizeof (char *)); if (!args) return; int i, count = 0; for (i = 0; i < programArgc; ++i) { if (strcmp (programArgv[i], "--sm-client-id") == 0) ++i; /* skip old client id, we'll add the new one later */ else if (strcmp (programArgv[i], "--replace") == 0) continue; /* there's nothing to replace when starting session */ else args[count++] = programArgv[i]; } setStringListProperty (connection, SmCloneCommand, args, count); /* insert new client id at position 1 and 2; position 0 is the executable name */ for (i = count - 1; i >= 1; i--) args[i + 2] = args[i]; args[1] = "--sm-client-id"; args[2] = smClientId; count += 2; setStringListProperty (connection, SmRestartCommand, args, count); free (args); } static void setRestartStyle (SmcConn connection, char hint) { SmProp prop, *pProp; SmPropValue propVal; prop.name = const_cast (SmRestartStyleHint); prop.type = const_cast (SmCARD8); prop.num_vals = 1; prop.vals = &propVal; propVal.value = &hint; propVal.length = 1; pProp = ∝ SmcSetProperties (connection, 1, &pProp); } static void setProgramInfo (SmcConn connection, pid_t pid, uid_t uid) { SmProp progProp, pidProp, userProp; SmPropValue progVal, pidVal, userVal; SmProp *props[3]; char pidBuffer[32]; unsigned int count = 0; struct passwd *pw; progProp.name = const_cast (SmProgram); progProp.type = const_cast (SmARRAY8); progProp.num_vals = 1; progProp.vals = &progVal; progVal.value = (SmPointer) "compiz"; progVal.length = strlen ((char *) progVal.value); props[count++] = &progProp; snprintf (pidBuffer, sizeof (pidBuffer), "%d", pid); pidProp.name = const_cast (SmProcessID); pidProp.type = const_cast (SmARRAY8); pidProp.num_vals = 1; pidProp.vals = &pidVal; pidVal.value = (SmPointer) pidBuffer; pidVal.length = strlen (pidBuffer); props[count++] = &pidProp; pw = getpwuid (uid); if (pw) { userProp.name = const_cast (SmUserID); userProp.type = const_cast (SmARRAY8); userProp.num_vals = 1; userProp.vals = &userVal; userVal.value = (SmPointer) pw->pw_name; userVal.length = strlen (pw->pw_name); props[count++] = &userProp; } SmcSetProperties (connection, count, props); } static void saveYourselfCallback (SmcConn connection, SmPointer client_data, int saveType, Bool shutdown, int interact_Style, Bool fast) { CompOption::Vector args; args.push_back (CompOption ("save_type", CompOption::TypeInt)); args.push_back (CompOption ("shutdown", CompOption::TypeBool)); args.push_back (CompOption ("interact_style", CompOption::TypeInt)); args.push_back (CompOption ("fast", CompOption::TypeBool)); args[0].value ().set (saveType); args[1].value ().set ((bool) shutdown); args[2].value ().set (interact_Style); args[3].value ().set ((bool) fast); screen->sessionEvent (CompSession::EventSaveYourself, args); setCloneRestartCommands (connection); setRestartStyle (connection, SmRestartImmediately); setProgramInfo (connection, getpid (), getuid ()); SmcSaveYourselfDone (connection, 1); } static void dieCallback (SmcConn connection, SmPointer clientData) { screen->sessionEvent (CompSession::EventDie, noOptions ()); CompSession::close (); exit (0); } static void saveCompleteCallback (SmcConn connection, SmPointer clientData) { screen->sessionEvent (CompSession::EventSaveComplete, noOptions ()); } static void shutdownCancelledCallback (SmcConn connection, SmPointer clientData) { screen->sessionEvent (CompSession::EventShutdownCancelled, noOptions ()); } void CompSession::init (char *prevClientId) { static SmcCallbacks callbacks; if (getenv ("SESSION_MANAGER")) { char errorBuffer[1024]; iceInit (); callbacks.save_yourself.callback = saveYourselfCallback; callbacks.save_yourself.client_data = NULL; callbacks.die.callback = dieCallback; callbacks.die.client_data = NULL; callbacks.save_complete.callback = saveCompleteCallback; callbacks.save_complete.client_data = NULL; callbacks.shutdown_cancelled.callback = shutdownCancelledCallback; callbacks.shutdown_cancelled.client_data = NULL; smcConnection = SmcOpenConnection (NULL, NULL, SmProtoMajor, SmProtoMinor, SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, &callbacks, prevClientId, &smClientId, sizeof (errorBuffer), errorBuffer); if (!smcConnection) compLogMessage ("core", CompLogLevelWarn, "SmcOpenConnection failed: %s", errorBuffer); else { connected = true; if (prevClientId) smPrevClientId = strdup (prevClientId); setRestartStyle (smcConnection, SmRestartImmediately); } } } void CompSession::close () { if (connected) { setRestartStyle (smcConnection, SmRestartIfRunning); if (SmcCloseConnection (smcConnection, 0, NULL) != SmcConnectionInUse) connected = false; if (smClientId) { free (smClientId); smClientId = NULL; } if (smPrevClientId) { free (smPrevClientId); smPrevClientId = NULL; } } } CompString CompSession::getClientId (CompSession::ClientIdType type) { if (!connected) return ""; switch (type) { case CompSession::ClientId: if (smClientId) return smClientId; break; case CompSession::PrevClientId: if (smPrevClientId) return smPrevClientId; break; default: break; } return ""; } /* ice connection handling taken and updated from gnome-ice.c * original gnome-ice.c code written by Tom Tromey */ /* This is called when data is available on an ICE connection. */ static bool iceProcessMessages (IceConn connection) { SM_DEBUG (printf ("ICE connection process messages\n")); IceProcessMessagesStatus status = IceProcessMessages (connection, NULL, NULL); if (status == IceProcessMessagesIOError) { SM_DEBUG (printf ("ICE connection process messages" " - error => shutting down the connection\n")); IceSetShutdownNegotiation (connection, False); IceCloseConnection (connection); } return 1; } /* This is called when a new ICE connection is made. It arranges for the ICE connection to be handled via the event loop. */ static void iceNewConnection (IceConn connection, IcePointer clientData, Bool opening, IcePointer *watchData) { if (opening) { SM_DEBUG (printf ("ICE connection opening\n")); /* Make sure we don't pass on these file descriptors to any exec'ed children */ fcntl (IceConnectionNumber (connection), F_SETFD, fcntl (IceConnectionNumber (connection), F_GETFD,0) | FD_CLOEXEC); iceWatchFdHandle = screen->addWatchFd (IceConnectionNumber (connection), POLLIN | POLLPRI | POLLHUP | POLLERR, boost::bind (iceProcessMessages, connection)); iceConnected = true; } else { SM_DEBUG (printf ("ICE connection closing\n")); if (iceConnected) { screen->removeWatchFd (iceWatchFdHandle); iceWatchFdHandle = 0; iceConnected = false; } } } static IceIOErrorHandler oldIceHandler; static void iceErrorHandler (IceConn connection) { if (oldIceHandler) (*oldIceHandler) (connection); } /* We call any handler installed before (or after) iceInit but avoid calling the default libICE handler which does an exit() */ static void iceInit (void) { static bool iceInitialized = false; if (!iceInitialized) { IceIOErrorHandler defaultIceHandler; oldIceHandler = IceSetIOErrorHandler (NULL); defaultIceHandler = IceSetIOErrorHandler (iceErrorHandler); if (oldIceHandler == defaultIceHandler) oldIceHandler = NULL; IceAddConnectionWatch (iceNewConnection, NULL); iceInitialized = true; } }