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 }