diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch) | |
tree | 5ac38a06f3dde268dc7927dc155896926aaf7012 /kinit/kinit.cpp | |
download | tdelibs-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.cpp | 1912 |
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; +} + |