File indexing completed on 2024-04-21 16:21:52
0001 /* 0002 ksmserver - the KDE session management server 0003 0004 SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org> 0005 0006 SPDX-License-Identifier: MIT 0007 */ 0008 0009 #include <config-ksmserver.h> 0010 #include <config-workspace.h> 0011 #include <errno.h> 0012 #include <fcntl.h> 0013 #include <fixx11h.h> 0014 #include <stdlib.h> 0015 #include <string.h> 0016 #include <unistd.h> 0017 0018 #include <KMessageBox> 0019 0020 #include "server.h" 0021 #include <KLocalizedString> 0022 #include <KRuntimePlatform> 0023 #include <KSharedConfig> 0024 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0025 #include <private/qtx11extras_p.h> 0026 #else 0027 #include <QX11Info> 0028 #endif 0029 #include <kconfig.h> 0030 #include <kconfiggroup.h> 0031 #include <kdbusservice.h> 0032 #include <kmanagerselection.h> 0033 #include <ksmserver_debug.h> 0034 #include <kwindowsystem.h> 0035 0036 #include <QApplication> 0037 #include <QCommandLineParser> 0038 #include <QFile> 0039 #include <QQuickWindow> 0040 #include <X11/extensions/Xrender.h> 0041 0042 static const char version[] = "0.4"; 0043 0044 Display *dpy = nullptr; 0045 Colormap colormap = 0; 0046 Visual *visual = nullptr; 0047 0048 extern KSMServer *the_server; 0049 0050 void IoErrorHandler(IceConn iceConn) 0051 { 0052 the_server->ioError(iceConn); 0053 } 0054 0055 bool writeTest(QByteArray path) 0056 { 0057 path += "/XXXXXX"; 0058 int fd = mkstemp(path.data()); 0059 if (fd == -1) 0060 return false; 0061 if (write(fd, "Hello World\n", 12) == -1) { 0062 int save_errno = errno; 0063 close(fd); 0064 unlink(path.data()); 0065 errno = save_errno; 0066 return false; 0067 } 0068 close(fd); 0069 unlink(path.data()); 0070 return true; 0071 } 0072 0073 void checkComposite() 0074 { 0075 if (qgetenv("KDE_SKIP_ARGB_VISUALS") == "1") 0076 return; 0077 // thanks to zack rusin and frederik for pointing me in the right direction 0078 // for the following bits of X11 code 0079 dpy = XOpenDisplay(nullptr); // open default display 0080 if (!dpy) { 0081 qCCritical(KSMSERVER) << "Cannot connect to the X server"; 0082 return; 0083 } 0084 0085 int screen = DefaultScreen(dpy); 0086 int eventBase, errorBase; 0087 0088 if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) { 0089 int nvi; 0090 XVisualInfo templ; 0091 templ.screen = screen; 0092 templ.depth = 32; 0093 templ.c_class = TrueColor; 0094 XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, &nvi); 0095 for (int i = 0; i < nvi; ++i) { 0096 XRenderPictFormat *format = XRenderFindVisualFormat(dpy, xvi[i].visual); 0097 if (format->type == PictTypeDirect && format->direct.alphaMask) { 0098 visual = xvi[i].visual; 0099 colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, AllocNone); 0100 0101 XFree(xvi); 0102 return; 0103 } 0104 } 0105 0106 XFree(xvi); 0107 } 0108 XCloseDisplay(dpy); 0109 dpy = nullptr; 0110 } 0111 0112 void sanity_check(int argc, char *argv[]) 0113 { 0114 QString msg; 0115 QByteArray path = qgetenv("HOME"); 0116 const QByteArray readOnly = qgetenv("KDE_HOME_READONLY"); 0117 if (path.isEmpty()) { 0118 msg = i18n("$HOME not set!"); 0119 } 0120 if (msg.isEmpty() && access(path.data(), W_OK)) { 0121 if (errno == ENOENT) 0122 msg = i18n("$HOME directory (%1) does not exist.", QFile::decodeName(path)); 0123 else if (readOnly.isEmpty()) 0124 msg = i18n("No write access to $HOME directory (%1).", QFile::decodeName(path)); 0125 } 0126 if (msg.isEmpty() && access(path.data(), R_OK)) { 0127 if (errno == ENOENT) 0128 msg = i18n("$HOME directory (%1) does not exist.", QFile::decodeName(path)); 0129 else 0130 msg = i18n("No read access to $HOME directory (%1).", QFile::decodeName(path)); 0131 } 0132 if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path)) { 0133 if (errno == ENOSPC) 0134 msg = i18n("$HOME directory (%1) is out of disk space.", QFile::decodeName(path)); 0135 else 0136 msg = i18n( 0137 "Writing to the $HOME directory (%2) failed with " 0138 "the error '%1'", 0139 QString::fromLocal8Bit(strerror(errno)), 0140 QFile::decodeName(path)); 0141 } 0142 if (msg.isEmpty()) { 0143 path = getenv("ICEAUTHORITY"); 0144 if (path.isEmpty()) { 0145 path = qgetenv("HOME"); 0146 path += "/.ICEauthority"; 0147 } 0148 0149 if (access(path.data(), W_OK) && (errno != ENOENT)) 0150 msg = i18n("No write access to '%1'.", QFile::decodeName(path)); 0151 else if (access(path.data(), R_OK) && (errno != ENOENT)) 0152 msg = i18n("No read access to '%1'.", QFile::decodeName(path)); 0153 } 0154 if (msg.isEmpty()) { 0155 path = getenv("KDETMP"); 0156 if (path.isEmpty()) 0157 path = "/tmp"; 0158 if (!writeTest(path)) { 0159 if (errno == ENOSPC) 0160 msg = i18n("Temp directory (%1) is out of disk space.", QFile::decodeName(path)); 0161 else 0162 msg = i18n( 0163 "Writing to the temp directory (%2) failed with\n " 0164 "the error '%1'", 0165 QString::fromLocal8Bit(strerror(errno)), 0166 QFile::decodeName(path)); 0167 } 0168 } 0169 if (msg.isEmpty() && (path != "/tmp")) { 0170 path = "/tmp"; 0171 if (!writeTest(path)) { 0172 if (errno == ENOSPC) 0173 msg = i18n("Temp directory (%1) is out of disk space.", QFile::decodeName(path)); 0174 else 0175 msg = i18n( 0176 "Writing to the temp directory (%2) failed with\n " 0177 "the error '%1'", 0178 QString::fromLocal8Bit(strerror(errno)), 0179 QFile::decodeName(path)); 0180 } 0181 } 0182 if (msg.isEmpty()) { 0183 path += "/.ICE-unix"; 0184 if (access(path.data(), W_OK) && (errno != ENOENT)) 0185 msg = i18n("No write access to '%1'.", QFile::decodeName(path)); 0186 else if (access(path.data(), R_OK) && (errno != ENOENT)) 0187 msg = i18n("No read access to '%1'.", QFile::decodeName(path)); 0188 } 0189 if (!msg.isEmpty()) { 0190 const QString msg_pre = i18n( 0191 "The following installation problem was detected\n" 0192 "while trying to start Plasma:") 0193 + QStringLiteral("\n\n "); 0194 const QString msg_post = i18n("\n\nPlasma is unable to start.\n"); 0195 fputs(msg_pre.toUtf8().constData(), stderr); 0196 fprintf(stderr, "%s", msg.toUtf8().constData()); 0197 fputs(msg_post.toUtf8().constData(), stderr); 0198 0199 QApplication a(argc, argv); 0200 const QString qmsg = msg_pre + msg + msg_post; 0201 KMessageBox::error(nullptr, qmsg, i18n("Plasma Workspace installation problem!")); 0202 exit(255); 0203 } 0204 } 0205 0206 int main(int argc, char *argv[]) 0207 { 0208 sanity_check(argc, argv); 0209 0210 putenv((char *)"SESSION_MANAGER="); 0211 checkComposite(); 0212 0213 // force xcb QPA plugin as ksmserver is very X11 specific 0214 const QByteArray origQpaPlatform = qgetenv("QT_QPA_PLATFORM"); 0215 qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("xcb")); 0216 0217 QQuickWindow::setDefaultAlphaBuffer(true); 0218 QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); 0219 QApplication *a = new QApplication(argc, argv); 0220 0221 // now the QPA platform is set, unset variable again to not launch apps with incorrect environment 0222 if (origQpaPlatform.isEmpty()) { 0223 qunsetenv("QT_QPA_PLATFORM"); 0224 } else { 0225 qputenv("QT_QPA_PLATFORM", origQpaPlatform); 0226 } 0227 0228 QApplication::setApplicationName(QStringLiteral("ksmserver")); 0229 QApplication::setApplicationVersion(QString::fromLatin1(version)); 0230 QApplication::setOrganizationDomain(QStringLiteral("kde.org")); 0231 0232 fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, 1); 0233 0234 a->setQuitOnLastWindowClosed(false); // #169486 0235 0236 QCommandLineParser parser; 0237 parser.setApplicationDescription(i18n("The reliable Plasma session manager that talks the standard X11R6 \nsession management protocol (XSMP).")); 0238 parser.addHelpOption(); 0239 parser.addVersionOption(); 0240 0241 QCommandLineOption restoreOption(QStringList() << QStringLiteral("r") << QStringLiteral("restore"), i18n("Restores the saved user session if available")); 0242 parser.addOption(restoreOption); 0243 0244 QCommandLineOption nolocalOption(QStringLiteral("nolocal"), i18n("Also allow remote connections")); 0245 parser.addOption(nolocalOption); 0246 0247 QCommandLineOption lockscreenOption(QStringLiteral("lockscreen"), i18n("Starts the session in locked mode")); 0248 parser.addOption(lockscreenOption); 0249 0250 QCommandLineOption noLockscreenOption(QStringLiteral("no-lockscreen"), 0251 i18n("Starts without lock screen support. Only needed if other component provides the lock screen.")); 0252 parser.addOption(noLockscreenOption); 0253 0254 parser.process(*a); 0255 0256 bool only_local = !parser.isSet(nolocalOption); 0257 #ifndef HAVE__ICETRANSNOLISTEN 0258 /* this seems strange, but the default is only_local, so if !only_local 0259 * the option --nolocal was given, and we warn (the option --nolocal 0260 * does nothing on this platform, as here the default is reversed) 0261 */ 0262 if (!only_local) { 0263 qCWarning(KSMSERVER, "--nolocal is not supported on your platform. Sorry."); 0264 } 0265 only_local = false; 0266 #endif 0267 0268 KSMServer::InitFlags flags = KSMServer::InitFlag::None; 0269 if (only_local) { 0270 flags |= KSMServer::InitFlag::OnlyLocal; 0271 } 0272 if (parser.isSet(lockscreenOption)) { 0273 flags |= KSMServer::InitFlag::ImmediateLockScreen; 0274 } 0275 if (parser.isSet(noLockscreenOption)) { 0276 flags |= KSMServer::InitFlag::NoLockScreen; 0277 } 0278 0279 // we use the session_type here as ksmserver is already forced as X above 0280 // in wayland, kwin manages the lock screen 0281 if (qgetenv("XDG_SESSION_TYPE") == QByteArrayLiteral("wayland")) { 0282 flags |= KSMServer::InitFlag::NoLockScreen; 0283 } 0284 0285 KSMServer *server = new KSMServer(flags); 0286 0287 // for the KDE-already-running check in startkde 0288 KSelectionOwner kde_running("_KDE_RUNNING", 0); 0289 kde_running.claim(false); 0290 0291 IceSetIOErrorHandler(IoErrorHandler); 0292 0293 KConfigGroup config(KSharedConfig::openConfig(), "General"); 0294 0295 QString loginMode = config.readEntry("loginMode", "restorePreviousLogout"); 0296 0297 // we don't need session restoring in Plasma Mobile 0298 if (KRuntimePlatform::runtimePlatform().contains(QStringLiteral("phone"))) { 0299 loginMode = QStringLiteral("emptySession"); 0300 } 0301 0302 if (parser.isSet(restoreOption)) 0303 server->setRestoreSession(QStringLiteral(SESSION_BY_USER)); 0304 else if (loginMode == QLatin1String("restorePreviousLogout")) 0305 server->setRestoreSession(QStringLiteral(SESSION_PREVIOUS_LOGOUT)); 0306 else if (loginMode == QLatin1String("restoreSavedSession")) 0307 server->setRestoreSession(QStringLiteral(SESSION_BY_USER)); 0308 else 0309 server->startDefaultSession(); 0310 0311 KDBusService service(KDBusService::Unique); 0312 0313 server->setupShortcuts(); 0314 int ret = a->exec(); 0315 kde_running.release(); // needs to be done before QApplication destruction 0316 delete a; 0317 return ret; 0318 }