1/*****************************************************************
2ksmserver - the KDE session management server
3
4Copyright 2000 Matthias Ettrich <ettrich@kde.org>
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23******************************************************************/
24#include <fixx11h.h>
25#include <config-workspace.h>
26#include <config-ksmserver.h>
27#include <unistd.h>
28#include <stdlib.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <string.h>
32
33#include <KMessageBox>
34#include <QtDBus/QtDBus>
35
36#include <kapplication.h>
37#include <kcmdlineargs.h>
38#include <kconfiggroup.h>
39#include <kaboutdata.h>
40#include <kdebug.h>
41#include <klocale.h>
42#include <kglobal.h>
43#include <kconfig.h>
44#include <kmanagerselection.h>
45#include <kwindowsystem.h>
46#include "server.h"
47#include <QX11Info>
48
49#include <X11/extensions/Xrender.h>
50
51static const char version[] = "0.4";
52static const char description[] = I18N_NOOP( "The reliable KDE session manager that talks the standard X11R6 \nsession management protocol (XSMP)." );
53
54Display* dpy = 0;
55Colormap colormap = 0;
56Visual *visual = 0;
57
58extern KSMServer* the_server;
59
60void IoErrorHandler ( IceConn iceConn)
61{
62 the_server->ioError( iceConn );
63}
64
65bool writeTest(QByteArray path)
66{
67 path += "/XXXXXX";
68 int fd = mkstemp(path.data());
69 if (fd == -1)
70 return false;
71 if (write(fd, "Hello World\n", 12) == -1)
72 {
73 int save_errno = errno;
74 close(fd);
75 unlink(path.data());
76 errno = save_errno;
77 return false;
78 }
79 close(fd);
80 unlink(path.data());
81 return true;
82}
83
84void checkComposite()
85{
86 if( qgetenv( "KDE_SKIP_ARGB_VISUALS" ) == "1" )
87 return;
88 // thanks to zack rusin and frederik for pointing me in the right direction
89 // for the following bits of X11 code
90 dpy = XOpenDisplay(0); // open default display
91 if (!dpy)
92 {
93 kError() << "Cannot connect to the X server";
94 return;
95 }
96
97 int screen = DefaultScreen(dpy);
98 int eventBase, errorBase;
99
100 if (XRenderQueryExtension(dpy, &eventBase, &errorBase))
101 {
102 int nvi;
103 XVisualInfo templ;
104 templ.screen = screen;
105 templ.depth = 32;
106 templ.c_class = TrueColor;
107 XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask |
108 VisualDepthMask |
109 VisualClassMask,
110 &templ, &nvi);
111 for (int i = 0; i < nvi; ++i)
112 {
113 XRenderPictFormat *format = XRenderFindVisualFormat(dpy,
114 xvi[i].visual);
115 if (format->type == PictTypeDirect && format->direct.alphaMask)
116 {
117 visual = xvi[i].visual;
118 colormap = XCreateColormap(dpy, RootWindow(dpy, screen),
119 visual, AllocNone);
120
121 XFree(xvi);
122 return;
123 }
124 }
125
126 XFree(xvi);
127
128 }
129 XCloseDisplay( dpy );
130 dpy = NULL;
131}
132
133void sanity_check( int argc, char* argv[], KAboutData* aboutDataPtr )
134{
135 QString msg;
136 QByteArray path = getenv("HOME");
137 QByteArray readOnly = getenv("KDE_HOME_READONLY");
138 if (path.isEmpty())
139 {
140 msg = QLatin1String("$HOME not set!");
141 }
142 if (msg.isEmpty() && access(path.data(), W_OK))
143 {
144 if (errno == ENOENT)
145 msg = QLatin1String("$HOME directory (%1) does not exist.");
146 else if (readOnly.isEmpty())
147 msg = QLatin1String("No write access to $HOME directory (%1).");
148 }
149 if (msg.isEmpty() && access(path.data(), R_OK))
150 {
151 if (errno == ENOENT)
152 msg = "$HOME directory (%1) does not exist.";
153 else
154 msg = "No read access to $HOME directory (%1).";
155 }
156 if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path))
157 {
158 if (errno == ENOSPC)
159 msg = "$HOME directory (%1) is out of disk space.";
160 else
161 msg = QByteArray("Writing to the $HOME directory (%1) failed with\n "
162 "the error '")+QByteArray(strerror(errno))+QByteArray("'");
163 }
164 if (msg.isEmpty())
165 {
166 path = getenv("ICEAUTHORITY");
167 if (path.isEmpty())
168 {
169 path = getenv("HOME");
170 path += "/.ICEauthority";
171 }
172
173 if (access(path.data(), W_OK) && (errno != ENOENT))
174 msg = "No write access to '%1'.";
175 else if (access(path.data(), R_OK) && (errno != ENOENT))
176 msg = "No read access to '%1'.";
177 }
178 if (msg.isEmpty())
179 {
180 path = getenv("KDETMP");
181 if (path.isEmpty())
182 path = "/tmp";
183 if (!writeTest(path))
184 {
185 if (errno == ENOSPC)
186 msg = "Temp directory (%1) is out of disk space.";
187 else
188 msg = "Writing to the temp directory (%1) failed with\n "
189 "the error '"+QByteArray(strerror(errno))+QByteArray("'");
190 }
191 }
192 if (msg.isEmpty() && (path != "/tmp"))
193 {
194 path = "/tmp";
195 if (!writeTest(path))
196 {
197 if (errno == ENOSPC)
198 msg = "Temp directory (%1) is out of disk space.";
199 else
200 msg = "Writing to the temp directory (%1) failed with\n "
201 "the error '"+QByteArray(strerror(errno))+QByteArray("'");
202 }
203 }
204 if (msg.isEmpty())
205 {
206 path += "/.ICE-unix";
207 if (access(path.data(), W_OK) && (errno != ENOENT))
208 msg = "No write access to '%1'.";
209 else if (access(path.data(), R_OK) && (errno != ENOENT))
210 msg = "No read access to '%1'.";
211 }
212 if (!msg.isEmpty())
213 {
214 const char *msg_pre =
215 "The following installation problem was detected\n"
216 "while trying to start KDE:"
217 "\n\n ";
218 const char *msg_post = "\n\nKDE is unable to start.\n";
219 fputs(msg_pre, stderr);
220 fprintf(stderr, "%s", qPrintable(msg.arg(QFile::decodeName(path))));
221 fputs(msg_post, stderr);
222
223 QApplication a(argc, argv);
224 KComponentData i(aboutDataPtr);
225 QString qmsg = msg_pre+msg.arg(QFile::decodeName(path))+msg_post;
226 KMessageBox::error(0, qmsg, "KDE Installation Problem!");
227 exit(255);
228 }
229}
230
231extern "C" KDE_EXPORT int kdemain( int argc, char* argv[] )
232{
233 KAboutData aboutData( "ksmserver", 0, ki18n("The KDE Session Manager"),
234 version, ki18n(description), KAboutData::License_BSD,
235 ki18n("(C) 2000, The KDE Developers"));
236 aboutData.addAuthor(ki18n("Matthias Ettrich"),KLocalizedString(), "ettrich@kde.org");
237 aboutData.addAuthor(ki18n("Luboš Luňák"), ki18n( "Maintainer" ), "l.lunak@kde.org" );
238
239 sanity_check(argc, argv, &aboutData);
240
241 KCmdLineArgs::init(argc, argv, &aboutData);
242
243 KCmdLineOptions options;
244 options.add("r");
245 options.add("restore", ki18n("Restores the saved user session if available"));
246 options.add("w");
247 options.add("windowmanager <wm>", ki18n("Starts 'wm' in case no other window manager is \nparticipating in the session. Default is 'kwin'"));
248 options.add("nolocal", ki18n("Also allow remote connections"));
249#if COMPILE_SCREEN_LOCKER
250 options.add("lockscreen", ki18n("Starts the session in locked mode"));
251#endif
252 KCmdLineArgs::addCmdLineOptions( options );
253
254 putenv((char*)"SESSION_MANAGER=");
255 checkComposite();
256 KApplication *a;
257
258 if( dpy != NULL && DefaultDepth(dpy, DefaultScreen(dpy)) >= 24) // 16bpp breaks the software logout effect for some reason???
259 a = new KApplication(dpy, visual ? Qt::HANDLE(visual) : 0, colormap ? Qt::HANDLE(colormap) : 0);
260 else
261 a = new KApplication(true);
262 fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, 1);
263
264 a->setQuitOnLastWindowClosed(false); // #169486
265
266 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
267
268 if( !QDBusConnection::sessionBus().interface()->registerService( "org.kde.ksmserver", QDBusConnectionInterface::DontQueueService ) )
269 {
270 qWarning("Could not register with D-BUS. Aborting.");
271 return 1;
272 }
273
274 QString wm = args->getOption("windowmanager");
275
276 bool only_local = args->isSet("local");
277#ifndef HAVE__ICETRANSNOLISTEN
278 /* this seems strange, but the default is only_local, so if !only_local
279 * the option --nolocal was given, and we warn (the option --nolocal
280 * does nothing on this platform, as here the default is reversed)
281 */
282 if (!only_local) {
283 qWarning("--[no]local is not supported on your platform. Sorry.");
284 }
285 only_local = false;
286#endif
287
288#if COMPILE_SCREEN_LOCKER
289 KSMServer *server = new KSMServer( wm, only_local, args->isSet("lockscreen") );
290#else
291 KSMServer *server = new KSMServer( wm, only_local );
292#endif
293
294 // for the KDE-already-running check in startkde
295 KSelectionOwner kde_running( "_KDE_RUNNING", 0 );
296 kde_running.claim( false );
297
298 IceSetIOErrorHandler( IoErrorHandler );
299
300 KConfigGroup config(KGlobal::config(), "General");
301
302 int realScreenCount = ScreenCount( QX11Info::display() );
303 bool screenCountChanged =
304 ( config.readEntry( "screenCount", realScreenCount ) != realScreenCount );
305
306 QString loginMode = config.readEntry( "loginMode", "restorePreviousLogout" );
307
308 if ( args->isSet("restore") && ! screenCountChanged )
309 server->restoreSession( SESSION_BY_USER );
310 else if ( loginMode == "default" || screenCountChanged )
311 server->startDefaultSession();
312 else if ( loginMode == "restorePreviousLogout" )
313 server->restoreSession( SESSION_PREVIOUS_LOGOUT );
314 else if ( loginMode == "restoreSavedSession" )
315 server->restoreSession( SESSION_BY_USER );
316 else
317 server->startDefaultSession();
318 int ret = a->exec();
319 kde_running.release(); // needs to be done before QApplication destruction
320 delete a;
321 return ret;
322}
323