1/*
2 * This file is part of the KDE libraries
3 * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org>
4 * (c) 1999 Mario Weilguni <mweilguni@sime.com>
5 * (c) 2001 Lubos Lunak <l.lunak@kde.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License version 2 as published by the Free Software Foundation.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#define QT_NO_CAST_FROM_ASCII
23
24#include <config.h>
25#include <config-kdeinit.h>
26
27#include <sys/types.h>
28#include <sys/time.h>
29#include <sys/resource.h>
30#include <sys/stat.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <sys/wait.h>
34#ifdef HAVE_SYS_SELECT_H
35#include <sys/select.h> // Needed on some systems.
36#endif
37
38#include <ctype.h>
39#include <errno.h>
40#include <fcntl.h>
41#include "proctitle.h"
42#include <signal.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <locale.h>
48
49#include <QtCore/QLibrary>
50#include <QtCore/QString>
51#include <QtCore/QFile>
52#include <QtCore/QDate>
53#include <QtCore/QFileInfo>
54#include <QtCore/QRegExp>
55#include <QtGui/QFont>
56#include <kcomponentdata.h>
57#include <klibrary.h>
58#include <kdemacros.h>
59#include <kstandarddirs.h>
60#include <kglobal.h>
61#include <kconfig.h>
62#include <kapplication.h>
63#include <klocale.h>
64#include <kdebug.h>
65#include <kde_file.h>
66#include <ksavefile.h>
67
68#ifdef Q_OS_LINUX
69#include <sys/prctl.h>
70#ifndef PR_SET_NAME
71#define PR_SET_NAME 15
72#endif
73#endif
74
75#ifdef Q_WS_MACX
76#include <kkernel_mac.h>
77#endif
78
79#include <kdeversion.h>
80
81#include "klauncher_cmds.h"
82
83#ifdef Q_WS_X11
84#include <X11/Xlib.h>
85#include <X11/Xatom.h>
86#include <fixx11h.h>
87#include <kstartupinfo.h>
88#endif
89
90#ifdef Q_WS_X11
91static const char *extra_libs[] = {
92 "libkio.so.5",
93 "libkparts.so.4",
94#ifdef __KDE_HAVE_GCC_VISIBILITY
95 "libplasma.so.3"
96#endif
97};
98#endif
99
100// #define SKIP_PROCTITLE 1
101
102extern char **environ;
103
104#ifdef Q_WS_X11
105static int X11fd = -1;
106static Display *X11display = 0;
107static int X11_startup_notify_fd = -1;
108static Display *X11_startup_notify_display = 0;
109#endif
110static KComponentData *s_instance = 0;
111#define MAX_SOCK_FILE 255
112static char sock_file[MAX_SOCK_FILE];
113
114#ifdef Q_WS_X11
115#define DISPLAY "DISPLAY"
116#elif defined(Q_WS_QWS)
117#define DISPLAY "QWS_DISPLAY"
118#elif defined(Q_WS_MACX)
119#define DISPLAY "MAC_DISPLAY"
120#elif defined(Q_WS_WIN)
121#define DISPLAY "WIN_DISPLAY"
122#else
123#error Use QT/X11 or QT/Embedded
124#endif
125
126/* Group data */
127static struct {
128 int maxname;
129 int fd[2];
130 int launcher[2]; /* socket pair for launcher communication */
131 int deadpipe[2]; /* pipe used to detect dead children */
132 int initpipe[2];
133 int wrapper; /* socket for wrapper communication */
134 int accepted_fd; /* socket accepted and that must be closed in the child process */
135 char result;
136 int exit_status;
137 pid_t fork;
138 pid_t launcher_pid;
139 pid_t kded_pid;
140 int n;
141 char **argv;
142 int (*func)(int, char *[]);
143 int (*launcher_func)(int);
144 bool debug_wait;
145 QByteArray errorMsg;
146 bool launcher_ok;
147 bool suicide;
148} d;
149
150struct child
151{
152 pid_t pid;
153 int sock; /* fd to write message when child is dead*/
154 struct child *next;
155};
156
157static struct child *children;
158
159#ifdef Q_WS_X11
160extern "C" {
161int kdeinit_xio_errhandler( Display * );
162int kdeinit_x_errhandler( Display *, XErrorEvent *err );
163}
164#endif
165
166#ifdef KDEINIT_OOM_PROTECT
167static int oom_pipe = -1;
168#endif
169
170/*
171 * Clean up the file descriptor table by closing all file descriptors
172 * that are still open.
173 *
174 * This function is called very early in the main() function, so that
175 * we don't leak anything that was leaked to us.
176 */
177static void cleanup_fds()
178{
179 int maxfd = FD_SETSIZE;
180 struct rlimit rl;
181 if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
182 maxfd = rl.rlim_max;
183 for (int fd = 3; fd < maxfd; ++fd)
184 {
185#ifdef KDEINIT_OOM_PROTECT
186 if( fd != oom_pipe )
187#endif
188 close(fd);
189 }
190}
191
192/*
193 * Close fd's which are only useful for the parent process.
194 * Restore default signal handlers.
195 */
196static void close_fds()
197{
198 while (struct child *child = children) {
199 close(child->sock);
200 children = child->next;
201 free(child);
202 }
203
204 if (d.deadpipe[0] != -1)
205 {
206 close(d.deadpipe[0]);
207 d.deadpipe[0] = -1;
208 }
209
210 if (d.deadpipe[1] != -1)
211 {
212 close(d.deadpipe[1]);
213 d.deadpipe[1] = -1;
214 }
215
216 if (d.initpipe[0] != -1)
217 {
218 close(d.initpipe[0]);
219 d.initpipe[0] = -1;
220 }
221
222 if (d.initpipe[1] != -1)
223 {
224 close(d.initpipe[1]);
225 d.initpipe[1] = -1;
226 }
227
228 if (d.launcher[0] != -1)
229 {
230 close(d.launcher[0]);
231 d.launcher[0] = -1;
232 }
233 if (d.wrapper != -1)
234 {
235 close(d.wrapper);
236 d.wrapper = -1;
237 }
238 if (d.accepted_fd != -1)
239 {
240 close(d.accepted_fd);
241 d.accepted_fd = -1;
242 }
243#ifdef Q_WS_X11
244 if (X11fd >= 0)
245 {
246 close(X11fd);
247 X11fd = -1;
248 }
249 if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd )
250 {
251 close(X11_startup_notify_fd);
252 X11_startup_notify_fd = -1;
253 }
254#endif
255
256 KDE_signal(SIGCHLD, SIG_DFL);
257 KDE_signal(SIGPIPE, SIG_DFL);
258}
259
260/* Notify wrapper program that the child it started has finished. */
261static void child_died(pid_t exit_pid, int exit_status)
262{
263 struct child *child, **childptr = &children;
264
265 while ((child = *childptr))
266 {
267 if (child->pid == exit_pid)
268 {
269 /* Send a message with the return value of the child on the control socket */
270 klauncher_header request_header;
271 long request_data[2];
272 request_header.cmd = LAUNCHER_CHILD_DIED;
273 request_header.arg_length = sizeof(long) * 2;
274 request_data[0] = exit_pid;
275 request_data[1] = exit_status;
276 write(child->sock, &request_header, sizeof(request_header));
277 write(child->sock, request_data, request_header.arg_length);
278 close(child->sock);
279
280 *childptr = child->next;
281 free(child);
282 return;
283 }
284
285 childptr = &child->next;
286 }
287}
288
289
290static void exitWithErrorMsg(const QString &errorMsg)
291{
292 fprintf( stderr, "%s\n", errorMsg.toLocal8Bit().data() );
293 QByteArray utf8ErrorMsg = errorMsg.toUtf8();
294 d.result = 3; // Error with msg
295 write(d.fd[1], &d.result, 1);
296 int l = utf8ErrorMsg.length();
297 write(d.fd[1], &l, sizeof(int));
298 write(d.fd[1], utf8ErrorMsg.data(), l);
299 close(d.fd[1]);
300 exit(255);
301}
302
303static void setup_tty( const char* tty )
304{
305 if( tty == NULL || *tty == '\0' )
306 return;
307 int fd = KDE_open( tty, O_WRONLY );
308 if( fd < 0 )
309 {
310 perror( "kdeinit4: could not open() tty" );
311 return;
312 }
313 if( dup2( fd, STDOUT_FILENO ) < 0 )
314 {
315 perror( "kdeinit4: could not dup2() stdout tty" );
316 }
317 if( dup2( fd, STDERR_FILENO ) < 0 )
318 {
319 perror( "kdeinit4: could not dup2() stderr tty" );
320 }
321 close( fd );
322}
323
324// from kdecore/netwm.cpp
325static int get_current_desktop( Display* disp )
326{
327 int desktop = 0; // no desktop by default
328#ifdef Q_WS_X11 // Only X11 supports multiple desktops
329 Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP", False );
330 Atom type_ret;
331 int format_ret;
332 unsigned char *data_ret;
333 unsigned long nitems_ret, unused;
334 if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop,
335 0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret )
336 == Success)
337 {
338 if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1)
339 desktop = *((long *) data_ret) + 1;
340 if (data_ret)
341 XFree ((char*) data_ret);
342 }
343#endif
344 return desktop;
345}
346
347// var has to be e.g. "DISPLAY=", i.e. with =
348const char* get_env_var( const char* var, int envc, const char* envs )
349{
350 if( envc > 0 )
351 { // get the var from envs
352 const char* env_l = envs;
353 int ln = strlen( var );
354 for (int i = 0; i < envc; i++)
355 {
356 if( strncmp( env_l, var, ln ) == 0 )
357 return env_l + ln;
358 while(*env_l != 0) env_l++;
359 env_l++;
360 }
361 }
362 return NULL;
363}
364
365#ifdef Q_WS_X11
366static void init_startup_info( KStartupInfoId& id, const char* bin,
367 int envc, const char* envs )
368{
369 const char* dpy = get_env_var( DISPLAY"=", envc, envs );
370 // this may be called in a child, so it can't use display open using X11display
371 // also needed for multihead
372 X11_startup_notify_display = XOpenDisplay( dpy );
373 if( X11_startup_notify_display == NULL )
374 return;
375 X11_startup_notify_fd = XConnectionNumber( X11_startup_notify_display );
376 KStartupInfoData data;
377 int desktop = get_current_desktop( X11_startup_notify_display );
378 data.setDesktop( desktop );
379 data.setBin(QFile::decodeName(bin));
380 KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
381 XFlush( X11_startup_notify_display );
382}
383
384static void complete_startup_info( KStartupInfoId& id, pid_t pid )
385{
386 if( X11_startup_notify_display == NULL )
387 return;
388 if( pid == 0 ) // failure
389 KStartupInfo::sendFinishX( X11_startup_notify_display, id );
390 else
391 {
392 KStartupInfoData data;
393 data.addPid( pid );
394 data.setHostname();
395 KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
396 }
397 XCloseDisplay( X11_startup_notify_display );
398 X11_startup_notify_display = NULL;
399 X11_startup_notify_fd = -1;
400}
401#endif
402
403QByteArray execpath_avoid_loops( const QByteArray& exec, int envc, const char* envs, bool avoid_loops )
404{
405 QStringList paths;
406 const QRegExp pathSepRegExp(QString::fromLatin1("[:\b]"));
407 if( envc > 0 ) /* use the passed environment */
408 {
409 const char* path = get_env_var( "PATH=", envc, envs );
410 if( path != NULL )
411 paths = QFile::decodeName(path).split(pathSepRegExp);
412 } else {
413 paths = QString::fromLocal8Bit(qgetenv("PATH")).split(pathSepRegExp, QString::KeepEmptyParts);
414 }
415 QString execpath =
416 s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(QLatin1String(":")));
417 if (avoid_loops && !execpath.isEmpty()) {
418 const int pos = execpath.lastIndexOf(QLatin1Char('/'));
419 const QString bin_path = execpath.left(pos);
420 for( QStringList::Iterator it = paths.begin();
421 it != paths.end();
422 ++it ) {
423 if( *it == bin_path || *it == bin_path + QLatin1Char('/')) {
424 paths.erase( it );
425 break; // -->
426 }
427 }
428 execpath = s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(QLatin1String(":")));
429 }
430 return QFile::encodeName(execpath);
431}
432
433#ifdef KDEINIT_OOM_PROTECT
434static void oom_protect_sighandler( int ) {
435}
436
437static void reset_oom_protect() {
438 if( oom_pipe <= 0 )
439 return;
440 struct sigaction act, oldact;
441 act.sa_handler = oom_protect_sighandler;
442 act.sa_flags = 0;
443 sigemptyset( &act.sa_mask );
444 sigaction( SIGUSR1, &act, &oldact );
445 sigset_t sigs, oldsigs;
446 sigemptyset( &sigs );
447 sigaddset( &sigs, SIGUSR1 );
448 sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
449 pid_t pid = getpid();
450 if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) {
451 sigsuspend( &oldsigs ); // wait for the signal to come
452 } else {
453#ifndef NDEBUG
454 fprintf( stderr, "Failed to reset OOM protection: %d\n", pid );
455#endif
456 }
457 sigprocmask( SIG_SETMASK, &oldsigs, NULL );
458 sigaction( SIGUSR1, &oldact, NULL );
459 close( oom_pipe );
460 oom_pipe = -1;
461}
462#else
463static void reset_oom_protect() {
464}
465#endif
466
467static pid_t launch(int argc, const char *_name, const char *args,
468 const char *cwd=0, int envc=0, const char *envs=0,
469 bool reset_env = false,
470 const char *tty=0, bool avoid_loops = false,
471 const char* startup_id_str = "0" ) // krazy:exclude=doublequote_chars
472{
473 QString lib;
474 QByteArray name;
475 QByteArray exec;
476 QString libpath;
477 QByteArray execpath;
478
479 if (_name[0] != '/') {
480 name = _name;
481 lib = QFile::decodeName(name);
482 exec = name;
483 KLibrary klib(QLatin1String("libkdeinit4_") + lib, *s_instance );
484 libpath = klib.fileName();
485 if( libpath.isEmpty()) {
486 KLibrary klib(lib, *s_instance);
487 libpath = klib.fileName();
488 }
489 execpath = execpath_avoid_loops(exec, envc, envs, avoid_loops);
490 } else {
491 name = _name;
492 lib = QFile::decodeName(name);
493 name = name.mid(name.lastIndexOf('/') + 1);
494 exec = _name;
495 if (lib.endsWith(QLatin1String(".so")))
496 libpath = lib;
497 else {
498 // try to match an absolute path to an executable binary (either in bin/ or in libexec/)
499 // to a kdeinit module in the same prefix
500 if( lib.contains( QLatin1String( "/lib" KDELIBSUFF "/kde4/libexec/" ))) {
501 libpath = QString( lib ).replace( QLatin1String( "/lib" KDELIBSUFF "/kde4/libexec/" ),
502 QLatin1String("/lib" KDELIBSUFF "/libkdeinit4_")) + QLatin1String(".so");
503 } else if( lib.contains( QLatin1String( "/bin/" ))) {
504 libpath = QString( lib ).replace( QLatin1String( "/bin/" ),
505 QLatin1String("/lib" KDELIBSUFF "/libkdeinit4_")) + QLatin1String(".so");
506 }
507 // Don't confuse the user with "Could not load libkdeinit4_foo.so" if it doesn't exist
508 if (!QFile::exists(libpath)) {
509 libpath.clear();
510 }
511 execpath = exec;
512 }
513 }
514#ifndef NDEBUG
515 fprintf(stderr,"kdeinit4: preparing to launch %s\n", libpath.isEmpty()
516 ? execpath.constData() : libpath.toUtf8().constData());
517#endif
518 if (!args) {
519 argc = 1;
520 }
521
522 if (0 > pipe(d.fd))
523 {
524 perror("kdeinit4: pipe() failed");
525 d.result = 3;
526 d.errorMsg = i18n("Unable to start new process.\n"
527 "The system may have reached the maximum number of open files possible or the maximum number of open files that you are allowed to use has been reached.").toUtf8();
528 d.fork = 0;
529 return d.fork;
530 }
531
532#ifdef Q_WS_X11
533 KStartupInfoId startup_id;
534 startup_id.initId( startup_id_str );
535 if( !startup_id.none())
536 init_startup_info( startup_id, name, envc, envs );
537#endif
538 // find out this path before forking, doing it afterwards
539 // crashes on some platforms, notably OSX
540#ifdef Q_WS_MAC
541 const QString bundlepath = s_instance->dirs()->findExe(QFile::decodeName(execpath));
542#endif
543
544 d.errorMsg = 0;
545 d.fork = fork();
546 switch(d.fork) {
547 case -1:
548 perror("kdeinit4: fork() failed");
549 d.result = 3;
550 d.errorMsg = i18n("Unable to create new process.\n"
551 "The system may have reached the maximum number of processes possible or the maximum number of processes that you are allowed to use has been reached.").toUtf8();
552 close(d.fd[0]);
553 close(d.fd[1]);
554 d.fork = 0;
555 break;
556 case 0:
557 {
558 /** Child **/
559 close(d.fd[0]);
560 close_fds();
561 reset_oom_protect();
562
563 // Try to chdir, either to the requested directory or to the user's document path by default.
564 // We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist,
565 // we still want to execute `foo` even if the chdir() failed.
566 if (cwd && *cwd) {
567 (void)chdir(cwd);
568 }
569
570 if( reset_env ) // KWRAPPER/SHELL
571 {
572
573 QList<QByteArray> unset_envs;
574 for( int tmp_env_count = 0;
575 environ[tmp_env_count];
576 tmp_env_count++)
577 unset_envs.append( environ[ tmp_env_count ] );
578 foreach(const QByteArray &tmp, unset_envs)
579 {
580 int pos = tmp.indexOf( '=' );
581 if( pos >= 0 )
582 unsetenv( tmp.left( pos ));
583 }
584 }
585
586 for (int i = 0; i < envc; i++)
587 {
588 putenv((char *)envs);
589 while(*envs != 0) envs++;
590 envs++;
591 }
592
593#ifdef Q_WS_X11
594 if( startup_id.none())
595 KStartupInfo::resetStartupEnv();
596 else
597 startup_id.setupStartupEnv();
598#endif
599 {
600 int r;
601 QByteArray procTitle;
602 d.argv = (char **) malloc(sizeof(char *) * (argc+1));
603 d.argv[0] = (char *) _name;
604#ifdef Q_WS_MAC
605 QString argvexe = s_instance->dirs()->findExe(QString::fromLatin1(d.argv[0]));
606 if (!argvexe.isEmpty()) {
607 QByteArray cstr = argvexe.toLocal8Bit();
608 kDebug(7016) << "kdeinit4: launch() setting argv: " << cstr.data();
609 d.argv[0] = strdup(cstr.data());
610 }
611#endif
612 for (int i = 1; i < argc; i++)
613 {
614 d.argv[i] = (char *) args;
615 procTitle += ' ';
616 procTitle += (char *) args;
617 while(*args != 0) args++;
618 args++;
619 }
620 d.argv[argc] = 0;
621
622#ifndef SKIP_PROCTITLE
623 /** Give the process a new name **/
624#ifdef Q_OS_LINUX
625 /* set the process name, so that killall works like intended */
626 r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0);
627 if ( r == 0 )
628 proctitle_set( "%s [kdeinit]%s", name.data(), procTitle.data() ? procTitle.data() : "" );
629 else
630 proctitle_set( "kdeinit4: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
631#else
632 proctitle_set( "kdeinit4: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
633#endif
634#endif
635 }
636
637 if (libpath.isEmpty() && execpath.isEmpty())
638 {
639 QString errorMsg = i18n("Could not find '%1' executable.", QFile::decodeName(_name));
640 exitWithErrorMsg(errorMsg);
641 }
642
643
644 if ( !qgetenv("KDE_IS_PRELINKED").isEmpty() && !execpath.isEmpty())
645 libpath.truncate(0);
646
647 QLibrary l(libpath);
648
649 if ( !libpath.isEmpty() )
650 {
651 if (!l.load() || !l.isLoaded() )
652 {
653 QString ltdlError (l.errorString());
654 if (execpath.isEmpty())
655 {
656 // Error
657 QString errorMsg = i18n("Could not open library '%1'.\n%2", libpath, ltdlError);
658 exitWithErrorMsg(errorMsg);
659 }
660 else
661 {
662 // Print warning
663 fprintf(stderr, "Could not open library %s: %s\n", qPrintable(lib),
664 qPrintable(ltdlError) );
665 }
666 }
667 }
668 if (!l.isLoaded())
669 {
670 d.result = 2; // Try execing
671 write(d.fd[1], &d.result, 1);
672
673 // We set the close on exec flag.
674 // Closing of d.fd[1] indicates that the execvp succeeded!
675 fcntl(d.fd[1], F_SETFD, FD_CLOEXEC);
676
677 setup_tty( tty );
678
679 QByteArray executable = execpath;
680#ifdef Q_WS_MAC
681 if (!bundlepath.isEmpty())
682 executable = QFile::encodeName(bundlepath);
683#endif
684
685 if (!executable.isEmpty())
686 execvp(executable, d.argv);
687
688 d.result = 1; // Error
689 write(d.fd[1], &d.result, 1);
690 close(d.fd[1]);
691 exit(255);
692 }
693
694 void * sym = l.resolve( "kdeinitmain");
695 if (!sym )
696 {
697 sym = l.resolve( "kdemain" );
698 if ( !sym )
699 {
700 QString ltdlError = l.errorString();
701 fprintf(stderr, "Could not find kdemain: %s\n", qPrintable(ltdlError) );
702 QString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2",
703 libpath, ltdlError);
704 exitWithErrorMsg(errorMsg);
705 }
706 }
707
708 d.result = 0; // Success
709 write(d.fd[1], &d.result, 1);
710 close(d.fd[1]);
711
712 d.func = (int (*)(int, char *[])) sym;
713 if (d.debug_wait)
714 {
715 fprintf(stderr, "kdeinit4: Suspending process\n"
716 "kdeinit4: 'gdb kdeinit4 %d' to debug\n"
717 "kdeinit4: 'kill -SIGCONT %d' to continue\n",
718 getpid(), getpid());
719 kill(getpid(), SIGSTOP);
720 }
721 else
722 {
723 setup_tty( tty );
724 }
725
726 exit( d.func(argc, d.argv)); /* Launch! */
727
728 break;
729 }
730 default:
731 /** Parent **/
732 close(d.fd[1]);
733 bool exec = false;
734 for(;;)
735 {
736 d.n = read(d.fd[0], &d.result, 1);
737 if (d.n == 1)
738 {
739 if (d.result == 2)
740 {
741#ifndef NDEBUG
742 //fprintf(stderr, "kdeinit4: no kdeinit module, trying exec....\n");
743#endif
744 exec = true;
745 continue;
746 }
747 if (d.result == 3)
748 {
749 int l = 0;
750 d.n = read(d.fd[0], &l, sizeof(int));
751 if (d.n == sizeof(int))
752 {
753 QByteArray tmp;
754 tmp.resize(l+1);
755 d.n = read(d.fd[0], tmp.data(), l);
756 tmp[l] = 0;
757 if (d.n == l)
758 d.errorMsg = tmp;
759 }
760 }
761 // Finished
762 break;
763 }
764 if (d.n == -1)
765 {
766 if (errno == ECHILD) { // a child died.
767 continue;
768 }
769 if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read
770 continue;
771 }
772 }
773 if (d.n == 0)
774 {
775 if (exec) {
776 d.result = 0;
777 } else {
778 fprintf(stderr,"kdeinit4: (%s %s) Pipe closed unexpectedly", name.constData(), execpath.constData());
779 perror("kdeinit4: Pipe closed unexpectedly");
780 d.result = 1; // Error
781 }
782 break;
783 }
784 perror("kdeinit4: Error reading from pipe");
785 d.result = 1; // Error
786 break;
787 }
788 close(d.fd[0]);
789 }
790#ifdef Q_WS_X11
791 if( !startup_id.none())
792 {
793 if( d.fork && d.result == 0 ) // launched successfully
794 complete_startup_info( startup_id, d.fork );
795 else // failure, cancel ASN
796 complete_startup_info( startup_id, 0 );
797 }
798#endif
799 return d.fork;
800}
801
802extern "C" {
803
804static void sig_child_handler(int)
805{
806 /*
807 * Write into the pipe of death.
808 * This way we are sure that we return from the select()
809 *
810 * A signal itself causes select to return as well, but
811 * this creates a race-condition in case the signal arrives
812 * just before we enter the select.
813 */
814 char c = 0;
815 write(d.deadpipe[1], &c, 1);
816}
817
818}
819
820static void init_signals()
821{
822 struct sigaction act;
823 long options;
824
825 if (pipe(d.deadpipe) != 0)
826 {
827 perror("kdeinit4: Aborting. Can not create pipe");
828 exit(255);
829 }
830
831 options = fcntl(d.deadpipe[0], F_GETFL);
832 if (options == -1)
833 {
834 perror("kdeinit4: Aborting. Can not make pipe non-blocking");
835 exit(255);
836 }
837
838 if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1)
839 {
840 perror("kdeinit4: Aborting. Can not make pipe non-blocking");
841 exit(255);
842 }
843
844 /*
845 * A SIGCHLD handler is installed which sends a byte into the
846 * pipe of death. This is to ensure that a dying child causes
847 * an exit from select().
848 */
849 act.sa_handler=sig_child_handler;
850 sigemptyset(&(act.sa_mask));
851 sigaddset(&(act.sa_mask), SIGCHLD);
852 sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
853 act.sa_flags = SA_NOCLDSTOP;
854
855 // CC: take care of SunOS which automatically restarts interrupted system
856 // calls (and thus does not have SA_RESTART)
857
858#ifdef SA_RESTART
859 act.sa_flags |= SA_RESTART;
860#endif
861 sigaction( SIGCHLD, &act, 0L);
862
863 act.sa_handler=SIG_IGN;
864 sigemptyset(&(act.sa_mask));
865 sigaddset(&(act.sa_mask), SIGPIPE);
866 sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
867 act.sa_flags = 0;
868 sigaction( SIGPIPE, &act, 0L);
869}
870
871static void init_kdeinit_socket()
872{
873 struct sockaddr_un sa;
874 kde_socklen_t socklen;
875 long options;
876 const QByteArray home_dir = qgetenv("HOME");
877 int max_tries = 10;
878 if (home_dir.isEmpty())
879 {
880 fprintf(stderr, "kdeinit4: Aborting. $HOME not set!");
881 exit(255);
882 }
883 if (chdir(home_dir) != 0) {
884 fprintf(stderr, "kdeinit4: Aborting. Couldn't enter '%s'!", home_dir.constData());
885 exit(255);
886 }
887
888 {
889 QByteArray path = home_dir;
890 QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
891 if (access(path.data(), R_OK|W_OK))
892 {
893 if (errno == ENOENT)
894 {
895 fprintf(stderr, "kdeinit4: Aborting. $HOME directory (%s) does not exist.\n", path.data());
896 exit(255);
897 }
898 else if (readOnly.isEmpty())
899 {
900 fprintf(stderr, "kdeinit4: Aborting. No write access to $HOME directory (%s).\n", path.data());
901 exit(255);
902 }
903 }
904#if 0 // obsolete in kde4. Should we check writing to another file instead?
905 path = qgetenv("ICEAUTHORITY");
906 if (path.isEmpty())
907 {
908 path = home_dir;
909 path += "/.ICEauthority";
910 }
911 if (access(path.data(), R_OK|W_OK) && (errno != ENOENT))
912 {
913 fprintf(stderr, "kdeinit4: Aborting. No write access to '%s'.\n", path.data());
914 exit(255);
915 }
916#endif
917 }
918
919 /** Test if socket file is already present
920 * note that access() resolves symlinks, and so we check the actual
921 * socket file if it exists
922 */
923 if (access(sock_file, W_OK) == 0)
924 {
925 int s;
926 struct sockaddr_un server;
927
928// fprintf(stderr, "kdeinit4: Warning, socket_file already exists!\n");
929 /*
930 * create the socket stream
931 */
932 s = socket(PF_UNIX, SOCK_STREAM, 0);
933 if (s < 0)
934 {
935 perror("socket() failed");
936 exit(255);
937 }
938 server.sun_family = AF_UNIX;
939 strcpy(server.sun_path, sock_file);
940 socklen = sizeof(server);
941
942 if(connect(s, (struct sockaddr *)&server, socklen) == 0)
943 {
944 fprintf(stderr, "kdeinit4: Shutting down running client.\n");
945 klauncher_header request_header;
946 request_header.cmd = LAUNCHER_TERMINATE_KDEINIT;
947 request_header.arg_length = 0;
948 write(s, &request_header, sizeof(request_header));
949 sleep(1); // Give it some time
950 }
951 close(s);
952 }
953
954 /** Delete any stale socket file (and symlink) **/
955 unlink(sock_file);
956
957 /** create socket **/
958 d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0);
959 if (d.wrapper < 0)
960 {
961 perror("kdeinit4: Aborting. socket() failed");
962 exit(255);
963 }
964
965 options = fcntl(d.wrapper, F_GETFL);
966 if (options == -1)
967 {
968 perror("kdeinit4: Aborting. Can not make socket non-blocking");
969 close(d.wrapper);
970 exit(255);
971 }
972
973 if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1)
974 {
975 perror("kdeinit4: Aborting. Can not make socket non-blocking");
976 close(d.wrapper);
977 exit(255);
978 }
979
980 while (1) {
981 /** bind it **/
982 socklen = sizeof(sa);
983 memset(&sa, 0, socklen);
984 sa.sun_family = AF_UNIX;
985 strcpy(sa.sun_path, sock_file);
986 if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0)
987 {
988 if (max_tries == 0) {
989 perror("kdeinit4: Aborting. bind() failed");
990 fprintf(stderr, "Could not bind to socket '%s'\n", sock_file);
991 close(d.wrapper);
992 exit(255);
993 }
994 max_tries--;
995 } else
996 break;
997 }
998
999 /** set permissions **/
1000 if (chmod(sock_file, 0600) != 0)
1001 {
1002 perror("kdeinit4: Aborting. Can not set permissions on socket");
1003 fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
1004 unlink(sock_file);
1005 close(d.wrapper);
1006 exit(255);
1007 }
1008
1009 if(listen(d.wrapper, SOMAXCONN) < 0)
1010 {
1011 perror("kdeinit4: Aborting. listen() failed");
1012 unlink(sock_file);
1013 close(d.wrapper);
1014 exit(255);
1015 }
1016}
1017
1018/*
1019 * Read 'len' bytes from 'sock' into buffer.
1020 * returns 0 on success, -1 on failure.
1021 */
1022static int read_socket(int sock, char *buffer, int len)
1023{
1024 ssize_t result;
1025 int bytes_left = len;
1026 while ( bytes_left > 0)
1027 {
1028 result = read(sock, buffer, bytes_left);
1029 if (result > 0)
1030 {
1031 buffer += result;
1032 bytes_left -= result;
1033 }
1034 else if (result == 0)
1035 return -1;
1036 else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
1037 return -1;
1038 }
1039 return 0;
1040}
1041
1042static void start_klauncher()
1043{
1044 if (socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher) < 0) {
1045 perror("kdeinit4: socketpair() failed");
1046 exit(255);
1047 }
1048 char args[32];
1049 strcpy(args, "--fd=");
1050 sprintf(args + 5, "%d", d.launcher[1]);
1051 d.launcher_pid = launch( 2, "klauncher", args );
1052 close(d.launcher[1]);
1053#ifndef NDEBUG
1054 fprintf(stderr, "kdeinit4: Launched KLauncher, pid = %ld, result = %d\n",
1055 (long) d.launcher_pid, d.result);
1056#endif
1057}
1058
1059static void launcher_died()
1060{
1061 if (!d.launcher_ok)
1062 {
1063 /* This is bad. */
1064 fprintf(stderr, "kdeinit4: Communication error with launcher. Exiting!\n");
1065 ::exit(255);
1066 return;
1067 }
1068
1069 // KLauncher died... restart
1070#ifndef NDEBUG
1071 fprintf(stderr, "kdeinit4: KLauncher died unexpectedly.\n");
1072#endif
1073 // Make sure it's really dead.
1074 if (d.launcher_pid)
1075 {
1076 kill(d.launcher_pid, SIGKILL);
1077 sleep(1); // Give it some time
1078 }
1079
1080 d.launcher_ok = false;
1081 d.launcher_pid = 0;
1082 close(d.launcher[0]);
1083 d.launcher[0] = -1;
1084
1085 start_klauncher();
1086}
1087
1088static bool handle_launcher_request(int sock, const char *who)
1089{
1090 (void)who; // for NDEBUG
1091
1092 klauncher_header request_header;
1093 char *request_data = 0L;
1094 int result = read_socket(sock, (char *) &request_header, sizeof(request_header));
1095 if (result != 0)
1096 {
1097 return false;
1098 }
1099
1100 if ( request_header.arg_length != 0 )
1101 {
1102 request_data = (char *) malloc(request_header.arg_length);
1103
1104 result = read_socket(sock, request_data, request_header.arg_length);
1105 if (result != 0)
1106 {
1107 free(request_data);
1108 return false;
1109 }
1110 }
1111
1112 //kDebug() << "Got cmd" << request_header.cmd << commandToString(request_header.cmd);
1113 if (request_header.cmd == LAUNCHER_OK)
1114 {
1115 d.launcher_ok = true;
1116 }
1117 else if (request_header.arg_length &&
1118 ((request_header.cmd == LAUNCHER_EXEC) ||
1119 (request_header.cmd == LAUNCHER_EXT_EXEC) ||
1120 (request_header.cmd == LAUNCHER_SHELL ) ||
1121 (request_header.cmd == LAUNCHER_KWRAPPER) ||
1122 (request_header.cmd == LAUNCHER_EXEC_NEW)))
1123 {
1124 pid_t pid;
1125 klauncher_header response_header;
1126 long response_data;
1127 long l;
1128 memcpy( &l, request_data, sizeof( long ));
1129 int argc = l;
1130 const char *name = request_data + sizeof(long);
1131 const char *args = name + strlen(name) + 1;
1132 const char *cwd = 0;
1133 int envc = 0;
1134 const char *envs = 0;
1135 const char *tty = 0;
1136 int avoid_loops = 0;
1137 const char *startup_id_str = "0"; // krazy:exclude=doublequote_chars
1138
1139#ifndef NDEBUG
1140 fprintf(stderr, "kdeinit4: Got %s '%s' from %s.\n",
1141 commandToString(request_header.cmd),
1142 name, who);
1143#endif
1144
1145 const char *arg_n = args;
1146 for(int i = 1; i < argc; i++)
1147 {
1148 arg_n = arg_n + strlen(arg_n) + 1;
1149 }
1150
1151 if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER )
1152 {
1153 // Shell or kwrapper
1154 cwd = arg_n; arg_n += strlen(cwd) + 1;
1155 }
1156 if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
1157 || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
1158 {
1159 memcpy( &l, arg_n, sizeof( long ));
1160 envc = l;
1161 arg_n += sizeof(long);
1162 envs = arg_n;
1163 for(int i = 0; i < envc; i++)
1164 {
1165 arg_n = arg_n + strlen(arg_n) + 1;
1166 }
1167 if( request_header.cmd == LAUNCHER_KWRAPPER )
1168 {
1169 tty = arg_n;
1170 arg_n += strlen( tty ) + 1;
1171 }
1172 }
1173
1174 if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
1175 || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
1176 {
1177 memcpy( &l, arg_n, sizeof( long ));
1178 avoid_loops = l;
1179 arg_n += sizeof( long );
1180 }
1181
1182 if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
1183 || request_header.cmd == LAUNCHER_EXT_EXEC )
1184 {
1185 startup_id_str = arg_n;
1186 arg_n += strlen( startup_id_str ) + 1;
1187 }
1188
1189 if ((request_header.arg_length > (arg_n - request_data)) &&
1190 (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ))
1191 {
1192 // Optional cwd
1193 cwd = arg_n; arg_n += strlen(cwd) + 1;
1194 }
1195
1196 if ((arg_n - request_data) != request_header.arg_length)
1197 {
1198#ifndef NDEBUG
1199 fprintf(stderr, "kdeinit4: EXEC request has invalid format.\n");
1200#endif
1201 free(request_data);
1202 d.debug_wait = false;
1203 return true; // sure?
1204 }
1205
1206 // support for the old a bit broken way of setting DISPLAY for multihead
1207 QByteArray olddisplay = qgetenv(DISPLAY);
1208 QByteArray kdedisplay = qgetenv("KDE_DISPLAY");
1209 bool reset_display = (! olddisplay.isEmpty() &&
1210 ! kdedisplay.isEmpty() &&
1211 olddisplay != kdedisplay);
1212
1213 if (reset_display)
1214 setenv(DISPLAY, kdedisplay, true);
1215
1216 pid = launch( argc, name, args, cwd, envc, envs,
1217 request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER,
1218 tty, avoid_loops, startup_id_str );
1219
1220 if (reset_display) {
1221 unsetenv("KDE_DISPLAY");
1222 setenv(DISPLAY, olddisplay, true);
1223 }
1224
1225 if (pid && (d.result == 0))
1226 {
1227 response_header.cmd = LAUNCHER_OK;
1228 response_header.arg_length = sizeof(response_data);
1229 response_data = pid;
1230 write(sock, &response_header, sizeof(response_header));
1231 write(sock, &response_data, response_header.arg_length);
1232
1233 /* add new child to list */
1234 struct child *child = (struct child *) malloc(sizeof(struct child));
1235 child->pid = pid;
1236 child->sock = dup(sock);
1237 child->next = children;
1238 children = child;
1239 }
1240 else
1241 {
1242 int l = d.errorMsg.length();
1243 if (l) l++; // Include trailing null.
1244 response_header.cmd = LAUNCHER_ERROR;
1245 response_header.arg_length = l;
1246 write(sock, &response_header, sizeof(response_header));
1247 if (l)
1248 write(sock, d.errorMsg.data(), l);
1249 }
1250 d.debug_wait = false;
1251 }
1252 else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV)
1253 {
1254 const char *env_name;
1255 const char *env_value;
1256 env_name = request_data;
1257 env_value = env_name + strlen(env_name) + 1;
1258
1259#ifndef NDEBUG
1260 fprintf(stderr, "kdeinit4: Got SETENV '%s=%s' from %s.\n", env_name, env_value, who);
1261#endif
1262
1263 if ( request_header.arg_length !=
1264 (int) (strlen(env_name) + strlen(env_value) + 2))
1265 {
1266#ifndef NDEBUG
1267 fprintf(stderr, "kdeinit4: SETENV request has invalid format.\n");
1268#endif
1269 free(request_data);
1270 return true; // sure?
1271 }
1272 setenv( env_name, env_value, 1);
1273 }
1274 else if (request_header.cmd == LAUNCHER_TERMINATE_KDE)
1275 {
1276#ifndef NDEBUG
1277 fprintf(stderr,"kdeinit4: terminate KDE.\n");
1278#endif
1279#ifdef Q_WS_X11
1280 kdeinit_xio_errhandler( 0L );
1281#endif
1282 }
1283 else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT)
1284 {
1285#ifndef NDEBUG
1286 fprintf(stderr,"kdeinit4: Got termination request (PID %ld).\n", (long) getpid());
1287#endif
1288 if (d.launcher_pid) {
1289 kill(d.launcher_pid, SIGTERM);
1290 d.launcher_pid = 0;
1291 close(d.launcher[0]);
1292 d.launcher[0] = -1;
1293 }
1294 unlink(sock_file);
1295 if (children) {
1296 close(d.wrapper);
1297 d.wrapper = -1;
1298#ifndef NDEBUG
1299 fprintf(stderr,"kdeinit4: Closed sockets, but not exiting until all children terminate.\n");
1300#endif
1301 } else {
1302 raise(SIGTERM);
1303 }
1304 }
1305 else if (request_header.cmd == LAUNCHER_DEBUG_WAIT)
1306 {
1307#ifndef NDEBUG
1308 fprintf(stderr,"kdeinit4: Debug wait activated.\n");
1309#endif
1310 d.debug_wait = true;
1311 }
1312 if (request_data)
1313 free(request_data);
1314 return true;
1315}
1316
1317static void handle_requests(pid_t waitForPid)
1318{
1319 int max_sock = d.deadpipe[0];
1320 if (d.wrapper > max_sock)
1321 max_sock = d.wrapper;
1322 if (d.launcher[0] > max_sock)
1323 max_sock = d.launcher[0];
1324#ifdef Q_WS_X11
1325 if (X11fd > max_sock)
1326 max_sock = X11fd;
1327#endif
1328 max_sock++;
1329
1330 while(1)
1331 {
1332 fd_set rd_set;
1333 fd_set wr_set;
1334 fd_set e_set;
1335 int result;
1336 pid_t exit_pid;
1337 int exit_status;
1338 char c;
1339
1340 /* Flush the pipe of death */
1341 while( read(d.deadpipe[0], &c, 1) == 1)
1342 {}
1343
1344 /* Handle dying children */
1345 do {
1346 exit_pid = waitpid(-1, &exit_status, WNOHANG);
1347 if (exit_pid > 0)
1348 {
1349#ifndef NDEBUG
1350 fprintf(stderr, "kdeinit4: PID %ld terminated.\n", (long) exit_pid);
1351#endif
1352 if (waitForPid && (exit_pid == waitForPid))
1353 return;
1354
1355 if( WIFEXITED( exit_status )) // fix process return value
1356 exit_status = WEXITSTATUS(exit_status);
1357 else if( WIFSIGNALED(exit_status))
1358 exit_status = 128 + WTERMSIG( exit_status );
1359 child_died(exit_pid, exit_status);
1360
1361 if (d.wrapper < 0 && !children) {
1362#ifndef NDEBUG
1363 fprintf(stderr, "kdeinit4: Last child terminated, exiting (PID %ld).\n",
1364 (long) getpid());
1365#endif
1366 raise(SIGTERM);
1367 }
1368 }
1369 }
1370 while( exit_pid > 0);
1371
1372 FD_ZERO(&rd_set);
1373 FD_ZERO(&wr_set);
1374 FD_ZERO(&e_set);
1375
1376 if (d.launcher[0] >= 0)
1377 FD_SET(d.launcher[0], &rd_set);
1378 if (d.wrapper >= 0)
1379 FD_SET(d.wrapper, &rd_set);
1380 FD_SET(d.deadpipe[0], &rd_set);
1381#ifdef Q_WS_X11
1382 if(X11fd >= 0) FD_SET(X11fd, &rd_set);
1383#endif
1384
1385 result = select(max_sock, &rd_set, &wr_set, &e_set, 0);
1386 if (result < 0) {
1387 if (errno == EINTR || errno == EAGAIN)
1388 continue;
1389 perror("kdeinit4: Aborting. select() failed");
1390 return;
1391 }
1392
1393 /* Handle wrapper request */
1394 if (d.wrapper >= 0 && FD_ISSET(d.wrapper, &rd_set))
1395 {
1396 struct sockaddr_un client;
1397 kde_socklen_t sClient = sizeof(client);
1398 int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient);
1399 if (sock >= 0)
1400 {
1401 d.accepted_fd = sock;
1402 handle_launcher_request(sock, "wrapper");
1403 close(sock);
1404 d.accepted_fd = -1;
1405 }
1406 }
1407
1408 /* Handle launcher request */
1409 if (d.launcher[0] >= 0 && FD_ISSET(d.launcher[0], &rd_set))
1410 {
1411 if (!handle_launcher_request(d.launcher[0], "launcher"))
1412 launcher_died();
1413 if (waitForPid == d.launcher_pid)
1414 return;
1415 }
1416
1417#ifdef Q_WS_X11
1418 /* Look for incoming X11 events */
1419 if(X11fd >= 0 && FD_ISSET(X11fd,&rd_set)) {
1420 if (X11display != 0) {
1421 XEvent event_return;
1422 while (XPending(X11display))
1423 XNextEvent(X11display, &event_return);
1424 }
1425 }
1426#endif
1427 }
1428}
1429
1430static void kdeinit_library_path()
1431{
1432 const QStringList ltdl_library_path =
1433 QFile::decodeName(qgetenv("LTDL_LIBRARY_PATH")).split(QLatin1Char(':'),QString::SkipEmptyParts);
1434#ifdef Q_OS_DARWIN
1435 const QByteArray ldlibpath = qgetenv("DYLD_LIBRARY_PATH");
1436#else
1437 const QByteArray ldlibpath = qgetenv("LD_LIBRARY_PATH");
1438#endif
1439 const QStringList ld_library_path =
1440 QFile::decodeName(ldlibpath).split(QLatin1Char(':'),QString::SkipEmptyParts);
1441
1442 QByteArray extra_path;
1443 const QStringList candidates = s_instance->dirs()->resourceDirs("lib");
1444 for (QStringList::ConstIterator it = candidates.begin();
1445 it != candidates.end();
1446 ++it)
1447 {
1448 QString d = *it;
1449 if (ltdl_library_path.contains(d))
1450 continue;
1451 if (ld_library_path.contains(d))
1452 continue;
1453 if (d[d.length()-1] == QLatin1Char('/'))
1454 {
1455 d.truncate(d.length()-1);
1456 if (ltdl_library_path.contains(d))
1457 continue;
1458 if (ld_library_path.contains(d))
1459 continue;
1460 }
1461 if ((d == QLatin1String("/lib")) || (d == QLatin1String("/usr/lib")))
1462 continue;
1463
1464 QByteArray dir = QFile::encodeName(d);
1465
1466 if (access(dir, R_OK))
1467 continue;
1468
1469 if ( !extra_path.isEmpty())
1470 extra_path += ':';
1471 extra_path += dir;
1472 }
1473
1474// if (!extra_path.isEmpty())
1475// lt_dlsetsearchpath(extra_path.data());
1476
1477 QByteArray display = qgetenv(DISPLAY);
1478 if (display.isEmpty())
1479 {
1480#if defined(Q_WS_X11) || defined(Q_WS_QWS)
1481 fprintf(stderr, "kdeinit4: Aborting. $"DISPLAY" is not set.\n");
1482 exit(255);
1483#endif
1484 }
1485 int i;
1486 if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0)
1487 display.truncate(i);
1488
1489 display.replace(':','_');
1490#ifdef __APPLE__
1491 display.replace('/','_');
1492#endif
1493 // WARNING, if you change the socket name, adjust kwrapper too
1494 const QString socketFileName = QString::fromLatin1("kdeinit4_%1").arg(QLatin1String(display));
1495 QByteArray socketName = QFile::encodeName(KStandardDirs::locateLocal("socket", socketFileName, *s_instance));
1496 if (socketName.length() >= MAX_SOCK_FILE)
1497 {
1498 fprintf(stderr, "kdeinit4: Aborting. Socket name will be too long:\n");
1499 fprintf(stderr, " '%s'\n", socketName.data());
1500 exit(255);
1501 }
1502 strcpy(sock_file, socketName.data());
1503}
1504
1505int kdeinit_xio_errhandler( Display *disp )
1506{
1507 // disp is 0L when KDE shuts down. We don't want those warnings then.
1508
1509 if ( disp )
1510 qWarning( "kdeinit4: Fatal IO error: client killed" );
1511
1512 if (sock_file[0])
1513 {
1514 /** Delete any stale socket file **/
1515 unlink(sock_file);
1516 }
1517
1518 // Don't kill our children in suicide mode, they may still be in use
1519 if (d.suicide)
1520 {
1521 if (d.launcher_pid)
1522 kill(d.launcher_pid, SIGTERM);
1523 if (d.kded_pid)
1524 kill(d.kded_pid, SIGTERM);
1525 exit( 0 );
1526 }
1527
1528 if ( disp )
1529 qWarning( "kdeinit4: sending SIGHUP to children." );
1530
1531 /* this should remove all children we started */
1532 KDE_signal(SIGHUP, SIG_IGN);
1533 kill(0, SIGHUP);
1534
1535 sleep(2);
1536
1537 if ( disp )
1538 qWarning( "kdeinit4: sending SIGTERM to children." );
1539
1540 /* and if they don't listen to us, this should work */
1541 KDE_signal(SIGTERM, SIG_IGN);
1542 kill(0, SIGTERM);
1543
1544 if ( disp )
1545 qWarning( "kdeinit4: Exit." );
1546
1547 exit( 0 );
1548 return 0;
1549}
1550
1551#ifdef Q_WS_X11
1552int kdeinit_x_errhandler( Display *dpy, XErrorEvent *err )
1553{
1554#ifndef NDEBUG
1555 char errstr[256];
1556 // kdeinit almost doesn't use X, and therefore there shouldn't be any X error
1557 XGetErrorText( dpy, err->error_code, errstr, 256 );
1558 fprintf(stderr, "kdeinit4(%d) : KDE detected X Error: %s %d\n"
1559 " Major opcode: %d\n"
1560 " Minor opcode: %d\n"
1561 " Resource id: 0x%lx\n",
1562 getpid(), errstr, err->error_code, err->request_code, err->minor_code, err->resourceid );
1563
1564 //kDebug() << kBacktrace();
1565
1566#else
1567 Q_UNUSED(dpy);
1568 Q_UNUSED(err);
1569#endif
1570 return 0;
1571}
1572#endif
1573
1574#ifdef Q_WS_X11
1575// needs to be done sooner than initXconnection() because of also opening
1576// another X connection for startup notification purposes
1577static void setupX()
1578{
1579 XSetIOErrorHandler(kdeinit_xio_errhandler);
1580 XSetErrorHandler(kdeinit_x_errhandler);
1581/*
1582 Handle the tricky case of running via kdesu/su/sudo/etc. There the usual case
1583 is that kdesu (etc.) creates a file with xauth information, sets XAUTHORITY,
1584 runs the command and removes the xauth file after the command finishes. However,
1585 dbus and kdeinit daemon currently don't clean up properly and keeping running.
1586 Which means that running a KDE app via kdesu the second time talks to kdeinit
1587 with obsolete xauth information, which makes it unable to connect to X or launch
1588 any X11 applications.
1589 Even fixing the cleanup probably wouldn't be sufficient, since it'd be possible to
1590 launch one kdesu session, another one, exit the first one and the app from the second
1591 session would be using kdeinit from the first one.
1592 So the trick here is to duplicate the xauth file to another file in KDE's tmp
1593 location, make the file have a consistent name so that future sessions will use it
1594 as well, point XAUTHORITY there and never remove the file (except for possible
1595 tmp cleanup).
1596*/
1597 if( !qgetenv( "XAUTHORITY" ).isEmpty()) {
1598 QByteArray display = qgetenv( DISPLAY );
1599 int i;
1600 if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0)
1601 display.truncate(i);
1602 display.replace(':','_');
1603#ifdef __APPLE__
1604 display.replace('/','_');
1605#endif
1606 QString xauth = s_instance->dirs()->saveLocation( "tmp" ) + QLatin1String( "xauth-" )
1607 + QString::number( getuid()) + QLatin1String( "-" ) + QString::fromLocal8Bit( display );
1608 KSaveFile xauthfile( xauth );
1609 QFile xauthfrom( QFile::decodeName( qgetenv( "XAUTHORITY" )));
1610 if( !xauthfrom.open( QFile::ReadOnly ) || !xauthfile.open( QFile::WriteOnly )
1611 || xauthfile.write( xauthfrom.readAll()) != xauthfrom.size() || !xauthfile.finalize()) {
1612 xauthfile.abort();
1613 } else {
1614 setenv( "XAUTHORITY", QFile::encodeName( xauth ), true );
1615 }
1616 }
1617}
1618
1619// Borrowed from kdebase/kaudio/kaudioserver.cpp
1620static int initXconnection()
1621{
1622 X11display = XOpenDisplay(NULL);
1623 if ( X11display != 0 ) {
1624 XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \
1625 0,
1626 BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)),
1627 BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) );
1628#ifndef NDEBUG
1629 fprintf(stderr, "kdeinit4: opened connection to %s\n", DisplayString(X11display));
1630#endif
1631 int fd = XConnectionNumber( X11display );
1632 int on = 1;
1633 (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on));
1634 return fd;
1635 } else
1636 fprintf(stderr, "kdeinit4: Can not connect to the X Server.\n" \
1637 "kdeinit4: Might not terminate at end of session.\n");
1638
1639 return -1;
1640}
1641#endif
1642
1643extern "C" {
1644
1645static void secondary_child_handler(int)
1646{
1647 waitpid(-1, 0, WNOHANG);
1648}
1649
1650}
1651
1652int main(int argc, char **argv, char **envp)
1653{
1654#ifndef _WIN32_WCE
1655 setlocale (LC_ALL, "");
1656 setlocale (LC_NUMERIC, "C");
1657#endif
1658
1659 pid_t pid;
1660 bool do_fork = true;
1661 int launch_klauncher = 1;
1662 int launch_kded = 1;
1663 int keep_running = 1;
1664 d.suicide = false;
1665
1666 /** Save arguments first... **/
1667 char **safe_argv = (char **) malloc( sizeof(char *) * argc);
1668 for(int i = 0; i < argc; i++)
1669 {
1670 safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]);
1671 if (strcmp(safe_argv[i], "--no-klauncher") == 0)
1672 launch_klauncher = 0;
1673 if (strcmp(safe_argv[i], "--no-kded") == 0)
1674 launch_kded = 0;
1675#ifdef Q_WS_MACX
1676 // make it nofork to match KUniqueApplication, technically command-line incompatible
1677 if (strcmp(safe_argv[i], "--nofork") == 0)
1678#else
1679 if (strcmp(safe_argv[i], "--no-fork") == 0)
1680#endif
1681 do_fork = false;
1682 if (strcmp(safe_argv[i], "--suicide") == 0)
1683 d.suicide = true;
1684 if (strcmp(safe_argv[i], "--exit") == 0)
1685 keep_running = 0;
1686 if (strcmp(safe_argv[i], "--version") == 0)
1687 {
1688 printf("Qt: %s\n", qVersion());
1689 printf("KDE: %s\n", KDE_VERSION_STRING);
1690 exit(0);
1691 }
1692#ifdef KDEINIT_OOM_PROTECT
1693 if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i+1<argc)
1694 oom_pipe = atol(argv[i+1]);
1695#endif
1696 if (strcmp(safe_argv[i], "--help") == 0)
1697 {
1698 printf("Usage: kdeinit4 [options]\n");
1699 // printf(" --no-dcop Do not start dcopserver\n");
1700#ifdef Q_WS_MACX
1701 printf(" --nofork Do not fork\n");
1702#else
1703 printf(" --no-fork Do not fork\n");
1704#endif
1705 // printf(" --no-klauncher Do not start klauncher\n");
1706 printf(" --no-kded Do not start kded\n");
1707 printf(" --suicide Terminate when no KDE applications are left running\n");
1708 printf(" --version Show version information\n");
1709 // printf(" --exit Terminate when kded has run\n");
1710 exit(0);
1711 }
1712 }
1713
1714 cleanup_fds();
1715
1716 // Redirect stdout to stderr. We have no reason to use stdout anyway.
1717 // This minimizes our impact on commands used in pipes.
1718 (void)dup2(2, 1);
1719
1720 if (do_fork) {
1721#ifdef Q_WS_MACX
1722 mac_fork_and_reexec_self();
1723#else
1724 if (pipe(d.initpipe) != 0) {
1725 perror("kdeinit4: pipe failed");
1726 return 1;
1727 }
1728
1729 // Fork here and let parent process exit.
1730 // Parent process may only exit after all required services have been
1731 // launched. (dcopserver/klauncher and services which start with '+')
1732 KDE_signal( SIGCHLD, secondary_child_handler);
1733 if (fork() > 0) // Go into background
1734 {
1735 close(d.initpipe[1]);
1736 d.initpipe[1] = -1;
1737 // wait till init is complete
1738 char c;
1739 while( read(d.initpipe[0], &c, 1) < 0)
1740 ;
1741 // then exit;
1742 close(d.initpipe[0]);
1743 d.initpipe[0] = -1;
1744 return 0;
1745 }
1746 close(d.initpipe[0]);
1747 d.initpipe[0] = -1;
1748#endif
1749 }
1750
1751 /** Make process group leader (for shutting down children later) **/
1752 if(keep_running)
1753 setsid();
1754
1755 /** Create our instance **/
1756 s_instance = new KComponentData("kdeinit4", QByteArray(), KComponentData::SkipMainComponentRegistration);
1757
1758 /** Prepare to change process name **/
1759#ifndef SKIP_PROCTITLE
1760 proctitle_init(argc, argv, envp);
1761#endif
1762
1763 kdeinit_library_path();
1764 // Don't make our instance the global instance
1765 // (do it only after kdeinit_library_path, that one indirectly uses KConfig,
1766 // which seems to be buggy and always use KGlobal instead of the matching KComponentData)
1767 Q_ASSERT(!KGlobal::hasMainComponent());
1768 // don't change envvars before proctitle_init()
1769 unsetenv("LD_BIND_NOW");
1770 unsetenv("DYLD_BIND_AT_LAUNCH");
1771 KApplication::loadedByKdeinit = true;
1772
1773 d.maxname = strlen(argv[0]);
1774 d.launcher_pid = 0;
1775 d.kded_pid = 0;
1776 d.wrapper = -1;
1777 d.accepted_fd = -1;
1778 d.debug_wait = false;
1779 d.launcher_ok = false;
1780 children = NULL;
1781 init_signals();
1782#ifdef Q_WS_X11
1783 setupX();
1784#endif
1785
1786 if (keep_running)
1787 {
1788 /*
1789 * Create ~/.kde/tmp-<hostname>/kdeinit4-<display> socket for incoming wrapper
1790 * requests.
1791 */
1792 init_kdeinit_socket();
1793 }
1794#ifdef Q_WS_X11
1795 if (!d.suicide && qgetenv("KDE_IS_PRELINKED").isEmpty()) {
1796 const int extrasCount = sizeof(extra_libs)/sizeof(extra_libs[0]);
1797 for (int i=0; i<extrasCount; i++) {
1798 QString extra = KStandardDirs::locate("lib", QLatin1String(extra_libs[i]), *s_instance);
1799
1800 // can't use KLibLoader here as it would unload the library
1801 // again
1802 if (!extra.isEmpty()) {
1803 QLibrary l(extra);
1804 l.setLoadHints(QLibrary::ExportExternalSymbolsHint);
1805 (void)l.load();
1806 }
1807#ifndef NDEBUG
1808 else {
1809 fprintf( stderr, "%s was not found.\n", extra_libs[i] );
1810 }
1811#endif
1812
1813 }
1814 }
1815#endif
1816 if (launch_klauncher)
1817 {
1818 start_klauncher();
1819 handle_requests(d.launcher_pid); // Wait for klauncher to be ready
1820 }
1821
1822#ifdef Q_WS_X11
1823 X11fd = initXconnection();
1824#endif
1825
1826 {
1827 QFont::initialize();
1828#ifdef Q_WS_X11
1829 if (XSupportsLocale ())
1830 {
1831 // Similar to QApplication::create_xim()
1832 // but we need to use our own display
1833 XOpenIM (X11display, 0, 0, 0);
1834 }
1835#endif
1836 }
1837
1838 if (launch_kded)
1839 {
1840 setenv("KDED_STARTED_BY_KDEINIT", "1", true);
1841 pid = launch( 1, KDED_EXENAME, 0 );
1842 unsetenv("KDED_STARTED_BY_KDEINIT");
1843#ifndef NDEBUG
1844 fprintf(stderr, "kdeinit4: Launched KDED, pid = %ld result = %d\n", (long) pid, d.result);
1845#endif
1846 d.kded_pid = pid;
1847 handle_requests(pid);
1848 }
1849
1850 for(int i = 1; i < argc; i++)
1851 {
1852 if (safe_argv[i][0] == '+')
1853 {
1854 pid = launch( 1, safe_argv[i]+1, 0);
1855#ifndef NDEBUG
1856 fprintf(stderr, "kdeinit4: Launched '%s', pid = %ld result = %d\n", safe_argv[i]+1, (long) pid, d.result);
1857#endif
1858 handle_requests(pid);
1859 }
1860 else if (safe_argv[i][0] == '-'
1861#ifdef KDEINIT_OOM_PROTECT
1862 || isdigit(safe_argv[i][0])
1863#endif
1864 )
1865 {
1866 // Ignore
1867 }
1868 else
1869 {
1870 pid = launch( 1, safe_argv[i], 0 );
1871#ifndef NDEBUG
1872 fprintf(stderr, "kdeinit4: Launched '%s', pid = %ld result = %d\n", safe_argv[i], (long) pid, d.result);
1873#endif
1874 }
1875 }
1876
1877 /** Free arguments **/
1878 for(int i = 0; i < argc; i++)
1879 {
1880 free(safe_argv[i]);
1881 }
1882 free (safe_argv);
1883
1884#ifndef SKIP_PROCTITLE
1885 proctitle_set("kdeinit4 Running...");
1886#endif
1887
1888 if (!keep_running)
1889 return 0;
1890
1891 if (d.initpipe[1] != -1)
1892 {
1893 char c = 0;
1894 write(d.initpipe[1], &c, 1); // Kdeinit is started.
1895 close(d.initpipe[1]);
1896 d.initpipe[1] = -1;
1897 }
1898
1899 handle_requests(0);
1900
1901 return 0;
1902}
1903
1904
1905