File indexing completed on 2024-04-14 14:23:51

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 Waldo Bastian <bastian@kde.org>
0004     SPDX-FileCopyrightText: 1999 Mario Weilguni <mweilguni@sime.com>
0005     SPDX-FileCopyrightText: 2001 Lubos Lunak <l.lunak@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-only
0008 */
0009 
0010 #include "klauncher_cmds.h"
0011 
0012 #include "config-kdeinit.h"
0013 
0014 #include <QStandardPaths>
0015 #include <QFile>
0016 
0017 #include <sys/types.h>
0018 #include <sys/param.h>
0019 #include <sys/socket.h>
0020 #include <sys/stat.h>
0021 #include <sys/un.h>
0022 
0023 #include <cerrno>
0024 #include <string.h>
0025 #include <stdio.h>
0026 #include <stdlib.h>
0027 #include <unistd.h>
0028 #include <pwd.h>
0029 #include <signal.h>
0030 
0031 #include <KDEInitInterface>
0032 
0033 extern char **environ;
0034 
0035 // copied from kdeinit/kinit.cpp
0036 // Can't use QGuiApplication::platformName() here, there is no app instance.
0037 #if HAVE_X11 || HAVE_XCB
0038 static const char* displayEnvVarName_c()
0039 {
0040     return "DISPLAY";
0041 }
0042 #endif
0043 
0044 // adapted from kdeinit/kinit.cpp
0045 // WARNING, if you change the socket name, adjust kinit.cpp too
0046 static const QString generate_socket_file_name()
0047 {
0048 
0049 #if HAVE_X11 || HAVE_XCB // qt5: see displayEnvVarName_c()
0050     QByteArray display = qgetenv(displayEnvVarName_c());
0051     if (display.isEmpty()) {
0052         fprintf(stderr, "Error: could not determine $%s.\n", displayEnvVarName_c());
0053         return QString();
0054     }
0055     int i;
0056     if ((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0) {
0057         display.truncate(i);
0058     }
0059 
0060     display.replace(':', '_');
0061 #ifdef __APPLE__
0062     // not entirely impossible, so let's leave it
0063     display.replace('/', '_');
0064 #endif
0065 #else
0066     // not using a DISPLAY variable; use an empty string instead
0067     QByteArray display = "";
0068 #endif
0069     // WARNING, if you change the socket name, adjust kwrapper too
0070     const QString socketFileName = QStringLiteral("kdeinit5_%1").arg(QLatin1String(display));
0071     return socketFileName;
0072 }
0073 
0074 /*
0075  * Write 'len' bytes from 'buffer' into 'sock'.
0076  * returns 0 on success, -1 on failure.
0077  */
0078 static int write_socket(int sock, char *buffer, int len)
0079 {
0080     ssize_t result;
0081     int bytes_left = len;
0082     while (bytes_left > 0) {
0083         result = write(sock, buffer, bytes_left);
0084         if (result > 0) {
0085             buffer += result;
0086             bytes_left -= result;
0087         } else if (result == 0) {
0088             return -1;
0089         } else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) {
0090             return -1;
0091         }
0092     }
0093     return 0;
0094 }
0095 
0096 /*
0097  * Read 'len' bytes from 'sock' into 'buffer'.
0098  * returns 0 on success, -1 on failure.
0099  */
0100 static int read_socket(int sock, char *buffer, int len)
0101 {
0102     ssize_t result;
0103     int bytes_left = len;
0104     while (bytes_left > 0) {
0105         result = read(sock, buffer, bytes_left);
0106         if (result > 0) {
0107             buffer += result;
0108             bytes_left -= result;
0109         } else if (result == 0) {
0110             return -1;
0111         } else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) {
0112             return -1;
0113         }
0114     }
0115     return 0;
0116 }
0117 
0118 static int openSocket()
0119 {
0120     const QString socketFileName = generate_socket_file_name();
0121     if (socketFileName.isEmpty()) {
0122         return -1;
0123     }
0124     QByteArray socketName = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) +
0125                             QLatin1Char('/') + socketFileName);
0126     const char *sock_file = socketName.constData();
0127 
0128     struct sockaddr_un server;
0129     if (strlen(sock_file) >= sizeof(server.sun_path)) {
0130         fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n");
0131         return -1;
0132     }
0133 
0134     /*
0135      * create the socket stream
0136      */
0137     int s = socket(PF_UNIX, SOCK_STREAM, 0);
0138     if (s < 0) {
0139         perror("Warning: socket() failed: ");
0140         return -1;
0141     }
0142 
0143     server.sun_family = AF_UNIX;
0144     strcpy(server.sun_path, sock_file);
0145     kde_socklen_t socklen = sizeof(server);
0146     if (connect(s, (struct sockaddr *)&server, socklen) == -1) {
0147         fprintf(stderr, "kdeinit5_wrapper: Warning: connect(%s) failed:", sock_file);
0148         perror(" ");
0149         close(s);
0150         return -1;
0151     }
0152     return s;
0153 }
0154 
0155 static pid_t kwrapper_pid;
0156 
0157 static void sig_pass_handler(int signo);
0158 static void setup_signals(void);
0159 
0160 static void setup_signal_handler(int signo, int clean)
0161 {
0162     struct sigaction sa;
0163     if (clean) {
0164         sa.sa_handler = SIG_DFL;
0165     } else {
0166         sa.sa_handler = sig_pass_handler;
0167     }
0168     sigemptyset(&sa.sa_mask);
0169     sigaddset(&sa.sa_mask, signo);
0170     sa.sa_flags = 0; /* don't use SA_RESTART */
0171     sigaction(signo, &sa, nullptr);
0172 }
0173 
0174 static void sig_pass_handler(int signo)
0175 {
0176     int save_errno = errno;
0177     if (signo == SIGTSTP) {
0178         kill(kwrapper_pid, SIGSTOP);    /* pass the signal to the real process */
0179     } else {                           /* SIGTSTP wouldn't work ... I don't think is much */
0180         kill(kwrapper_pid, signo);    /* of a problem */
0181     }
0182 
0183     if (signo == SIGCONT) {
0184         setup_signals();    /* restore signals */
0185     } else if (signo == SIGCHLD)
0186         ; /* nothing, ignore */
0187     else { /* do the default action ( most of them quit the app ) */
0188         setup_signal_handler(signo, 1);
0189         raise(signo);   /* handle the signal again */
0190     }
0191 
0192     errno = save_errno;
0193 }
0194 
0195 static void setup_signals()
0196 {
0197     setup_signal_handler(SIGHUP, 0);
0198     setup_signal_handler(SIGINT, 0);
0199     setup_signal_handler(SIGQUIT, 0);
0200     setup_signal_handler(SIGILL, 0);    /* e.g. this one is probably doesn't make sense to pass */
0201     setup_signal_handler(SIGABRT, 0);   /* but anyway ... */
0202     setup_signal_handler(SIGFPE, 0);
0203     /*   SIGKILL   can't be handled :( */
0204     setup_signal_handler(SIGSEGV, 0);
0205     setup_signal_handler(SIGPIPE, 0);
0206     setup_signal_handler(SIGALRM, 0);
0207     setup_signal_handler(SIGTERM, 0);
0208     setup_signal_handler(SIGUSR1, 0);
0209     setup_signal_handler(SIGUSR2, 0);
0210     setup_signal_handler(SIGCHLD, 0);   /* is this a good idea ??? */
0211     setup_signal_handler(SIGCONT, 0);   /* SIGSTOP can't be handled, but SIGTSTP and SIGCONT can */
0212     /* SIGSTOP */                       /* which should be enough */
0213     setup_signal_handler(SIGTSTP, 0);
0214     setup_signal_handler(SIGTTIN, 0);   /* is this a good idea ??? */
0215     setup_signal_handler(SIGTTOU, 0);   /* is this a good idea ??? */
0216     /* some more ? */
0217 }
0218 
0219 static int kwrapper_run(pid_t wrapped, int sock)
0220 {
0221     klauncher_header header;
0222     char *buffer;
0223     long pid, status;
0224 
0225     kwrapper_pid = wrapped;
0226     setup_signals();
0227 
0228     read_socket(sock, (char *)&header, sizeof(header));
0229 
0230     if (header.cmd != LAUNCHER_CHILD_DIED) {
0231         fprintf(stderr, "Unexpected response from KInit (response = %ld).\n", header.cmd);
0232         exit(255);
0233     }
0234 
0235     buffer = (char *) malloc(header.arg_length);
0236     if (buffer == nullptr) {
0237         perror("Error: malloc() failed\n");
0238         exit(255);
0239     }
0240 
0241     read_socket(sock, buffer, header.arg_length);
0242     pid = ((long *) buffer)[0];
0243     if (pid !=  kwrapper_pid) {
0244         fprintf(stderr, "Unexpected LAUNCHER_CHILD_DIED from KInit - pid = %ld\n", pid);
0245         exit(255);
0246     }
0247 
0248     status = ((long *) buffer)[1];
0249     free(buffer);
0250     return (int) status;
0251 }
0252 
0253 int main(int argc, char **argv)
0254 {
0255     int i;
0256     int wrapper = 0;
0257     int ext_wrapper = 0;
0258     int kwrapper = 0;
0259     long arg_count;
0260     long env_count;
0261     klauncher_header header;
0262     char *start, *p, *buffer;
0263     char cwd[8192];
0264     const char *tty = nullptr;
0265     long avoid_loops = 0;
0266     const char *startup_id = nullptr;
0267     int sock;
0268 
0269     long size = 0;
0270 
0271     start = argv[0];
0272     p = start + strlen(argv[0]);
0273     while (--p > start) {
0274         if (*p == '/') {
0275             break;
0276         }
0277     }
0278     if (p > start) {
0279         p++;
0280     }
0281     start = p;
0282 
0283     /*
0284      * Place it here before the call to ensureKdeinitRunning(), because we shouldn't be trying to
0285      * start kdeinit during the process of shutdown.
0286      */
0287     if (strcmp(start, "kdeinit5_shutdown") == 0) {
0288         if (argc > 1) {
0289             fprintf(stderr, "Usage: %s\n\n", start);
0290             fprintf(stderr, "Shuts down kdeinit5 master process and terminates all processes spawned from it.\n");
0291             exit(255);
0292         }
0293         sock = openSocket();
0294         if (sock < 0) {
0295             fprintf(stderr, "Error: Can not contact kdeinit5!\n");
0296             exit(255);
0297         }
0298         header.cmd = LAUNCHER_TERMINATE_KDE;
0299         header.arg_length = 0;
0300         write_socket(sock, (char *) &header, sizeof(header));
0301         read_socket(sock, (char *) &header, 1); /* wait for the socket to close */
0302         return 0;
0303     }
0304 
0305 #ifdef QT_DBUS_LIB
0306     KDEInitInterface::ensureKdeinitRunning();
0307 #endif
0308 
0309     if (strcmp(start, "kdeinit5_wrapper") == 0) {
0310         wrapper = 1;
0311     } else if (strcmp(start, "kshell5") == 0) {
0312         ext_wrapper = 1;
0313     } else if (strcmp(start, "kwrapper5") == 0) {
0314         kwrapper = 1;
0315     }
0316 
0317     if (wrapper || ext_wrapper || kwrapper) {
0318         argv++;
0319         argc--;
0320         if (argc < 1) {
0321             fprintf(stderr, "Usage: %s <application> [<args>]\n", start);
0322             exit(255); /* usage should be documented somewhere ... */
0323         }
0324         start = argv[0];
0325     }
0326 
0327     sock = openSocket();
0328     if (sock < 0) { /* couldn't contact kdeinit5, start argv[ 0 ] directly */
0329         execvp(argv[ 0 ], argv);
0330         fprintf(stderr, "Error: Can not run %s !\n", argv[ 0 ]);
0331         exit(255);
0332     }
0333 
0334     if (!wrapper && !ext_wrapper && !kwrapper) {
0335         /* was called as a symlink */
0336         avoid_loops = 1;
0337 #if defined(WE_ARE_KWRAPPER)
0338         kwrapper = 1;
0339 #elif defined(WE_ARE_KSHELL)
0340         ext_wrapper = 1;
0341 #else
0342         wrapper = 1;
0343 #endif
0344     }
0345 
0346     arg_count = argc;
0347     env_count = 0;
0348 
0349     size += sizeof(long); /* Number of arguments*/
0350 
0351     size += strlen(start) + 1; /* Size of first argument. */
0352 
0353     for (i = 1; i < argc; i++) {
0354         size += strlen(argv[i]) + 1;
0355     }
0356     if (wrapper) {
0357         size += sizeof(long); /* empty envs */
0358     }
0359     if (ext_wrapper || kwrapper) {
0360         if (!getcwd(cwd, 8192)) {
0361             cwd[0] = '\0';
0362         }
0363         size += strlen(cwd) + 1;
0364 
0365         size += sizeof(long); /* Number of env.vars. */
0366 
0367         for (; environ[env_count]; env_count++) {
0368             int l = strlen(environ[env_count]) + 1;
0369             size += l;
0370         }
0371 
0372         if (kwrapper) {
0373             tty = ttyname(1);
0374             if (!tty || !isatty(2)) {
0375                 tty = "";
0376             }
0377             size += strlen(tty) + 1;
0378         }
0379     }
0380 
0381     size += sizeof(avoid_loops);
0382 
0383     if (!wrapper) {
0384         startup_id = getenv("DESKTOP_STARTUP_ID");
0385         if (startup_id == nullptr) {
0386             startup_id = "";
0387         }
0388         size += strlen(startup_id) + 1;
0389     }
0390 
0391     if (wrapper) {
0392         header.cmd = LAUNCHER_EXEC_NEW;
0393     } else if (kwrapper) {
0394         header.cmd = LAUNCHER_KWRAPPER;
0395     } else {
0396         header.cmd = LAUNCHER_SHELL;
0397     }
0398     header.arg_length = size;
0399     write_socket(sock, (char *) &header, sizeof(header));
0400 
0401     buffer = (char *) malloc(size);
0402     if (buffer == nullptr) {
0403         perror("Error: malloc() failed.");
0404         exit(255);
0405     }
0406     p = buffer;
0407 
0408     memcpy(p, &arg_count, sizeof(arg_count));
0409     p += sizeof(arg_count);
0410 
0411     memcpy(p, start, strlen(start) + 1);
0412     p += strlen(start) + 1;
0413 
0414     for (i = 1; i < argc; i++) {
0415         memcpy(p, argv[i], strlen(argv[i]) + 1);
0416         p += strlen(argv[i]) + 1;
0417     }
0418 
0419     if (wrapper) {
0420         long dummy = 0;
0421         memcpy(p, &dummy, sizeof(dummy)); /* empty envc */
0422         p += sizeof(dummy);
0423     }
0424     if (ext_wrapper || kwrapper) {
0425         memcpy(p, cwd, strlen(cwd) + 1);
0426         p += strlen(cwd) + 1;
0427 
0428         memcpy(p, &env_count, sizeof(env_count));
0429         p += sizeof(env_count);
0430 
0431         for (i = 0; i < env_count; i++) {
0432             int l = strlen(environ[i]) + 1;
0433             memcpy(p, environ[i], l);
0434             p += l;
0435         }
0436 
0437         if (kwrapper) {
0438             memcpy(p, tty, strlen(tty) + 1);
0439             p += strlen(tty) + 1;
0440         }
0441     }
0442 
0443     memcpy(p, &avoid_loops, sizeof(avoid_loops));
0444     p += sizeof(avoid_loops);
0445 
0446     if (!wrapper) {
0447         memcpy(p, startup_id, strlen(startup_id) + 1);
0448         p += strlen(startup_id) + 1;
0449     }
0450 
0451     if (p - buffer != size)  /* should fail only if you change this source and do */
0452         /* a stupid mistake, it should be assert() actually */
0453     {
0454         fprintf(stderr, "Oops. Invalid format.\n");
0455         exit(255);
0456     }
0457 
0458     write_socket(sock, buffer, size);
0459     free(buffer);
0460 
0461     if (read_socket(sock, (char *) &header, sizeof(header)) == -1) {
0462         fprintf(stderr, "Communication error with KInit.\n");
0463         exit(255);
0464     }
0465 
0466     if (header.cmd == LAUNCHER_OK) {
0467         long pid;
0468         buffer = (char *) malloc(header.arg_length);
0469         if (buffer == nullptr) {
0470             perror("Error: malloc() failed\n");
0471             exit(255);
0472         }
0473         read_socket(sock, buffer, header.arg_length);
0474         pid = *((long *) buffer);
0475         if (!kwrapper) { /* kwrapper shouldn't print any output */
0476             printf("Launched ok, pid = %ld\n", pid);
0477         } else {
0478             exit(kwrapper_run(pid, sock));
0479         }
0480     } else if (header.cmd == LAUNCHER_ERROR) {
0481         fprintf(stderr, "KInit could not launch '%s'.\n", start);
0482         exit(255);
0483     } else {
0484         fprintf(stderr, "Unexpected response from KInit (response = %ld).\n", header.cmd);
0485         exit(255);
0486     }
0487     exit(0);
0488 }