summaryrefslogtreecommitdiffstats
path: root/kinit/kinit.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kinit/kinit.cpp
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kinit/kinit.cpp')
-rw-r--r--kinit/kinit.cpp1912
1 files changed, 1912 insertions, 0 deletions
diff --git a/kinit/kinit.cpp b/kinit/kinit.cpp
new file mode 100644
index 000000000..8b654b038
--- /dev/null
+++ b/kinit/kinit.cpp
@@ -0,0 +1,1912 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org>
+ * (c) 1999 Mario Weilguni <mweilguni@sime.com>
+ * (c) 2001 Lubos Lunak <l.lunak@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <setproctitle.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qfont.h>
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <klibloader.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+#ifdef Q_OS_LINUX
+#include <sys/prctl.h>
+#ifndef PR_SET_NAME
+#define PR_SET_NAME 15
+#endif
+#endif
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#include <kstartupinfo.h> // schroder
+#endif
+
+#include <kdeversion.h>
+
+#include "ltdl.h"
+#include "klauncher_cmds.h"
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+//#undef K_WS_QTONLY
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LTDL_GLOBAL DL_GLOBAL
+# else
+# define LTDL_GLOBAL 0
+# endif
+#endif
+
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+#include <X11/Xft/Xft.h>
+extern "C" FcBool XftInitFtLibrary (void);
+#include <fontconfig/fontconfig.h>
+#endif
+
+extern char **environ;
+
+extern int lt_dlopen_flag;
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+static int X11fd = -1;
+static Display *X11display = 0;
+static int X11_startup_notify_fd = -1;
+static Display *X11_startup_notify_display = 0;
+#endif
+static const KInstance *s_instance = 0;
+#define MAX_SOCK_FILE 255
+static char sock_file[MAX_SOCK_FILE];
+static char sock_file_old[MAX_SOCK_FILE];
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+#define DISPLAY "DISPLAY"
+#elif defined(Q_WS_QWS)
+#define DISPLAY "QWS_DISPLAY"
+#elif defined(Q_WS_MACX)
+#define DISPLAY "MAC_DISPLAY"
+#elif defined(K_WS_QTONLY)
+#define DISPLAY "QT_DISPLAY"
+#else
+#error Use QT/X11 or QT/Embedded
+#endif
+
+/* Group data */
+static struct {
+ int maxname;
+ int fd[2];
+ int launcher[2]; /* socket pair for launcher communication */
+ int deadpipe[2]; /* pipe used to detect dead children */
+ int initpipe[2];
+ int wrapper; /* socket for wrapper communication */
+ int wrapper_old; /* old socket for wrapper communication */
+ char result;
+ int exit_status;
+ pid_t fork;
+ pid_t launcher_pid;
+ pid_t my_pid;
+ int n;
+ lt_dlhandle handle;
+ lt_ptr sym;
+ char **argv;
+ int (*func)(int, char *[]);
+ int (*launcher_func)(int);
+ bool debug_wait;
+ int lt_dlopen_flag;
+ QCString errorMsg;
+ bool launcher_ok;
+ bool suicide;
+} d;
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+extern "C" {
+int kdeinit_xio_errhandler( Display * );
+int kdeinit_x_errhandler( Display *, XErrorEvent *err );
+}
+#endif
+
+/* These are to link libkparts even if 'smart' linker is used */
+#include <kparts/plugin.h>
+extern "C" KParts::Plugin* _kinit_init_kparts() { return new KParts::Plugin(); }
+/* These are to link libkio even if 'smart' linker is used */
+#include <kio/authinfo.h>
+extern "C" KIO::AuthInfo* _kioslave_init_kio() { return new KIO::AuthInfo(); }
+
+/*
+ * Close fd's which are only useful for the parent process.
+ * Restore default signal handlers.
+ */
+static void close_fds()
+{
+ if (d.deadpipe[0] != -1)
+ {
+ close(d.deadpipe[0]);
+ d.deadpipe[0] = -1;
+ }
+
+ if (d.deadpipe[1] != -1)
+ {
+ close(d.deadpipe[1]);
+ d.deadpipe[1] = -1;
+ }
+
+ if (d.initpipe[0] != -1)
+ {
+ close(d.initpipe[0]);
+ d.initpipe[0] = -1;
+ }
+
+ if (d.initpipe[1] != -1)
+ {
+ close(d.initpipe[1]);
+ d.initpipe[1] = -1;
+ }
+
+ if (d.launcher_pid)
+ {
+ close(d.launcher[0]);
+ d.launcher_pid = 0;
+ }
+ if (d.wrapper)
+ {
+ close(d.wrapper);
+ d.wrapper = 0;
+ }
+ if (d.wrapper_old)
+ {
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ }
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if (X11fd >= 0)
+ {
+ close(X11fd);
+ X11fd = -1;
+ }
+ if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd )
+ {
+ close(X11_startup_notify_fd);
+ X11_startup_notify_fd = -1;
+ }
+#endif
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+}
+
+static void exitWithErrorMsg(const QString &errorMsg)
+{
+ fprintf( stderr, "%s\n", errorMsg.local8Bit().data() );
+ QCString utf8ErrorMsg = errorMsg.utf8();
+ d.result = 3; // Error with msg
+ write(d.fd[1], &d.result, 1);
+ int l = utf8ErrorMsg.length();
+ write(d.fd[1], &l, sizeof(int));
+ write(d.fd[1], utf8ErrorMsg.data(), l);
+ close(d.fd[1]);
+ exit(255);
+}
+
+static void setup_tty( const char* tty )
+{
+ if( tty == NULL || *tty == '\0' )
+ return;
+ int fd = open( tty, O_WRONLY );
+ if( fd < 0 )
+ {
+ perror( "kdeinit: couldn't open() tty" );
+ return;
+ }
+ if( dup2( fd, STDOUT_FILENO ) < 0 )
+ {
+ perror( "kdeinit: couldn't dup2() tty" );
+ close( fd );
+ return;
+ }
+ if( dup2( fd, STDERR_FILENO ) < 0 )
+ {
+ perror( "kdeinit: couldn't dup2() tty" );
+ close( fd );
+ return;
+ }
+ close( fd );
+}
+
+// from kdecore/netwm.cpp
+static int get_current_desktop( Display* disp )
+{
+ int desktop = 0; // no desktop by default
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11 // Only X11 supports multiple desktops
+ Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP", False );
+ Atom type_ret;
+ int format_ret;
+ unsigned char *data_ret;
+ unsigned long nitems_ret, unused;
+ if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop,
+ 0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret )
+ == Success)
+ {
+ if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1)
+ desktop = *((long *) data_ret) + 1;
+ if (data_ret)
+ XFree ((char*) data_ret);
+ }
+#endif
+ return desktop;
+}
+
+// var has to be e.g. "DISPLAY=", i.e. with =
+const char* get_env_var( const char* var, int envc, const char* envs )
+{
+ if( envc > 0 )
+ { // get the var from envs
+ const char* env_l = envs;
+ int ln = strlen( var );
+ for (int i = 0; i < envc; i++)
+ {
+ if( strncmp( env_l, var, ln ) == 0 )
+ return env_l + ln;
+ while(*env_l != 0) env_l++;
+ env_l++;
+ }
+ }
+ return NULL;
+}
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
+static void init_startup_info( KStartupInfoId& id, const char* bin,
+ int envc, const char* envs )
+{
+ const char* dpy = get_env_var( DISPLAY"=", envc, envs );
+ // this may be called in a child, so it can't use display open using X11display
+ // also needed for multihead
+ X11_startup_notify_display = XOpenDisplay( dpy );
+ if( X11_startup_notify_display == NULL )
+ return;
+ X11_startup_notify_fd = XConnectionNumber( X11_startup_notify_display );
+ KStartupInfoData data;
+ int desktop = get_current_desktop( X11_startup_notify_display );
+ data.setDesktop( desktop );
+ data.setBin( bin );
+ KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
+ XFlush( X11_startup_notify_display );
+}
+
+static void complete_startup_info( KStartupInfoId& id, pid_t pid )
+{
+ if( X11_startup_notify_display == NULL )
+ return;
+ if( pid == 0 ) // failure
+ KStartupInfo::sendFinishX( X11_startup_notify_display, id );
+ else
+ {
+ KStartupInfoData data;
+ data.addPid( pid );
+ data.setHostname();
+ KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
+ }
+ XCloseDisplay( X11_startup_notify_display );
+ X11_startup_notify_display = NULL;
+ X11_startup_notify_fd = -1;
+}
+#endif
+
+QCString execpath_avoid_loops( const QCString& exec, int envc, const char* envs, bool avoid_loops )
+{
+ QStringList paths;
+ if( envc > 0 ) /* use the passed environment */
+ {
+ const char* path = get_env_var( "PATH=", envc, envs );
+ if( path != NULL )
+ paths = QStringList::split( QRegExp( "[:\b]" ), path, true );
+ }
+ else
+ paths = QStringList::split( QRegExp( "[:\b]" ), getenv( "PATH" ), true );
+ QCString execpath = QFile::encodeName(
+ s_instance->dirs()->findExe( exec, paths.join( QString( ":" ))));
+ if( avoid_loops && !execpath.isEmpty())
+ {
+ int pos = execpath.findRev( '/' );
+ QString bin_path = execpath.left( pos );
+ for( QStringList::Iterator it = paths.begin();
+ it != paths.end();
+ ++it )
+ if( ( *it ) == bin_path || ( *it ) == bin_path + '/' )
+ {
+ paths.remove( it );
+ break; // -->
+ }
+ execpath = QFile::encodeName(
+ s_instance->dirs()->findExe( exec, paths.join( QString( ":" ))));
+ }
+ return execpath;
+}
+
+#ifdef KDEINIT_OOM_PROTECT
+static int oom_pipe = -1;
+
+static void oom_protect_sighandler( int ) {
+}
+
+static void reset_oom_protect() {
+ if( oom_pipe <= 0 )
+ return;
+ struct sigaction act, oldact;
+ act.sa_handler = oom_protect_sighandler;
+ act.sa_flags = 0;
+ sigemptyset( &act.sa_mask );
+ sigaction( SIGUSR1, &act, &oldact );
+ sigset_t sigs, oldsigs;
+ sigemptyset( &sigs );
+ sigaddset( &sigs, SIGUSR1 );
+ sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
+ pid_t pid = getpid();
+ if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) {
+ sigsuspend( &oldsigs ); // wait for the signal to come
+ }
+ sigprocmask( SIG_SETMASK, &oldsigs, NULL );
+ sigaction( SIGUSR1, &oldact, NULL );
+ close( oom_pipe );
+ oom_pipe = -1;
+}
+#else
+static void reset_oom_protect() {
+}
+#endif
+
+static pid_t launch(int argc, const char *_name, const char *args,
+ const char *cwd=0, int envc=0, const char *envs=0,
+ bool reset_env = false,
+ const char *tty=0, bool avoid_loops = false,
+ const char* startup_id_str = "0" )
+{
+ int launcher = 0;
+ QCString lib;
+ QCString name;
+ QCString exec;
+
+ if (strcmp(_name, "klauncher") == 0) {
+ /* klauncher is launched in a special way:
+ * It has a communication socket on LAUNCHER_FD
+ */
+ if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher))
+ {
+ perror("kdeinit: socketpair() failed!\n");
+ exit(255);
+ }
+ launcher = 1;
+ }
+
+ QCString libpath;
+ QCString execpath;
+ if (_name[0] != '/')
+ {
+ /* Relative name without '.la' */
+ name = _name;
+ lib = name + ".la";
+ exec = name;
+ libpath = QFile::encodeName(KLibLoader::findLibrary( lib, s_instance ));
+ execpath = execpath_avoid_loops( exec, envc, envs, avoid_loops );
+ }
+ else
+ {
+ lib = _name;
+ name = _name;
+ name = name.mid( name.findRev('/') + 1);
+ exec = _name;
+ if (lib.right(3) == ".la")
+ libpath = lib;
+ else
+ execpath = exec;
+ }
+ if (!args)
+ {
+ argc = 1;
+ }
+
+ if (0 > pipe(d.fd))
+ {
+ perror("kdeinit: pipe() failed!\n");
+ d.result = 3;
+ d.errorMsg = i18n("Unable to start new process.\n"
+ "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.").utf8();
+ close(d.fd[0]);
+ close(d.fd[1]);
+ d.fork = 0;
+ return d.fork;
+ }
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ KStartupInfoId startup_id;
+ startup_id.initId( startup_id_str );
+ if( !startup_id.none())
+ init_startup_info( startup_id, name, envc, envs );
+#endif
+
+ d.errorMsg = 0;
+ d.fork = fork();
+ switch(d.fork) {
+ case -1:
+ perror("kdeinit: fork() failed!\n");
+ d.result = 3;
+ d.errorMsg = i18n("Unable to create new process.\n"
+ "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.").utf8();
+ close(d.fd[0]);
+ close(d.fd[1]);
+ d.fork = 0;
+ break;
+ case 0:
+ /** Child **/
+ close(d.fd[0]);
+ close_fds();
+ if (launcher)
+ {
+ if (d.fd[1] == LAUNCHER_FD)
+ {
+ d.fd[1] = dup(d.fd[1]); // Evacuate from LAUNCHER_FD
+ }
+ if (d.launcher[1] != LAUNCHER_FD)
+ {
+ dup2( d.launcher[1], LAUNCHER_FD); // Make sure the socket has fd LAUNCHER_FD
+ close( d.launcher[1] );
+ }
+ close( d.launcher[0] );
+ }
+ reset_oom_protect();
+
+ if (cwd && *cwd)
+ chdir(cwd);
+
+ if( reset_env ) // KWRAPPER/SHELL
+ {
+
+ QStrList unset_envs;
+ for( int tmp_env_count = 0;
+ environ[tmp_env_count];
+ tmp_env_count++)
+ unset_envs.append( environ[ tmp_env_count ] );
+ for( QStrListIterator it( unset_envs );
+ it.current() != NULL ;
+ ++it )
+ {
+ QCString tmp( it.current());
+ int pos = tmp.find( '=' );
+ if( pos >= 0 )
+ unsetenv( tmp.left( pos ));
+ }
+ }
+
+ for (int i = 0; i < envc; i++)
+ {
+ putenv((char *)envs);
+ while(*envs != 0) envs++;
+ envs++;
+ }
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if( startup_id.none())
+ KStartupInfo::resetStartupEnv();
+ else
+ startup_id.setupStartupEnv();
+#endif
+ {
+ int r;
+ QCString procTitle;
+ d.argv = (char **) malloc(sizeof(char *) * (argc+1));
+ d.argv[0] = (char *) _name;
+ for (int i = 1; i < argc; i++)
+ {
+ d.argv[i] = (char *) args;
+ procTitle += " ";
+ procTitle += (char *) args;
+ while(*args != 0) args++;
+ args++;
+ }
+ d.argv[argc] = 0;
+
+ /** Give the process a new name **/
+#ifdef Q_OS_LINUX
+ /* set the process name, so that killall works like intended */
+ r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0);
+ if ( r == 0 )
+ kdeinit_setproctitle( "%s [kdeinit]%s", name.data(), procTitle.data() ? procTitle.data() : "" );
+ else
+ kdeinit_setproctitle( "kdeinit: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
+#else
+ kdeinit_setproctitle( "kdeinit: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
+#endif
+ }
+
+ d.handle = 0;
+ if (libpath.isEmpty() && execpath.isEmpty())
+ {
+ QString errorMsg = i18n("Could not find '%1' executable.").arg(QFile::decodeName(_name));
+ exitWithErrorMsg(errorMsg);
+ }
+
+ if ( getenv("KDE_IS_PRELINKED") && !execpath.isEmpty() && !launcher)
+ libpath.truncate(0);
+
+ if ( !libpath.isEmpty() )
+ {
+ d.handle = lt_dlopen( QFile::encodeName(libpath) );
+ if (!d.handle )
+ {
+ const char * ltdlError = lt_dlerror();
+ if (execpath.isEmpty())
+ {
+ // Error
+ QString errorMsg = i18n("Could not open library '%1'.\n%2").arg(QFile::decodeName(libpath))
+ .arg(ltdlError ? QFile::decodeName(ltdlError) : i18n("Unknown error"));
+ exitWithErrorMsg(errorMsg);
+ }
+ else
+ {
+ // Print warning
+ fprintf(stderr, "Could not open library %s: %s\n", lib.data(), ltdlError != 0 ? ltdlError : "(null)" );
+ }
+ }
+ }
+ lt_dlopen_flag = d.lt_dlopen_flag;
+ if (!d.handle )
+ {
+ d.result = 2; // Try execing
+ write(d.fd[1], &d.result, 1);
+
+ // We set the close on exec flag.
+ // Closing of d.fd[1] indicates that the execvp succeeded!
+ fcntl(d.fd[1], F_SETFD, FD_CLOEXEC);
+
+ setup_tty( tty );
+
+ execvp(execpath.data(), d.argv);
+ d.result = 1; // Error
+ write(d.fd[1], &d.result, 1);
+ close(d.fd[1]);
+ exit(255);
+ }
+
+ d.sym = lt_dlsym( d.handle, "kdeinitmain");
+ if (!d.sym )
+ {
+ d.sym = lt_dlsym( d.handle, "kdemain" );
+ if ( !d.sym )
+ {
+#if ! KDE_IS_VERSION( 3, 90, 0 )
+ d.sym = lt_dlsym( d.handle, "main");
+#endif
+ if (!d.sym )
+ {
+ const char * ltdlError = lt_dlerror();
+ fprintf(stderr, "Could not find kdemain: %s\n", ltdlError != 0 ? ltdlError : "(null)" );
+ QString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2").arg(libpath)
+ .arg(ltdlError ? QFile::decodeName(ltdlError) : i18n("Unknown error"));
+ exitWithErrorMsg(errorMsg);
+ }
+ }
+ }
+
+ d.result = 0; // Success
+ write(d.fd[1], &d.result, 1);
+ close(d.fd[1]);
+
+ d.func = (int (*)(int, char *[])) d.sym;
+ if (d.debug_wait)
+ {
+ fprintf(stderr, "kdeinit: Suspending process\n"
+ "kdeinit: 'gdb kdeinit %d' to debug\n"
+ "kdeinit: 'kill -SIGCONT %d' to continue\n",
+ getpid(), getpid());
+ kill(getpid(), SIGSTOP);
+ }
+ else
+ {
+ setup_tty( tty );
+ }
+
+ exit( d.func(argc, d.argv)); /* Launch! */
+
+ break;
+ default:
+ /** Parent **/
+ close(d.fd[1]);
+ if (launcher)
+ {
+ close(d.launcher[1]);
+ d.launcher_pid = d.fork;
+ }
+ bool exec = false;
+ for(;;)
+ {
+ d.n = read(d.fd[0], &d.result, 1);
+ if (d.n == 1)
+ {
+ if (d.result == 2)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "Could not load library! Trying exec....\n");
+#endif
+ exec = true;
+ continue;
+ }
+ if (d.result == 3)
+ {
+ int l = 0;
+ d.n = read(d.fd[0], &l, sizeof(int));
+ if (d.n == sizeof(int))
+ {
+ QCString tmp;
+ tmp.resize(l+1);
+ d.n = read(d.fd[0], tmp.data(), l);
+ tmp[l] = 0;
+ if (d.n == l)
+ d.errorMsg = tmp;
+ }
+ }
+ // Finished
+ break;
+ }
+ if (d.n == -1)
+ {
+ if (errno == ECHILD) { // a child died.
+ continue;
+ }
+ if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read
+ continue;
+ }
+ }
+ if (exec)
+ {
+ d.result = 0;
+ break;
+ }
+ if (d.n == 0)
+ {
+ perror("kdeinit: Pipe closed unexpectedly");
+ d.result = 1; // Error
+ break;
+ }
+ perror("kdeinit: Error reading from pipe");
+ d.result = 1; // Error
+ break;
+ }
+ close(d.fd[0]);
+ if (launcher && (d.result == 0))
+ {
+ // Trader launched successful
+ d.launcher_pid = d.fork;
+ }
+ }
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if( !startup_id.none())
+ {
+ if( d.fork && d.result == 0 ) // launched successfully
+ complete_startup_info( startup_id, d.fork );
+ else // failure, cancel ASN
+ complete_startup_info( startup_id, 0 );
+ }
+#endif
+ return d.fork;
+}
+
+static void sig_child_handler(int)
+{
+ /*
+ * Write into the pipe of death.
+ * This way we are sure that we return from the select()
+ *
+ * A signal itself causes select to return as well, but
+ * this creates a race-condition in case the signal arrives
+ * just before we enter the select.
+ */
+ char c = 0;
+ write(d.deadpipe[1], &c, 1);
+}
+
+static void init_signals()
+{
+ struct sigaction act;
+ long options;
+
+ if (pipe(d.deadpipe) != 0)
+ {
+ perror("kdeinit: Aborting. Can't create pipe: ");
+ exit(255);
+ }
+
+ options = fcntl(d.deadpipe[0], F_GETFL);
+ if (options == -1)
+ {
+ perror("kdeinit: Aborting. Can't make pipe non-blocking: ");
+ exit(255);
+ }
+
+ if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1)
+ {
+ perror("kdeinit: Aborting. Can't make pipe non-blocking: ");
+ exit(255);
+ }
+
+ /*
+ * A SIGCHLD handler is installed which sends a byte into the
+ * pipe of death. This is to ensure that a dying child causes
+ * an exit from select().
+ */
+ act.sa_handler=sig_child_handler;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
+ act.sa_flags = SA_NOCLDSTOP;
+
+ // CC: take care of SunOS which automatically restarts interrupted system
+ // calls (and thus does not have SA_RESTART)
+
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+ sigaction( SIGCHLD, &act, 0L);
+
+ act.sa_handler=SIG_IGN;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGPIPE);
+ sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
+ act.sa_flags = 0;
+ sigaction( SIGPIPE, &act, 0L);
+}
+
+static void init_kdeinit_socket()
+{
+ struct sockaddr_un sa;
+ struct sockaddr_un sa_old;
+ kde_socklen_t socklen;
+ long options;
+ const char *home_dir = getenv("HOME");
+ int max_tries = 10;
+ if (!home_dir || !home_dir[0])
+ {
+ fprintf(stderr, "kdeinit: Aborting. $HOME not set!");
+ exit(255);
+ }
+ chdir(home_dir);
+
+ {
+ QCString path = home_dir;
+ QCString readOnly = getenv("KDE_HOME_READONLY");
+ if (access(path.data(), R_OK|W_OK))
+ {
+ if (errno == ENOENT)
+ {
+ fprintf(stderr, "kdeinit: Aborting. $HOME directory (%s) does not exist.\n", path.data());
+ exit(255);
+ }
+ else if (readOnly.isEmpty())
+ {
+ fprintf(stderr, "kdeinit: Aborting. No write access to $HOME directory (%s).\n", path.data());
+ exit(255);
+ }
+ }
+ path = getenv("ICEAUTHORITY");
+ if (path.isEmpty())
+ {
+ path = home_dir;
+ path += "/.ICEauthority";
+ }
+ if (access(path.data(), R_OK|W_OK) && (errno != ENOENT))
+ {
+ fprintf(stderr, "kdeinit: Aborting. No write access to '%s'.\n", path.data());
+ exit(255);
+ }
+ }
+
+ /** Test if socket file is already present
+ * note that access() resolves symlinks, and so we check the actual
+ * socket file if it exists
+ */
+ if (access(sock_file, W_OK) == 0)
+ {
+ int s;
+ struct sockaddr_un server;
+
+// fprintf(stderr, "kdeinit: Warning, socket_file already exists!\n");
+ /*
+ * create the socket stream
+ */
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ perror("socket() failed: ");
+ exit(255);
+ }
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, sock_file);
+ socklen = sizeof(server);
+
+ if(connect(s, (struct sockaddr *)&server, socklen) == 0)
+ {
+ fprintf(stderr, "kdeinit: Shutting down running client.\n");
+ klauncher_header request_header;
+ request_header.cmd = LAUNCHER_TERMINATE_KDEINIT;
+ request_header.arg_length = 0;
+ write(s, &request_header, sizeof(request_header));
+ sleep(1); // Give it some time
+ }
+ close(s);
+ }
+
+ /** Delete any stale socket file (and symlink) **/
+ unlink(sock_file);
+ unlink(sock_file_old);
+
+ /** create socket **/
+ d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (d.wrapper < 0)
+ {
+ perror("kdeinit: Aborting. socket() failed: ");
+ exit(255);
+ }
+
+ options = fcntl(d.wrapper, F_GETFL);
+ if (options == -1)
+ {
+ perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper);
+ exit(255);
+ }
+
+ if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1)
+ {
+ perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper);
+ exit(255);
+ }
+
+ while (1) {
+ /** bind it **/
+ socklen = sizeof(sa);
+ memset(&sa, 0, socklen);
+ sa.sun_family = AF_UNIX;
+ strcpy(sa.sun_path, sock_file);
+ if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0)
+ {
+ if (max_tries == 0) {
+ perror("kdeinit: Aborting. bind() failed: ");
+ fprintf(stderr, "Could not bind to socket '%s'\n", sock_file);
+ close(d.wrapper);
+ exit(255);
+ }
+ max_tries--;
+ } else
+ break;
+ }
+
+ /** set permissions **/
+ if (chmod(sock_file, 0600) != 0)
+ {
+ perror("kdeinit: Aborting. Can't set permissions on socket: ");
+ fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
+ unlink(sock_file);
+ close(d.wrapper);
+ exit(255);
+ }
+
+ if(listen(d.wrapper, SOMAXCONN) < 0)
+ {
+ perror("kdeinit: Aborting. listen() failed: ");
+ unlink(sock_file);
+ close(d.wrapper);
+ exit(255);
+ }
+
+ /** create compatibility socket **/
+ d.wrapper_old = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (d.wrapper_old < 0)
+ {
+ // perror("kdeinit: Aborting. socket() failed: ");
+ return;
+ }
+
+ options = fcntl(d.wrapper_old, F_GETFL);
+ if (options == -1)
+ {
+ // perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+
+ if (fcntl(d.wrapper_old, F_SETFL, options | O_NONBLOCK) == -1)
+ {
+ // perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+
+ max_tries = 10;
+ while (1) {
+ /** bind it **/
+ socklen = sizeof(sa_old);
+ memset(&sa_old, 0, socklen);
+ sa_old.sun_family = AF_UNIX;
+ strcpy(sa_old.sun_path, sock_file_old);
+ if(bind(d.wrapper_old, (struct sockaddr *)&sa_old, socklen) != 0)
+ {
+ if (max_tries == 0) {
+ // perror("kdeinit: Aborting. bind() failed: ");
+ fprintf(stderr, "Could not bind to socket '%s'\n", sock_file_old);
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+ max_tries--;
+ } else
+ break;
+ }
+
+ /** set permissions **/
+ if (chmod(sock_file_old, 0600) != 0)
+ {
+ fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
+ unlink(sock_file_old);
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+
+ if(listen(d.wrapper_old, SOMAXCONN) < 0)
+ {
+ // perror("kdeinit: Aborting. listen() failed: ");
+ unlink(sock_file_old);
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ }
+}
+
+/*
+ * Read 'len' bytes from 'sock' into buffer.
+ * returns 0 on success, -1 on failure.
+ */
+static int read_socket(int sock, char *buffer, int len)
+{
+ ssize_t result;
+ int bytes_left = len;
+ while ( bytes_left > 0)
+ {
+ result = read(sock, buffer, bytes_left);
+ if (result > 0)
+ {
+ buffer += result;
+ bytes_left -= result;
+ }
+ else if (result == 0)
+ return -1;
+ else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
+ return -1;
+ }
+ return 0;
+}
+
+static void WaitPid( pid_t waitForPid)
+{
+ int result;
+ while(1)
+ {
+ result = waitpid(waitForPid, &d.exit_status, 0);
+ if ((result == -1) && (errno == ECHILD))
+ return;
+ }
+}
+
+static void launcher_died()
+{
+ if (!d.launcher_ok)
+ {
+ /* This is bad. */
+ fprintf(stderr, "kdeinit: Communication error with launcher. Exiting!\n");
+ ::exit(255);
+ return;
+ }
+
+ // KLauncher died... restart
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: KLauncher died unexpectedly.\n");
+#endif
+ // Make sure it's really dead.
+ if (d.launcher_pid)
+ {
+ kill(d.launcher_pid, SIGKILL);
+ sleep(1); // Give it some time
+ }
+
+ d.launcher_ok = false;
+ d.launcher_pid = 0;
+ close(d.launcher[0]);
+ d.launcher[0] = -1;
+
+ pid_t pid = launch( 1, "klauncher", 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Relaunching KLauncher, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+}
+
+static void handle_launcher_request(int sock = -1)
+{
+ bool launcher = false;
+ if (sock < 0)
+ {
+ sock = d.launcher[0];
+ launcher = true;
+ }
+
+ klauncher_header request_header;
+ char *request_data = 0L;
+ int result = read_socket(sock, (char *) &request_header, sizeof(request_header));
+ if (result != 0)
+ {
+ if (launcher)
+ launcher_died();
+ return;
+ }
+
+ if ( request_header.arg_length != 0 )
+ {
+ request_data = (char *) malloc(request_header.arg_length);
+
+ result = read_socket(sock, request_data, request_header.arg_length);
+ if (result != 0)
+ {
+ if (launcher)
+ launcher_died();
+ free(request_data);
+ return;
+ }
+ }
+
+ if (request_header.cmd == LAUNCHER_OK)
+ {
+ d.launcher_ok = true;
+ }
+ else if (request_header.arg_length &&
+ ((request_header.cmd == LAUNCHER_EXEC) ||
+ (request_header.cmd == LAUNCHER_EXT_EXEC) ||
+ (request_header.cmd == LAUNCHER_SHELL ) ||
+ (request_header.cmd == LAUNCHER_KWRAPPER) ||
+ (request_header.cmd == LAUNCHER_EXEC_NEW)))
+ {
+ pid_t pid;
+ klauncher_header response_header;
+ long response_data;
+ long l;
+ memcpy( &l, request_data, sizeof( long ));
+ int argc = l;
+ const char *name = request_data + sizeof(long);
+ const char *args = name + strlen(name) + 1;
+ const char *cwd = 0;
+ int envc = 0;
+ const char *envs = 0;
+ const char *tty = 0;
+ int avoid_loops = 0;
+ const char *startup_id_str = "0";
+
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Got %s '%s' from %s.\n",
+ (request_header.cmd == LAUNCHER_EXEC ? "EXEC" :
+ (request_header.cmd == LAUNCHER_EXT_EXEC ? "EXT_EXEC" :
+ (request_header.cmd == LAUNCHER_EXEC_NEW ? "EXEC_NEW" :
+ (request_header.cmd == LAUNCHER_SHELL ? "SHELL" : "KWRAPPER" )))),
+ name, launcher ? "launcher" : "socket" );
+#endif
+
+ const char *arg_n = args;
+ for(int i = 1; i < argc; i++)
+ {
+ arg_n = arg_n + strlen(arg_n) + 1;
+ }
+
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER )
+ {
+ // Shell or kwrapper
+ cwd = arg_n; arg_n += strlen(cwd) + 1;
+ }
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
+ || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
+ {
+ memcpy( &l, arg_n, sizeof( long ));
+ envc = l;
+ arg_n += sizeof(long);
+ envs = arg_n;
+ for(int i = 0; i < envc; i++)
+ {
+ arg_n = arg_n + strlen(arg_n) + 1;
+ }
+ if( request_header.cmd == LAUNCHER_KWRAPPER )
+ {
+ tty = arg_n;
+ arg_n += strlen( tty ) + 1;
+ }
+ }
+
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
+ || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
+ {
+ memcpy( &l, arg_n, sizeof( long ));
+ avoid_loops = l;
+ arg_n += sizeof( long );
+ }
+
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
+ || request_header.cmd == LAUNCHER_EXT_EXEC )
+ {
+ startup_id_str = arg_n;
+ arg_n += strlen( startup_id_str ) + 1;
+ }
+
+ if ((request_header.arg_length > (arg_n - request_data)) &&
+ (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ))
+ {
+ // Optional cwd
+ cwd = arg_n; arg_n += strlen(cwd) + 1;
+ }
+
+ if ((arg_n - request_data) != request_header.arg_length)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: EXEC request has invalid format.\n");
+#endif
+ free(request_data);
+ d.debug_wait = false;
+ return;
+ }
+
+ // support for the old a bit broken way of setting DISPLAY for multihead
+ QCString olddisplay = getenv(DISPLAY);
+ QCString kdedisplay = getenv("KDE_DISPLAY");
+ bool reset_display = (! olddisplay.isEmpty() &&
+ ! kdedisplay.isEmpty() &&
+ olddisplay != kdedisplay);
+
+ if (reset_display)
+ setenv(DISPLAY, kdedisplay, true);
+
+ pid = launch( argc, name, args, cwd, envc, envs,
+ request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER,
+ tty, avoid_loops, startup_id_str );
+
+ if (reset_display) {
+ unsetenv("KDE_DISPLAY");
+ setenv(DISPLAY, olddisplay, true);
+ }
+
+ if (pid && (d.result == 0))
+ {
+ response_header.cmd = LAUNCHER_OK;
+ response_header.arg_length = sizeof(response_data);
+ response_data = pid;
+ write(sock, &response_header, sizeof(response_header));
+ write(sock, &response_data, response_header.arg_length);
+ }
+ else
+ {
+ int l = d.errorMsg.length();
+ if (l) l++; // Include trailing null.
+ response_header.cmd = LAUNCHER_ERROR;
+ response_header.arg_length = l;
+ write(sock, &response_header, sizeof(response_header));
+ if (l)
+ write(sock, d.errorMsg.data(), l);
+ }
+ d.debug_wait = false;
+ }
+ else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV)
+ {
+ const char *env_name;
+ const char *env_value;
+ env_name = request_data;
+ env_value = env_name + strlen(env_name) + 1;
+
+#ifndef NDEBUG
+ if (launcher)
+ fprintf(stderr, "kdeinit: Got SETENV '%s=%s' from klauncher.\n", env_name, env_value);
+ else
+ fprintf(stderr, "kdeinit: Got SETENV '%s=%s' from socket.\n", env_name, env_value);
+#endif
+
+ if ( request_header.arg_length !=
+ (int) (strlen(env_name) + strlen(env_value) + 2))
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: SETENV request has invalid format.\n");
+#endif
+ free(request_data);
+ return;
+ }
+ setenv( env_name, env_value, 1);
+ }
+ else if (request_header.cmd == LAUNCHER_TERMINATE_KDE)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,"kdeinit: terminate KDE.\n");
+#endif
+#ifdef Q_WS_X11
+ kdeinit_xio_errhandler( 0L );
+#endif
+ }
+ else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,"kdeinit: Killing kdeinit/klauncher.\n");
+#endif
+ if (d.launcher_pid)
+ kill(d.launcher_pid, SIGTERM);
+ if (d.my_pid)
+ kill(d.my_pid, SIGTERM);
+ }
+ else if (request_header.cmd == LAUNCHER_DEBUG_WAIT)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,"kdeinit: Debug wait activated.\n");
+#endif
+ d.debug_wait = true;
+ }
+ if (request_data)
+ free(request_data);
+}
+
+static void handle_requests(pid_t waitForPid)
+{
+ int max_sock = d.wrapper;
+ if (d.wrapper_old > max_sock)
+ max_sock = d.wrapper_old;
+ if (d.launcher_pid && (d.launcher[0] > max_sock))
+ max_sock = d.launcher[0];
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef _WS_X11
+ if (X11fd > max_sock)
+ max_sock = X11fd;
+#endif
+ max_sock++;
+
+ while(1)
+ {
+ fd_set rd_set;
+ fd_set wr_set;
+ fd_set e_set;
+ int result;
+ pid_t exit_pid;
+ char c;
+
+ /* Flush the pipe of death */
+ while( read(d.deadpipe[0], &c, 1) == 1);
+
+ /* Handle dying children */
+ do {
+ exit_pid = waitpid(-1, 0, WNOHANG);
+ if (exit_pid > 0)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: PID %ld terminated.\n", (long) exit_pid);
+#endif
+ if (waitForPid && (exit_pid == waitForPid))
+ return;
+
+ if (d.launcher_pid)
+ {
+ // TODO send process died message
+ klauncher_header request_header;
+ long request_data[2];
+ request_header.cmd = LAUNCHER_DIED;
+ request_header.arg_length = sizeof(long) * 2;
+ request_data[0] = exit_pid;
+ request_data[1] = 0; /* not implemented yet */
+ write(d.launcher[0], &request_header, sizeof(request_header));
+ write(d.launcher[0], request_data, request_header.arg_length);
+ }
+ }
+ }
+ while( exit_pid > 0);
+
+ FD_ZERO(&rd_set);
+ FD_ZERO(&wr_set);
+ FD_ZERO(&e_set);
+
+ if (d.launcher_pid)
+ {
+ FD_SET(d.launcher[0], &rd_set);
+ }
+ FD_SET(d.wrapper, &rd_set);
+ if (d.wrapper_old)
+ {
+ FD_SET(d.wrapper_old, &rd_set);
+ }
+ FD_SET(d.deadpipe[0], &rd_set);
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if(X11fd >= 0) FD_SET(X11fd, &rd_set);
+#endif
+
+ result = select(max_sock, &rd_set, &wr_set, &e_set, 0);
+
+ /* Handle wrapper request */
+ if ((result > 0) && (FD_ISSET(d.wrapper, &rd_set)))
+ {
+ struct sockaddr_un client;
+ kde_socklen_t sClient = sizeof(client);
+ int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient);
+ if (sock >= 0)
+ {
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+ if( FcGetVersion() < 20390 && !FcConfigUptoDate(NULL))
+ FcInitReinitialize();
+#endif
+ if (fork() == 0)
+ {
+ close_fds();
+ reset_oom_protect();
+ handle_launcher_request(sock);
+ exit(255); /* Terminate process. */
+ }
+ close(sock);
+ }
+ }
+ if ((result > 0) && (FD_ISSET(d.wrapper_old, &rd_set)))
+ {
+ struct sockaddr_un client;
+ kde_socklen_t sClient = sizeof(client);
+ int sock = accept(d.wrapper_old, (struct sockaddr *)&client, &sClient);
+ if (sock >= 0)
+ {
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+ if( FcGetVersion() < 20390 && !FcConfigUptoDate(NULL))
+ FcInitReinitialize();
+#endif
+ if (fork() == 0)
+ {
+ close_fds();
+ reset_oom_protect();
+ handle_launcher_request(sock);
+ exit(255); /* Terminate process. */
+ }
+ close(sock);
+ }
+ }
+
+ /* Handle launcher request */
+ if ((result > 0) && (d.launcher_pid) && (FD_ISSET(d.launcher[0], &rd_set)))
+ {
+ handle_launcher_request();
+ if (waitForPid == d.launcher_pid)
+ return;
+ }
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+ /* Look for incoming X11 events */
+ if((result > 0) && (X11fd >= 0))
+ {
+ if(FD_ISSET(X11fd,&rd_set))
+ {
+ if (X11display != 0) {
+ XEvent event_return;
+ while (XPending(X11display))
+ XNextEvent(X11display, &event_return);
+ }
+ }
+ }
+#endif
+ }
+}
+
+static void kdeinit_library_path()
+{
+ QStringList ltdl_library_path =
+ QStringList::split(':', QFile::decodeName(getenv("LTDL_LIBRARY_PATH")));
+ QStringList ld_library_path =
+ QStringList::split(':', QFile::decodeName(getenv("LD_LIBRARY_PATH")));
+
+ QCString extra_path;
+ QStringList candidates = s_instance->dirs()->resourceDirs("lib");
+ for (QStringList::ConstIterator it = candidates.begin();
+ it != candidates.end();
+ it++)
+ {
+ QString d = *it;
+ if (ltdl_library_path.contains(d))
+ continue;
+ if (ld_library_path.contains(d))
+ continue;
+ if (d[d.length()-1] == '/')
+ {
+ d.truncate(d.length()-1);
+ if (ltdl_library_path.contains(d))
+ continue;
+ if (ld_library_path.contains(d))
+ continue;
+ }
+ if ((d == "/lib") || (d == "/usr/lib"))
+ continue;
+
+ QCString dir = QFile::encodeName(d);
+
+ if (access(dir, R_OK))
+ continue;
+
+ if ( !extra_path.isEmpty())
+ extra_path += ":";
+ extra_path += dir;
+ }
+
+ if (lt_dlinit())
+ {
+ const char * ltdlError = lt_dlerror();
+ fprintf(stderr, "can't initialize dynamic loading: %s\n", ltdlError != 0 ? ltdlError : "(null)" );
+ }
+ if (!extra_path.isEmpty())
+ lt_dlsetsearchpath(extra_path.data());
+
+ QCString display = getenv(DISPLAY);
+ if (display.isEmpty())
+ {
+ fprintf(stderr, "kdeinit: Aborting. $"DISPLAY" is not set.\n");
+ exit(255);
+ }
+ int i;
+ if((i = display.findRev('.')) > display.findRev(':') && i >= 0)
+ display.truncate(i);
+
+ QCString socketName = QFile::encodeName(locateLocal("socket", QString("kdeinit-%1").arg(display), s_instance));
+ if (socketName.length() >= MAX_SOCK_FILE)
+ {
+ fprintf(stderr, "kdeinit: Aborting. Socket name will be too long:\n");
+ fprintf(stderr, " '%s'\n", socketName.data());
+ exit(255);
+ }
+ strcpy(sock_file_old, socketName.data());
+
+ display.replace(":","_");
+ socketName = QFile::encodeName(locateLocal("socket", QString("kdeinit_%1").arg(display), s_instance));
+ if (socketName.length() >= MAX_SOCK_FILE)
+ {
+ fprintf(stderr, "kdeinit: Aborting. Socket name will be too long:\n");
+ fprintf(stderr, " '%s'\n", socketName.data());
+ exit(255);
+ }
+ strcpy(sock_file, socketName.data());
+}
+
+int kdeinit_xio_errhandler( Display *disp )
+{
+ // disp is 0L when KDE shuts down. We don't want those warnings then.
+
+ if ( disp )
+ qWarning( "kdeinit: Fatal IO error: client killed" );
+
+ if (sock_file[0])
+ {
+ /** Delete any stale socket file **/
+ unlink(sock_file);
+ }
+ if (sock_file_old[0])
+ {
+ /** Delete any stale socket file **/
+ unlink(sock_file_old);
+ }
+
+ // Don't kill our children in suicide mode, they may still be in use
+ if (d.suicide)
+ {
+ if (d.launcher_pid)
+ kill(d.launcher_pid, SIGTERM);
+ exit( 0 );
+ }
+
+ if ( disp )
+ qWarning( "kdeinit: sending SIGHUP to children." );
+
+ /* this should remove all children we started */
+ signal(SIGHUP, SIG_IGN);
+ kill(0, SIGHUP);
+
+ sleep(2);
+
+ if ( disp )
+ qWarning( "kdeinit: sending SIGTERM to children." );
+
+ /* and if they don't listen to us, this should work */
+ signal(SIGTERM, SIG_IGN);
+ kill(0, SIGTERM);
+
+ if ( disp )
+ qWarning( "kdeinit: Exit." );
+
+ exit( 0 );
+ return 0;
+}
+
+#ifdef Q_WS_X11
+int kdeinit_x_errhandler( Display *dpy, XErrorEvent *err )
+{
+#ifndef NDEBUG
+ char errstr[256];
+ // kdeinit almost doesn't use X, and therefore there shouldn't be any X error
+ XGetErrorText( dpy, err->error_code, errstr, 256 );
+ fprintf(stderr, "kdeinit: KDE detected X Error: %s %d\n"
+ " Major opcode: %d\n"
+ " Minor opcode: %d\n"
+ " Resource id: 0x%lx\n",
+ errstr, err->error_code, err->request_code, err->minor_code, err->resourceid );
+#else
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+#endif
+ return 0;
+}
+#endif
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+// needs to be done sooner than initXconnection() because of also opening
+// another X connection for startup notification purposes
+static void setupX()
+{
+ XSetIOErrorHandler(kdeinit_xio_errhandler);
+ XSetErrorHandler(kdeinit_x_errhandler);
+}
+
+// Borrowed from kdebase/kaudio/kaudioserver.cpp
+static int initXconnection()
+{
+ X11display = XOpenDisplay(NULL);
+ if ( X11display != 0 ) {
+ XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \
+ 0,
+ BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)),
+ BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: opened connection to %s\n", DisplayString(X11display));
+#endif
+ int fd = XConnectionNumber( X11display );
+ int on = 1;
+ (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on));
+ return fd;
+ } else
+ fprintf(stderr, "kdeinit: Can't connect to the X Server.\n" \
+ "kdeinit: Might not terminate at end of session.\n");
+
+ return -1;
+}
+#endif
+
+#ifdef __KCC
+/* One of my horrible hacks. KCC includes in each "main" function a call
+ to _main(), which is provided by the C++ runtime system. It is
+ responsible for calling constructors for some static objects. That must
+ be done only once, so _main() is guarded against multiple calls.
+ For unknown reasons the designers of KAI's libKCC decided it would be
+ a good idea to actually abort() when it's called multiple times, instead
+ of ignoring further calls. This breaks our mechanism of KLM's, because
+ most KLM's have a main() function which is called from us.
+ The "solution" is to simply define our own _main(), which ignores multiple
+ calls, which is easy, and which does the same work as KAI'c _main(),
+ which is difficult. Currently (KAI 4.0f) it only calls __call_ctors(void)
+ (a C++ function), but if that changes we need to change our's too.
+ (matz) */
+/*
+ Those 'unknown reasons' are C++ standard forbidding recursive calls to main()
+ or any means that would possibly allow that (e.g. taking address of main()).
+ The correct solution is not using main() as entry point for kdeinit modules,
+ but only kdemain().
+*/
+extern "C" void _main(void);
+extern "C" void __call_ctors__Fv(void);
+static int main_called = 0;
+void _main(void)
+{
+ if (main_called)
+ return;
+ main_called = 1;
+ __call_ctors__Fv ();
+}
+#endif
+
+static void secondary_child_handler(int)
+{
+ waitpid(-1, 0, WNOHANG);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ int i;
+ pid_t pid;
+ int launch_dcop = 1;
+ int launch_klauncher = 1;
+ int launch_kded = 1;
+ int keep_running = 1;
+ int new_startup = 0;
+ d.suicide = false;
+
+ /** Save arguments first... **/
+ char **safe_argv = (char **) malloc( sizeof(char *) * argc);
+ for(i = 0; i < argc; i++)
+ {
+ safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]);
+ if (strcmp(safe_argv[i], "--no-dcop") == 0)
+ launch_dcop = 0;
+ if (strcmp(safe_argv[i], "--no-klauncher") == 0)
+ launch_klauncher = 0;
+ if (strcmp(safe_argv[i], "--no-kded") == 0)
+ launch_kded = 0;
+ if (strcmp(safe_argv[i], "--suicide") == 0)
+ d.suicide = true;
+ if (strcmp(safe_argv[i], "--exit") == 0)
+ keep_running = 0;
+ if (strcmp(safe_argv[i], "--new-startup") == 0)
+ new_startup = 1;
+#ifdef KDEINIT_OOM_PROTECT
+ if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i+1<argc)
+ oom_pipe = atol(argv[i+1]);
+#endif
+ if (strcmp(safe_argv[i], "--help") == 0)
+ {
+ printf("Usage: kdeinit [options]\n");
+ // printf(" --no-dcop Do not start dcopserver\n");
+ // printf(" --no-klauncher Do not start klauncher\n");
+ printf(" --no-kded Do not start kded\n");
+ printf(" --suicide Terminate when no KDE applications are left running\n");
+ // printf(" --exit Terminate when kded has run\n");
+ exit(0);
+ }
+ }
+
+ pipe(d.initpipe);
+
+ // Fork here and let parent process exit.
+ // Parent process may only exit after all required services have been
+ // launched. (dcopserver/klauncher and services which start with '+')
+ signal( SIGCHLD, secondary_child_handler);
+ if (fork() > 0) // Go into background
+ {
+ close(d.initpipe[1]);
+ d.initpipe[1] = -1;
+ // wait till init is complete
+ char c;
+ while( read(d.initpipe[0], &c, 1) < 0);
+ // then exit;
+ close(d.initpipe[0]);
+ d.initpipe[0] = -1;
+ return 0;
+ }
+ close(d.initpipe[0]);
+ d.initpipe[0] = -1;
+ d.my_pid = getpid();
+
+ /** Make process group leader (for shutting down children later) **/
+ if(keep_running)
+ setsid();
+
+ /** Create our instance **/
+ s_instance = new KInstance("kdeinit");
+
+ /** Prepare to change process name **/
+ kdeinit_initsetproctitle(argc, argv, envp);
+ kdeinit_library_path();
+ // Don't make our instance the global instance
+ // (do it only after kdeinit_library_path, that one indirectly uses KConfig,
+ // which seems to be buggy and always use KGlobal instead of the maching KInstance)
+ KGlobal::_instance = 0L;
+ // don't change envvars before kdeinit_initsetproctitle()
+ unsetenv("LD_BIND_NOW");
+ unsetenv("DYLD_BIND_AT_LAUNCH");
+ KApplication::loadedByKdeinit = true;
+
+ d.maxname = strlen(argv[0]);
+ d.launcher_pid = 0;
+ d.wrapper = 0;
+ d.wrapper_old = 0;
+ d.debug_wait = false;
+ d.launcher_ok = false;
+ d.lt_dlopen_flag = lt_dlopen_flag;
+ lt_dlopen_flag |= LTDL_GLOBAL;
+ init_signals();
+#ifdef Q_WS_X11
+ setupX();
+#endif
+
+ if (keep_running)
+ {
+ /*
+ * Create ~/.kde/tmp-<hostname>/kdeinit-<display> socket for incoming wrapper
+ * requests.
+ */
+ init_kdeinit_socket();
+ }
+
+ if (launch_dcop)
+ {
+ if (d.suicide)
+ pid = launch( 3, "dcopserver", "--nosid\0--suicide" );
+ else
+ pid = launch( 2, "dcopserver", "--nosid" );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched DCOPServer, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+ WaitPid(pid);
+ if (!WIFEXITED(d.exit_status) || (WEXITSTATUS(d.exit_status) != 0))
+ {
+ fprintf(stderr, "kdeinit: DCOPServer could not be started, aborting.\n");
+ exit(1);
+ }
+ }
+#ifndef __CYGWIN__
+ if (!d.suicide && !getenv("KDE_IS_PRELINKED"))
+ {
+ QString konq = locate("lib", "libkonq.la", s_instance);
+ if (!konq.isEmpty())
+ (void) lt_dlopen(QFile::encodeName(konq).data());
+ }
+#endif
+ if (launch_klauncher)
+ {
+ if( new_startup )
+ pid = launch( 2, "klauncher", "--new-startup" );
+ else
+ pid = launch( 1, "klauncher", 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched KLauncher, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+ handle_requests(pid); // Wait for klauncher to be ready
+ }
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ X11fd = initXconnection();
+#endif
+
+ {
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+ if( FcGetVersion() < 20390 )
+ {
+ XftInit(0);
+ XftInitFtLibrary();
+ }
+#endif
+ QFont::initialize();
+ setlocale (LC_ALL, "");
+ setlocale (LC_NUMERIC, "C");
+#ifdef Q_WS_X11
+ if (XSupportsLocale ())
+ {
+ // Similar to QApplication::create_xim()
+ // but we need to use our own display
+ XOpenIM (X11display, 0, 0, 0);
+ }
+#endif
+ }
+
+ if (launch_kded)
+ {
+ if( new_startup )
+ pid = launch( 2, "kded", "--new-startup" );
+ else
+ pid = launch( 1, "kded", 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched KDED, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+ handle_requests(pid);
+ }
+
+ for(i = 1; i < argc; i++)
+ {
+ if (safe_argv[i][0] == '+')
+ {
+ pid = launch( 1, safe_argv[i]+1, 0);
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched '%s', pid = %ld result = %d\n", safe_argv[i]+1, (long) pid, d.result);
+#endif
+ handle_requests(pid);
+ }
+ else if (safe_argv[i][0] == '-'
+#ifdef KDEINIT_OOM_PROTECT
+ || isdigit(safe_argv[i][0])
+#endif
+ )
+ {
+ // Ignore
+ }
+ else
+ {
+ pid = launch( 1, safe_argv[i], 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched '%s', pid = %ld result = %d\n", safe_argv[i], (long) pid, d.result);
+#endif
+ }
+ }
+
+ /** Free arguments **/
+ for(i = 0; i < argc; i++)
+ {
+ free(safe_argv[i]);
+ }
+ free (safe_argv);
+
+ kdeinit_setproctitle("kdeinit Running...");
+
+ if (!keep_running)
+ return 0;
+
+ char c = 0;
+ write(d.initpipe[1], &c, 1); // Kdeinit is started.
+ close(d.initpipe[1]);
+ d.initpipe[1] = -1;
+
+ handle_requests(0);
+
+ return 0;
+}
+