/*
* 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 <tqstring.h>
# include <tqfile.h>
# include <tqdatetime.h>
# include <tqfileinfo.h>
# include <tqtextstream.h>
# include <tqregexp.h>
# include <tqfont.h>
# include <kinstance.h>
# include <kstandarddirs.h>
# include <kglobal.h>
# include <kconfig.h>
# include <klibloader.h>
# include <kapplication.h>
# include <klocale.h>
# ifdef HAVE_SYS_PRCTL_H
# 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 <tdeversion.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(TDEINIT_USE_XFT) && defined(TDEINIT_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 ;
TQCString errorMsg ;
bool launcher_ok ;
bool suicide ;
} d ;
//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
# ifdef Q_WS_X11
extern " C " {
int tdeinit_xio_errhandler ( Display * ) ;
int tdeinit_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 TQString & errorMsg )
{
fprintf ( stderr , " %s \n " , errorMsg . local8Bit ( ) . data ( ) ) ;
TQCString 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 )
{
fprintf ( stderr , " tdeinit: couldn't open() %s: %s \n " , tty , strerror ( errno ) ) ;
return ;
}
if ( dup2 ( fd , STDOUT_FILENO ) < 0 )
{
fprintf ( stderr , " tdeinit: couldn't dup2() %s: %s \n " , tty , strerror ( errno ) ) ;
close ( fd ) ;
return ;
}
if ( dup2 ( fd , STDERR_FILENO ) < 0 )
{
fprintf ( stderr , " tdeinit: couldn't dup2() %s: %s \n " , tty , strerror ( errno ) ) ;
close ( fd ) ;
return ;
}
close ( fd ) ;
}
// from tdecore/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
TQCString execpath_avoid_loops ( const TQCString & exec , int envc , const char * envs , bool avoid_loops )
{
TQStringList paths ;
if ( envc > 0 ) /* use the passed environment */
{
const char * path = get_env_var ( " PATH= " , envc , envs ) ;
if ( path ! = NULL )
paths = TQStringList : : split ( TQRegExp ( " [: \b ] " ) , path , true ) ;
}
else
paths = TQStringList : : split ( TQRegExp ( " [: \b ] " ) , getenv ( " PATH " ) , true ) ;
TQCString execpath = TQFile : : encodeName (
s_instance - > dirs ( ) - > findExe ( exec , paths . join ( TQString ( " : " ) ) ) ) ;
if ( avoid_loops & & ! execpath . isEmpty ( ) )
{
int pos = execpath . findRev ( ' / ' ) ;
TQString bin_path = execpath . left ( pos ) ;
for ( TQStringList : : Iterator it = paths . begin ( ) ;
it ! = paths . end ( ) ;
+ + it )
if ( ( * it ) = = bin_path | | ( * it ) = = bin_path + ' / ' )
{
paths . remove ( it ) ;
break ; // -->
}
execpath = TQFile : : encodeName (
s_instance - > dirs ( ) - > findExe ( exec , paths . join ( TQString ( " : " ) ) ) ) ;
}
return execpath ;
}
# ifdef TDEINIT_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 ;
TQCString lib ;
TQCString name ;
TQCString 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 ( " tdeinit: socketpair() failed! \n " ) ;
exit ( 255 ) ;
}
launcher = 1 ;
}
TQCString libpath ;
TQCString execpath ;
if ( _name [ 0 ] ! = ' / ' )
{
/* Relative name without '.la' */
name = _name ;
lib = name + " .la " ;
exec = name ;
libpath = TQFile : : 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 ( " tdeinit: 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 ( " tdeinit: 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
{
TQStrList unset_envs ;
for ( int tmp_env_count = 0 ;
environ [ tmp_env_count ] ;
tmp_env_count + + )
unset_envs . append ( environ [ tmp_env_count ] ) ;
for ( TQStrListIterator it ( unset_envs ) ;
it . current ( ) ! = NULL ;
+ + it )
{
TQCString 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 ;
TQCString 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 HAVE_SYS_PRCTL_H
/* 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 )
tdeinit_setproctitle ( " %s [tdeinit]%s " , name . data ( ) , procTitle . data ( ) ? procTitle . data ( ) : " " ) ;
else
tdeinit_setproctitle ( " tdeinit: %s%s " , name . data ( ) , procTitle . data ( ) ? procTitle . data ( ) : " " ) ;
# else
tdeinit_setproctitle ( " tdeinit: %s%s " , name . data ( ) , procTitle . data ( ) ? procTitle . data ( ) : " " ) ;
# endif
}
d . handle = 0 ;
if ( libpath . isEmpty ( ) & & execpath . isEmpty ( ) )
{
TQString errorMsg = i18n ( " Could not find '%1' executable. " ) . arg ( TQFile : : decodeName ( _name ) ) ;
exitWithErrorMsg ( errorMsg ) ;
}
if ( getenv ( " KDE_IS_PRELINKED " ) & & ! execpath . isEmpty ( ) & & ! launcher )
libpath . truncate ( 0 ) ;
if ( ! libpath . isEmpty ( ) )
{
d . handle = lt_dlopen ( TQFile : : encodeName ( libpath ) ) ;
if ( ! d . handle )
{
const char * ltdlError = lt_dlerror ( ) ;
if ( execpath . isEmpty ( ) )
{
// Error
TQString errorMsg = i18n ( " Could not open library '%1'. \n %2 " ) . arg ( TQFile : : decodeName ( libpath ) )
. arg ( ltdlError ? TQFile : : 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 , " tdeinitmain " ) ;
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) " ) ;
TQString errorMsg = i18n ( " Could not find 'kdemain' in '%1'. \n %2 " ) . arg ( TQString ( libpath ) )
. arg ( ltdlError ? TQFile : : 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 , " tdeinit: Suspending process \n "
" tdeinit: 'gdb tdeinit %d' to debug \n "
" tdeinit: '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 ) )
{
TQCString 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 ( " tdeinit: Pipe closed unexpectedly " ) ;
d . result = 1 ; // Error
break ;
}
perror ( " tdeinit: 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 ( " tdeinit: Aborting. Can't create pipe: " ) ;
exit ( 255 ) ;
}
options = fcntl ( d . deadpipe [ 0 ] , F_GETFL ) ;
if ( options = = - 1 )
{
perror ( " tdeinit: Aborting. Can't make pipe non-blocking: " ) ;
exit ( 255 ) ;
}
if ( fcntl ( d . deadpipe [ 0 ] , F_SETFL , options | O_NONBLOCK ) = = - 1 )
{
perror ( " tdeinit: 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_tdeinit_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 , " tdeinit: Aborting. $HOME not set! " ) ;
exit ( 255 ) ;
}
chdir ( home_dir ) ;
{
TQCString path = home_dir ;
TQCString readOnly = getenv ( " KDE_HOME_READONLY " ) ;
if ( access ( path . data ( ) , R_OK | W_OK ) )
{
if ( errno = = ENOENT )
{
fprintf ( stderr , " tdeinit: Aborting. $HOME directory (%s) does not exist. \n " , path . data ( ) ) ;
exit ( 255 ) ;
}
else if ( readOnly . isEmpty ( ) )
{
fprintf ( stderr , " tdeinit: 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 , " tdeinit: 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, "tdeinit: 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 , " tdeinit: Shutting down running client. \n " ) ;
klauncher_header request_header ;
request_header . cmd = LAUNCHER_TERMINATE_TDEINIT ;
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 ( " tdeinit: Aborting. socket() failed: " ) ;
exit ( 255 ) ;
}
options = fcntl ( d . wrapper , F_GETFL ) ;
if ( options = = - 1 )
{
perror ( " tdeinit: Aborting. Can't make socket non-blocking: " ) ;
close ( d . wrapper ) ;
exit ( 255 ) ;
}
if ( fcntl ( d . wrapper , F_SETFL , options | O_NONBLOCK ) = = - 1 )
{
perror ( " tdeinit: 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 ( " tdeinit: 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 ( " tdeinit: 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 ( " tdeinit: 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("tdeinit: Aborting. socket() failed: ");
return ;
}
options = fcntl ( d . wrapper_old , F_GETFL ) ;
if ( options = = - 1 )
{
// perror("tdeinit: 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("tdeinit: 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("tdeinit: 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("tdeinit: 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 , " tdeinit: Communication error with launcher. Exiting! \n " ) ;
: : exit ( 255 ) ;
return ;
}
// KLauncher died... restart
# ifndef NDEBUG
fprintf ( stderr , " tdeinit: 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 , " tdeinit: 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 , " tdeinit: 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 , " tdeinit: 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
TQCString olddisplay = getenv ( DISPLAY ) ;
TQCString 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 , " tdeinit: Got SETENV '%s=%s' from klauncher. \n " , env_name , env_value ) ;
else
fprintf ( stderr , " tdeinit: 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 , " tdeinit: 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 , " tdeinit: terminate KDE. \n " ) ;
# endif
# ifdef Q_WS_X11
tdeinit_xio_errhandler ( 0L ) ;
# endif
}
else if ( request_header . cmd = = LAUNCHER_TERMINATE_TDEINIT )
{
# ifndef NDEBUG
fprintf ( stderr , " tdeinit: Killing tdeinit/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 , " tdeinit: 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 , " tdeinit: 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(TDEINIT_USE_XFT) && defined(TDEINIT_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(TDEINIT_USE_XFT) && defined(TDEINIT_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 tdeinit_library_path ( )
{
TQStringList ltdl_library_path =
TQStringList : : split ( ' : ' , TQFile : : decodeName ( getenv ( " LTDL_LIBRARY_PATH " ) ) ) ;
TQStringList ld_library_path =
TQStringList : : split ( ' : ' , TQFile : : decodeName ( getenv ( " LD_LIBRARY_PATH " ) ) ) ;
TQCString extra_path ;
TQStringList candidates = s_instance - > dirs ( ) - > resourceDirs ( " lib " ) ;
for ( TQStringList : : ConstIterator it = candidates . begin ( ) ;
it ! = candidates . end ( ) ;
it + + )
{
TQString 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 ;
TQCString dir = TQFile : : 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 ( ) ) ;
TQCString display = getenv ( DISPLAY ) ;
if ( display . isEmpty ( ) )
{
fprintf ( stderr , " tdeinit: Aborting. $ " DISPLAY " is not set. \n " ) ;
exit ( 255 ) ;
}
int i ;
if ( ( i = display . findRev ( ' . ' ) ) > display . findRev ( ' : ' ) & & i > = 0 )
display . truncate ( i ) ;
TQCString socketName = TQFile : : encodeName ( locateLocal ( " socket " , TQString ( " tdeinit-%1 " ) . arg ( TQString ( display ) ) , s_instance ) ) ;
if ( socketName . length ( ) > = MAX_SOCK_FILE )
{
fprintf ( stderr , " tdeinit: 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 = TQFile : : encodeName ( locateLocal ( " socket " , TQString ( " tdeinit_%1 " ) . arg ( TQString ( display ) ) , s_instance ) ) ;
if ( socketName . length ( ) > = MAX_SOCK_FILE )
{
fprintf ( stderr , " tdeinit: Aborting. Socket name will be too long: \n " ) ;
fprintf ( stderr , " '%s' \n " , socketName . data ( ) ) ;
exit ( 255 ) ;
}
strcpy ( sock_file , socketName . data ( ) ) ;
}
int tdeinit_xio_errhandler ( Display * disp )
{
// disp is 0L when KDE shuts down. We don't want those warnings then.
if ( disp )
qWarning ( " tdeinit: 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 ( " tdeinit: sending SIGHUP to children. " ) ;
/* this should remove all children we started */
signal ( SIGHUP , SIG_IGN ) ;
kill ( 0 , SIGHUP ) ;
sleep ( 2 ) ;
if ( disp )
qWarning ( " tdeinit: 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 ( " tdeinit: Exit. " ) ;
exit ( 0 ) ;
return 0 ;
}
# ifdef Q_WS_X11
int tdeinit_x_errhandler ( Display * dpy , XErrorEvent * err )
{
# ifndef NDEBUG
char errstr [ 256 ] ;
// tdeinit almost doesn't use X, and therefore there shouldn't be any X error
XGetErrorText ( dpy , err - > error_code , errstr , 256 ) ;
fprintf ( stderr , " tdeinit: 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 ( tdeinit_xio_errhandler ) ;
XSetErrorHandler ( tdeinit_x_errhandler ) ;
}
// Borrowed from tdebase/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 , " tdeinit: 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 , " tdeinit: Can't connect to the X Server. \n " \
" tdeinit: 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 tdeinit 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 TDEINIT_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: tdeinit [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 ( " tdeinit " ) ;
/** Prepare to change process name **/
tdeinit_initsetproctitle ( argc , argv , envp ) ;
tdeinit_library_path ( ) ;
// Don't make our instance the global instance
// (do it only after tdeinit_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 tdeinit_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 > / tdeinit - < display > socket for incoming wrapper
* requests .
*/
init_tdeinit_socket ( ) ;
}
if ( launch_dcop )
{
if ( d . suicide )
pid = launch ( 3 , " dcopserver " , " --nosid \0 --suicide " ) ;
else
pid = launch ( 2 , " dcopserver " , " --nosid " ) ;
# ifndef NDEBUG
fprintf ( stderr , " tdeinit: 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 , " tdeinit: DCOPServer could not be started, aborting. \n " ) ;
exit ( 1 ) ;
}
}
# ifndef __CYGWIN__
if ( ! d . suicide & & ! getenv ( " KDE_IS_PRELINKED " ) )
{
TQString konq = locate ( " lib " , " libkonq.la " , s_instance ) ;
if ( ! konq . isEmpty ( ) )
( void ) lt_dlopen ( TQFile : : 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 , " tdeinit: 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(TDEINIT_USE_XFT) && defined(TDEINIT_USE_FONTCONFIG)
if ( FcGetVersion ( ) < 20390 )
{
XftInit ( 0 ) ;
XftInitFtLibrary ( ) ;
}
# endif
TQFont : : initialize ( ) ;
setlocale ( LC_ALL , " " ) ;
setlocale ( LC_NUMERIC , " C " ) ;
# ifdef Q_WS_X11
if ( XSupportsLocale ( ) )
{
// Similar to TQApplication::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 , " tdeinit: 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 , " tdeinit: 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 TDEINIT_OOM_PROTECT
| | isdigit ( safe_argv [ i ] [ 0 ] )
# endif
)
{
// Ignore
}
else
{
pid = launch ( 1 , safe_argv [ i ] , 0 ) ;
# ifndef NDEBUG
fprintf ( stderr , " tdeinit: 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 ) ;
tdeinit_setproctitle ( " tdeinit 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 ;
}