2014-11-13 01:04:59 +02:00
/*
* 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 >
*
* 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 .
*/
# define QT_NO_CAST_FROM_ASCII
# include <config.h>
# include <config-kdeinit.h>
# include <sys/types.h>
# include <sys/time.h>
# include <sys/resource.h>
# include <sys/stat.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <sys/wait.h>
2022-09-23 15:42:09 +03:00
# include <sys/select.h> // Needed on some systems.
2014-11-13 01:04:59 +02:00
# include <ctype.h>
# include <errno.h>
# include <fcntl.h>
# include <signal.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <locale.h>
2016-04-22 13:19:18 +00:00
# include <qplatformdefs.h>
2014-11-13 01:04:59 +02:00
# include <QtCore/QLibrary>
# include <QtCore/QString>
# include <QtCore/QFile>
# include <QtCore/QRegExp>
# include <kcomponentdata.h>
# include <kstandarddirs.h>
# include <kglobal.h>
# include <kconfig.h>
# include <kapplication.h>
# include <klocale.h>
# include <kdebug.h>
# include <kde_file.h>
# include <ksavefile.h>
2016-03-05 03:36:21 +02:00
# include <kpluginloader.h>
2022-05-26 06:24:47 +03:00
# include <kdeversion.h>
# include "klauncher_cmds.h"
# include "proctitle.h"
2014-11-13 01:04:59 +02:00
# ifdef Q_OS_LINUX
# include <sys/prctl.h>
# ifndef PR_SET_NAME
# define PR_SET_NAME 15
# endif
# endif
# ifdef Q_WS_X11
# include <X11/Xlib.h>
# include <X11/Xatom.h>
# include <fixx11h.h>
# include <kstartupinfo.h>
# endif
// #define SKIP_PROCTITLE 1
extern char * * environ ;
# 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 KComponentData * s_instance = 0 ;
# define MAX_SOCK_FILE 255
static char sock_file [ MAX_SOCK_FILE ] ;
# ifdef Q_WS_X11
# define DISPLAY "DISPLAY"
# elif defined(Q_WS_QWS)
# define DISPLAY "QWS_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 accepted_fd ; /* socket accepted and that must be closed in the child process */
char result ;
int exit_status ;
pid_t fork ;
pid_t launcher_pid ;
int n ;
char * * argv ;
int ( * func ) ( int , char * [ ] ) ;
int ( * launcher_func ) ( int ) ;
bool debug_wait ;
QByteArray errorMsg ;
bool launcher_ok ;
bool suicide ;
} d ;
struct child
{
pid_t pid ;
int sock ; /* fd to write message when child is dead*/
struct child * next ;
} ;
static struct child * children ;
# ifdef Q_WS_X11
extern " C " {
int kdeinit_xio_errhandler ( Display * ) ;
int kdeinit_x_errhandler ( Display * , XErrorEvent * err ) ;
}
# endif
/*
* Clean up the file descriptor table by closing all file descriptors
* that are still open .
*
* This function is called very early in the main ( ) function , so that
* we don ' t leak anything that was leaked to us .
*/
static void cleanup_fds ( )
{
int maxfd = FD_SETSIZE ;
struct rlimit rl ;
if ( getrlimit ( RLIMIT_NOFILE , & rl ) = = 0 )
maxfd = rl . rlim_max ;
for ( int fd = 3 ; fd < maxfd ; + + fd )
{
2015-08-19 01:37:57 +03:00
close ( fd ) ;
2014-11-13 01:04:59 +02:00
}
}
/*
* Close fd ' s which are only useful for the parent process .
* Restore default signal handlers .
*/
static void close_fds ( )
{
while ( struct child * child = children ) {
close ( child - > sock ) ;
children = child - > next ;
free ( child ) ;
}
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 [ 0 ] ! = - 1 )
{
close ( d . launcher [ 0 ] ) ;
d . launcher [ 0 ] = - 1 ;
}
if ( d . wrapper ! = - 1 )
{
close ( d . wrapper ) ;
d . wrapper = - 1 ;
}
if ( d . accepted_fd ! = - 1 )
{
close ( d . accepted_fd ) ;
d . accepted_fd = - 1 ;
}
# 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
KDE_signal ( SIGCHLD , SIG_DFL ) ;
KDE_signal ( SIGPIPE , SIG_DFL ) ;
}
/* Notify wrapper program that the child it started has finished. */
static void child_died ( pid_t exit_pid , int exit_status )
{
struct child * child , * * childptr = & children ;
while ( ( child = * childptr ) )
{
if ( child - > pid = = exit_pid )
{
/* Send a message with the return value of the child on the control socket */
klauncher_header request_header ;
long request_data [ 2 ] ;
request_header . cmd = LAUNCHER_CHILD_DIED ;
request_header . arg_length = sizeof ( long ) * 2 ;
request_data [ 0 ] = exit_pid ;
request_data [ 1 ] = exit_status ;
write ( child - > sock , & request_header , sizeof ( request_header ) ) ;
write ( child - > sock , request_data , request_header . arg_length ) ;
close ( child - > sock ) ;
* childptr = child - > next ;
free ( child ) ;
return ;
}
childptr = & child - > next ;
}
}
static void exitWithErrorMsg ( const QString & errorMsg )
{
fprintf ( stderr , " %s \n " , errorMsg . toLocal8Bit ( ) . data ( ) ) ;
QByteArray utf8ErrorMsg = errorMsg . toUtf8 ( ) ;
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 = KDE_open ( tty , O_WRONLY ) ;
if ( fd < 0 )
{
perror ( " kdeinit4: could not open() tty " ) ;
return ;
}
if ( dup2 ( fd , STDOUT_FILENO ) < 0 )
{
perror ( " kdeinit4: could not dup2() stdout tty " ) ;
}
if ( dup2 ( fd , STDERR_FILENO ) < 0 )
{
perror ( " kdeinit4: could not dup2() stderr tty " ) ;
}
close ( fd ) ;
}
// from kdecore/netwm.cpp
static int get_current_desktop ( Display * disp )
{
int desktop = 0 ; // no desktop by default
# 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 ;
}
# ifdef Q_WS_X11
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 ( QFile : : decodeName ( 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
2022-10-08 15:44:22 +03:00
static QByteArray execpath_avoid_loops ( const QByteArray & exec , int envc , const char * envs , bool avoid_loops )
2014-11-13 01:04:59 +02:00
{
QStringList paths ;
2022-10-08 15:43:44 +03:00
static const QChar pathSep = QChar : : fromLatin1 ( ' : ' ) ;
2014-11-13 01:04:59 +02:00
if ( envc > 0 ) /* use the passed environment */
{
const char * path = get_env_var ( " PATH= " , envc , envs ) ;
if ( path ! = NULL )
2022-10-08 15:43:44 +03:00
paths = QFile : : decodeName ( path ) . split ( pathSep ) ;
2014-11-13 01:04:59 +02:00
} else {
2022-10-08 15:43:44 +03:00
paths = QString : : fromLocal8Bit ( qgetenv ( " PATH " ) ) . split ( pathSep , QString : : KeepEmptyParts ) ;
2014-11-13 01:04:59 +02:00
}
2022-10-08 15:43:44 +03:00
QString execpath = s_instance - > dirs ( ) - > findExe ( QFile : : decodeName ( exec ) , paths . join ( pathSep ) ) ;
2014-11-13 01:04:59 +02:00
if ( avoid_loops & & ! execpath . isEmpty ( ) ) {
const int pos = execpath . lastIndexOf ( QLatin1Char ( ' / ' ) ) ;
const QString bin_path = execpath . left ( pos ) ;
for ( QStringList : : Iterator it = paths . begin ( ) ;
it ! = paths . end ( ) ;
+ + it ) {
if ( * it = = bin_path | | * it = = bin_path + QLatin1Char ( ' / ' ) ) {
paths . erase ( it ) ;
break ; // -->
}
}
2022-10-08 15:43:44 +03:00
execpath = s_instance - > dirs ( ) - > findExe ( QFile : : decodeName ( exec ) , paths . join ( pathSep ) ) ;
2014-11-13 01:04:59 +02:00
}
return QFile : : encodeName ( execpath ) ;
}
static pid_t launch ( int argc , const char * _name , const char * args ,
const char * cwd = 0 , int envc = 0 , const char * envs = 0 ,
const char * tty = 0 , bool avoid_loops = false ,
const char * startup_id_str = " 0 " ) // krazy:exclude=doublequote_chars
{
QString lib ;
QByteArray name ;
QByteArray exec ;
QString libpath ;
QByteArray execpath ;
if ( _name [ 0 ] ! = ' / ' ) {
name = _name ;
lib = QFile : : decodeName ( name ) ;
exec = name ;
2016-03-05 03:36:21 +02:00
KPluginLoader klib ( lib , * s_instance ) ;
2014-11-13 01:04:59 +02:00
libpath = klib . fileName ( ) ;
execpath = execpath_avoid_loops ( exec , envc , envs , avoid_loops ) ;
} else {
name = _name ;
lib = QFile : : decodeName ( name ) ;
name = name . mid ( name . lastIndexOf ( ' / ' ) + 1 ) ;
exec = _name ;
if ( lib . endsWith ( QLatin1String ( " .so " ) ) )
libpath = lib ;
else {
execpath = exec ;
}
}
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: preparing to launch %s \n " , libpath . isEmpty ( )
? execpath . constData ( ) : libpath . toUtf8 ( ) . constData ( ) ) ;
# endif
if ( ! args ) {
argc = 1 ;
}
if ( 0 > pipe ( d . fd ) )
{
perror ( " kdeinit4: pipe() failed " ) ;
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. " ) . toUtf8 ( ) ;
d . fork = 0 ;
return d . fork ;
}
# 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
// find out this path before forking, doing it afterwards
// crashes on some platforms, notably OSX
d . errorMsg = 0 ;
d . fork = fork ( ) ;
switch ( d . fork ) {
case - 1 :
perror ( " kdeinit4: fork() failed " ) ;
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. " ) . toUtf8 ( ) ;
close ( d . fd [ 0 ] ) ;
close ( d . fd [ 1 ] ) ;
d . fork = 0 ;
break ;
case 0 :
{
2022-05-26 06:24:47 +03:00
// Child
2014-11-13 01:04:59 +02:00
close ( d . fd [ 0 ] ) ;
close_fds ( ) ;
// Try to chdir, either to the requested directory or to the user's document path by default.
// We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist,
// we still want to execute `foo` even if the chdir() failed.
if ( cwd & & * cwd ) {
( void ) chdir ( cwd ) ;
}
for ( int i = 0 ; i < envc ; i + + )
{
putenv ( ( char * ) envs ) ;
while ( * envs ! = 0 ) envs + + ;
envs + + ;
}
# ifdef Q_WS_X11
if ( startup_id . none ( ) )
KStartupInfo : : resetStartupEnv ( ) ;
else
startup_id . setupStartupEnv ( ) ;
# endif
{
int r ;
QByteArray 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 ;
# ifndef SKIP_PROCTITLE
2022-05-26 06:24:47 +03:00
// Give the process a new name
2014-11-13 01:04:59 +02:00
# ifdef Q_OS_LINUX
2022-05-26 06:24:47 +03:00
// Set the process name so that killall works like intended
2014-11-13 01:04:59 +02:00
r = prctl ( PR_SET_NAME , ( unsigned long ) name . data ( ) , 0 , 0 , 0 ) ;
if ( r = = 0 )
proctitle_set ( " %s [kdeinit]%s " , name . data ( ) , procTitle . data ( ) ? procTitle . data ( ) : " " ) ;
else
proctitle_set ( " kdeinit4: %s%s " , name . data ( ) , procTitle . data ( ) ? procTitle . data ( ) : " " ) ;
# else
proctitle_set ( " kdeinit4: %s%s " , name . data ( ) , procTitle . data ( ) ? procTitle . data ( ) : " " ) ;
# endif
# endif
}
if ( libpath . isEmpty ( ) & & execpath . isEmpty ( ) )
{
QString errorMsg = i18n ( " Could not find '%1' executable. " , QFile : : decodeName ( _name ) ) ;
exitWithErrorMsg ( errorMsg ) ;
}
QLibrary l ( libpath ) ;
if ( ! libpath . isEmpty ( ) )
{
if ( ! l . load ( ) | | ! l . isLoaded ( ) )
{
QString ltdlError ( l . errorString ( ) ) ;
if ( execpath . isEmpty ( ) )
{
// Error
QString errorMsg = i18n ( " Could not open library '%1'. \n %2 " , libpath , ltdlError ) ;
exitWithErrorMsg ( errorMsg ) ;
}
else
{
// Print warning
fprintf ( stderr , " Could not open library %s: %s \n " , qPrintable ( lib ) ,
qPrintable ( ltdlError ) ) ;
}
}
}
if ( ! l . isLoaded ( ) )
{
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 ) ;
2021-06-22 00:35:28 +03:00
if ( ! execpath . isEmpty ( ) )
execvp ( execpath . constData ( ) , d . argv ) ;
2014-11-13 01:04:59 +02:00
d . result = 1 ; // Error
write ( d . fd [ 1 ] , & d . result , 1 ) ;
close ( d . fd [ 1 ] ) ;
exit ( 255 ) ;
}
2015-08-25 18:13:21 +03:00
void * sym = l . resolve ( " kdemain " ) ;
if ( ! sym )
{
QString ltdlError = l . errorString ( ) ;
fprintf ( stderr , " Could not find kdemain: %s \n " , qPrintable ( ltdlError ) ) ;
QString errorMsg = i18n ( " Could not find 'kdemain' in '%1'. \n %2 " ,
libpath , ltdlError ) ;
exitWithErrorMsg ( errorMsg ) ;
}
2014-11-13 01:04:59 +02:00
d . result = 0 ; // Success
write ( d . fd [ 1 ] , & d . result , 1 ) ;
close ( d . fd [ 1 ] ) ;
d . func = ( int ( * ) ( int , char * [ ] ) ) sym ;
if ( d . debug_wait )
{
fprintf ( stderr , " kdeinit4: Suspending process \n "
" kdeinit4: 'gdb kdeinit4 %d' to debug \n "
" kdeinit4: 'kill -SIGCONT %d' to continue \n " ,
getpid ( ) , getpid ( ) ) ;
kill ( getpid ( ) , SIGSTOP ) ;
}
else
{
setup_tty ( tty ) ;
}
exit ( d . func ( argc , d . argv ) ) ; /* Launch! */
break ;
}
default :
2022-05-26 06:24:47 +03:00
// Parent
2014-11-13 01:04:59 +02:00
close ( d . fd [ 1 ] ) ;
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, "kdeinit4: no kdeinit module, 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 ) )
{
QByteArray 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 ( d . n = = 0 )
{
if ( exec ) {
d . result = 0 ;
} else {
fprintf ( stderr , " kdeinit4: (%s %s) Pipe closed unexpectedly " , name . constData ( ) , execpath . constData ( ) ) ;
perror ( " kdeinit4: Pipe closed unexpectedly " ) ;
d . result = 1 ; // Error
}
break ;
}
perror ( " kdeinit4: Error reading from pipe " ) ;
d . result = 1 ; // Error
break ;
}
close ( d . fd [ 0 ] ) ;
}
# 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 ;
}
extern " C " {
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 ( " kdeinit4: Aborting. Can not create pipe " ) ;
exit ( 255 ) ;
}
options = fcntl ( d . deadpipe [ 0 ] , F_GETFL ) ;
if ( options = = - 1 )
{
perror ( " kdeinit4: Aborting. Can not make pipe non-blocking " ) ;
exit ( 255 ) ;
}
if ( fcntl ( d . deadpipe [ 0 ] , F_SETFL , options | O_NONBLOCK ) = = - 1 )
{
perror ( " kdeinit4: Aborting. Can not 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 ) ;
}
2022-05-25 08:20:13 +03:00
static void shutdown_kdeinit ( )
{
QT_SOCKLEN_T socklen ;
/** 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, "kdeinit4: Warning, socket_file already exists!\n");
2022-05-26 06:24:47 +03:00
// Create the socket stream
2022-05-25 08:20:13 +03:00
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 , " kdeinit4: 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 ) ;
}
2022-05-26 06:24:47 +03:00
// Delete any stale socket file (and symlink)
2022-05-25 08:20:13 +03:00
unlink ( sock_file ) ;
}
2014-11-13 01:04:59 +02:00
static void init_kdeinit_socket ( )
{
struct sockaddr_un sa ;
2016-04-22 13:19:18 +00:00
QT_SOCKLEN_T socklen ;
2014-11-13 01:04:59 +02:00
long options ;
const QByteArray home_dir = qgetenv ( " HOME " ) ;
int max_tries = 10 ;
if ( home_dir . isEmpty ( ) )
{
fprintf ( stderr , " kdeinit4: Aborting. $HOME not set! " ) ;
exit ( 255 ) ;
}
if ( chdir ( home_dir ) ! = 0 ) {
fprintf ( stderr , " kdeinit4: Aborting. Couldn't enter '%s'! " , home_dir . constData ( ) ) ;
exit ( 255 ) ;
}
{
QByteArray path = home_dir ;
QByteArray readOnly = qgetenv ( " KDE_HOME_READONLY " ) ;
if ( access ( path . data ( ) , R_OK | W_OK ) )
{
if ( errno = = ENOENT )
{
fprintf ( stderr , " kdeinit4: Aborting. $HOME directory (%s) does not exist. \n " , path . data ( ) ) ;
exit ( 255 ) ;
}
else if ( readOnly . isEmpty ( ) )
{
fprintf ( stderr , " kdeinit4: Aborting. No write access to $HOME directory (%s). \n " , path . data ( ) ) ;
exit ( 255 ) ;
}
}
#if 0 // obsolete in kde4. Should we check writing to another file instead?
path = qgetenv ( " ICEAUTHORITY " ) ;
if ( path . isEmpty ( ) )
{
path = home_dir ;
path + = " /.ICEauthority " ;
}
if ( access ( path . data ( ) , R_OK | W_OK ) & & ( errno ! = ENOENT ) )
{
fprintf ( stderr , " kdeinit4: Aborting. No write access to '%s'. \n " , path . data ( ) ) ;
exit ( 255 ) ;
}
# endif
}
2022-05-25 08:20:13 +03:00
shutdown_kdeinit ( ) ;
2014-11-13 01:04:59 +02:00
2022-05-26 06:24:47 +03:00
// Create socket
2014-11-13 01:04:59 +02:00
d . wrapper = socket ( PF_UNIX , SOCK_STREAM , 0 ) ;
if ( d . wrapper < 0 )
{
perror ( " kdeinit4: Aborting. socket() failed " ) ;
exit ( 255 ) ;
}
options = fcntl ( d . wrapper , F_GETFL ) ;
if ( options = = - 1 )
{
perror ( " kdeinit4: Aborting. Can not make socket non-blocking " ) ;
close ( d . wrapper ) ;
exit ( 255 ) ;
}
if ( fcntl ( d . wrapper , F_SETFL , options | O_NONBLOCK ) = = - 1 )
{
perror ( " kdeinit4: Aborting. Can not make socket non-blocking " ) ;
close ( d . wrapper ) ;
exit ( 255 ) ;
}
while ( 1 ) {
2022-05-26 06:24:47 +03:00
// Bind it
2014-11-13 01:04:59 +02:00
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 ) {
2015-08-25 18:13:21 +03:00
perror ( " kdeinit4: Aborting. bind() failed " ) ;
fprintf ( stderr , " Could not bind to socket '%s' \n " , sock_file ) ;
close ( d . wrapper ) ;
exit ( 255 ) ;
}
max_tries - - ;
2014-11-13 01:04:59 +02:00
} else
break ;
}
2022-05-26 06:24:47 +03:00
// Set permissions
2014-11-13 01:04:59 +02:00
if ( chmod ( sock_file , 0600 ) ! = 0 )
{
perror ( " kdeinit4: Aborting. Can not 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 ( " kdeinit4: Aborting. listen() failed " ) ;
unlink ( sock_file ) ;
close ( d . wrapper ) ;
exit ( 255 ) ;
}
}
/*
* 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 start_klauncher ( )
{
if ( socketpair ( AF_UNIX , SOCK_STREAM , 0 , d . launcher ) < 0 ) {
perror ( " kdeinit4: socketpair() failed " ) ;
exit ( 255 ) ;
}
char args [ 32 ] ;
strcpy ( args , " --fd= " ) ;
sprintf ( args + 5 , " %d " , d . launcher [ 1 ] ) ;
d . launcher_pid = launch ( 2 , " klauncher " , args ) ;
close ( d . launcher [ 1 ] ) ;
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Launched KLauncher, pid = %ld, result = %d \n " ,
( long ) d . launcher_pid , d . result ) ;
# endif
}
static void launcher_died ( )
{
if ( ! d . launcher_ok )
{
/* This is bad. */
fprintf ( stderr , " kdeinit4: Communication error with launcher. Exiting! \n " ) ;
: : exit ( 255 ) ;
return ;
}
// KLauncher died... restart
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: 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 ;
start_klauncher ( ) ;
}
static bool handle_launcher_request ( int sock , const char * who )
{
( void ) who ; // for NDEBUG
klauncher_header request_header ;
char * request_data = 0L ;
int result = read_socket ( sock , ( char * ) & request_header , sizeof ( request_header ) ) ;
if ( result ! = 0 )
{
return false ;
}
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 )
{
free ( request_data ) ;
return false ;
}
}
//kDebug() << "Got cmd" << request_header.cmd << commandToString(request_header.cmd);
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_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 " ; // krazy:exclude=doublequote_chars
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Got %s '%s' from %s. \n " ,
commandToString ( request_header . cmd ) ,
name , who ) ;
# endif
const char * arg_n = args ;
for ( int i = 1 ; i < argc ; i + + )
{
arg_n = arg_n + strlen ( arg_n ) + 1 ;
}
2022-05-25 09:09:02 +03:00
if ( request_header . cmd = = LAUNCHER_EXT_EXEC | | request_header . cmd = = LAUNCHER_EXEC_NEW )
2014-11-13 01:04:59 +02:00
{
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 ;
}
memcpy ( & l , arg_n , sizeof ( long ) ) ;
avoid_loops = l ;
arg_n + = sizeof ( long ) ;
2022-05-25 09:09:02 +03:00
}
2014-11-13 01:04:59 +02:00
2022-05-25 09:09:02 +03:00
if ( request_header . cmd = = LAUNCHER_EXT_EXEC )
2014-11-13 01:04:59 +02:00
{
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 , " kdeinit4: EXEC request has invalid format. \n " ) ;
# endif
free ( request_data ) ;
d . debug_wait = false ;
return true ; // sure?
}
pid = launch ( argc , name , args , cwd , envc , envs ,
tty , avoid_loops , startup_id_str ) ;
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 ) ;
/* add new child to list */
struct child * child = ( struct child * ) malloc ( sizeof ( struct child ) ) ;
child - > pid = pid ;
child - > sock = dup ( sock ) ;
child - > next = children ;
children = child ;
}
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
fprintf ( stderr , " kdeinit4: Got SETENV '%s=%s' from %s. \n " , env_name , env_value , who ) ;
# endif
if ( request_header . arg_length ! =
( int ) ( strlen ( env_name ) + strlen ( env_value ) + 2 ) )
{
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: SETENV request has invalid format. \n " ) ;
# endif
free ( request_data ) ;
return true ; // sure?
}
setenv ( env_name , env_value , 1 ) ;
}
else if ( request_header . cmd = = LAUNCHER_TERMINATE_KDEINIT )
{
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Got termination request (PID %ld). \n " , ( long ) getpid ( ) ) ;
# endif
if ( d . launcher_pid ) {
kill ( d . launcher_pid , SIGTERM ) ;
d . launcher_pid = 0 ;
close ( d . launcher [ 0 ] ) ;
d . launcher [ 0 ] = - 1 ;
}
unlink ( sock_file ) ;
if ( children ) {
close ( d . wrapper ) ;
d . wrapper = - 1 ;
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Closed sockets, but not exiting until all children terminate. \n " ) ;
# endif
} else {
raise ( SIGTERM ) ;
}
}
else if ( request_header . cmd = = LAUNCHER_DEBUG_WAIT )
{
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Debug wait activated. \n " ) ;
# endif
d . debug_wait = true ;
}
if ( request_data )
free ( request_data ) ;
return true ;
}
static void handle_requests ( pid_t waitForPid )
{
int max_sock = d . deadpipe [ 0 ] ;
if ( d . wrapper > max_sock )
max_sock = d . wrapper ;
if ( d . launcher [ 0 ] > max_sock )
max_sock = d . launcher [ 0 ] ;
# ifdef Q_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 ;
int exit_status ;
char c ;
/* Flush the pipe of death */
while ( read ( d . deadpipe [ 0 ] , & c , 1 ) = = 1 )
{ }
/* Handle dying children */
do {
exit_pid = waitpid ( - 1 , & exit_status , WNOHANG ) ;
if ( exit_pid > 0 )
{
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: PID %ld terminated. \n " , ( long ) exit_pid ) ;
# endif
if ( waitForPid & & ( exit_pid = = waitForPid ) )
return ;
if ( WIFEXITED ( exit_status ) ) // fix process return value
exit_status = WEXITSTATUS ( exit_status ) ;
else if ( WIFSIGNALED ( exit_status ) )
exit_status = 128 + WTERMSIG ( exit_status ) ;
child_died ( exit_pid , exit_status ) ;
if ( d . wrapper < 0 & & ! children ) {
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Last child terminated, exiting (PID %ld). \n " ,
( long ) getpid ( ) ) ;
# endif
raise ( SIGTERM ) ;
}
}
}
while ( exit_pid > 0 ) ;
FD_ZERO ( & rd_set ) ;
FD_ZERO ( & wr_set ) ;
FD_ZERO ( & e_set ) ;
if ( d . launcher [ 0 ] > = 0 )
FD_SET ( d . launcher [ 0 ] , & rd_set ) ;
if ( d . wrapper > = 0 )
FD_SET ( d . wrapper , & rd_set ) ;
FD_SET ( d . deadpipe [ 0 ] , & rd_set ) ;
# ifdef Q_WS_X11
if ( X11fd > = 0 ) FD_SET ( X11fd , & rd_set ) ;
# endif
result = select ( max_sock , & rd_set , & wr_set , & e_set , 0 ) ;
if ( result < 0 ) {
if ( errno = = EINTR | | errno = = EAGAIN )
continue ;
perror ( " kdeinit4: Aborting. select() failed " ) ;
return ;
}
/* Handle wrapper request */
if ( d . wrapper > = 0 & & FD_ISSET ( d . wrapper , & rd_set ) )
{
struct sockaddr_un client ;
2016-04-22 13:19:18 +00:00
QT_SOCKLEN_T sClient = sizeof ( client ) ;
2014-11-13 01:04:59 +02:00
int sock = accept ( d . wrapper , ( struct sockaddr * ) & client , & sClient ) ;
if ( sock > = 0 )
{
d . accepted_fd = sock ;
handle_launcher_request ( sock , " wrapper " ) ;
close ( sock ) ;
d . accepted_fd = - 1 ;
}
}
/* Handle launcher request */
if ( d . launcher [ 0 ] > = 0 & & FD_ISSET ( d . launcher [ 0 ] , & rd_set ) )
{
if ( ! handle_launcher_request ( d . launcher [ 0 ] , " launcher " ) )
launcher_died ( ) ;
if ( waitForPid = = d . launcher_pid )
return ;
}
# ifdef Q_WS_X11
/* Look for incoming X11 events */
if ( X11fd > = 0 & & FD_ISSET ( X11fd , & rd_set ) ) {
if ( X11display ! = 0 ) {
XEvent event_return ;
while ( XPending ( X11display ) )
XNextEvent ( X11display , & event_return ) ;
}
}
# endif
}
}
static void kdeinit_library_path ( )
{
QByteArray display = qgetenv ( DISPLAY ) ;
if ( display . isEmpty ( ) )
{
# if defined(Q_WS_X11) || defined(Q_WS_QWS)
2016-05-13 10:19:21 +00:00
fprintf ( stderr , " kdeinit4: Aborting. $ " DISPLAY " is not set. \n " ) ;
2014-11-13 01:04:59 +02:00
exit ( 255 ) ;
# endif
}
int i ;
if ( ( i = display . lastIndexOf ( ' . ' ) ) > display . lastIndexOf ( ' : ' ) & & i > = 0 )
display . truncate ( i ) ;
display . replace ( ' : ' , ' _ ' ) ;
// WARNING, if you change the socket name, adjust kwrapper too
const QString socketFileName = QString : : fromLatin1 ( " kdeinit4_%1 " ) . arg ( QLatin1String ( display ) ) ;
2022-05-25 10:42:27 +03:00
QByteArray socketName = QFile : : encodeName ( KStandardDirs : : locateLocal ( " tmp " , socketFileName , * s_instance ) ) ;
2014-11-13 01:04:59 +02:00
if ( socketName . length ( ) > = MAX_SOCK_FILE )
{
fprintf ( stderr , " kdeinit4: 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 )
2022-05-24 16:24:51 +03:00
kWarning ( ) < < " Fatal IO error: client killed " ;
2014-11-13 01:04:59 +02:00
if ( sock_file [ 0 ] )
{
2022-05-26 06:24:47 +03:00
// Delete any stale socket file
2014-11-13 01:04:59 +02:00
unlink ( sock_file ) ;
}
// 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 )
2022-05-24 16:24:51 +03:00
kWarning ( ) < < " Sending SIGHUP to children. " ;
2014-11-13 01:04:59 +02:00
2022-05-26 06:24:47 +03:00
// this should remove all children we started
2014-11-13 01:04:59 +02:00
KDE_signal ( SIGHUP , SIG_IGN ) ;
kill ( 0 , SIGHUP ) ;
sleep ( 2 ) ;
if ( disp )
2022-05-24 16:24:51 +03:00
kWarning ( ) < < " Sending SIGTERM to children. " ;
2014-11-13 01:04:59 +02:00
2022-05-26 06:24:47 +03:00
// And if they don't listen to us, this should work
2014-11-13 01:04:59 +02:00
KDE_signal ( SIGTERM , SIG_IGN ) ;
kill ( 0 , SIGTERM ) ;
if ( disp )
2022-05-24 16:24:51 +03:00
kWarning ( ) < < " Exit. " ;
2014-11-13 01:04:59 +02:00
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 , " kdeinit4(%d) : KDE detected X Error: %s %d \n "
" Major opcode: %d \n "
" Minor opcode: %d \n "
" Resource id: 0x%lx \n " ,
getpid ( ) , errstr , err - > error_code , err - > request_code , err - > minor_code , err - > resourceid ) ;
//kDebug() << kBacktrace();
# else
Q_UNUSED ( dpy ) ;
Q_UNUSED ( err ) ;
# endif
return 0 ;
}
# endif
# 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 ) ;
/*
2016-05-15 02:06:56 +00:00
Handle the tricky case of running via kdesudo / su / sudo / etc . There the usual case
is that kdesudo ( etc . ) creates a file with xauth information , sets XAUTHORITY ,
2014-11-13 01:04:59 +02:00
runs the command and removes the xauth file after the command finishes . However ,
dbus and kdeinit daemon currently don ' t clean up properly and keeping running .
2016-05-15 02:06:56 +00:00
Which means that running a KDE app via kdesudo the second time talks to kdeinit
2014-11-13 01:04:59 +02:00
with obsolete xauth information , which makes it unable to connect to X or launch
any X11 applications .
Even fixing the cleanup probably wouldn ' t be sufficient , since it ' d be possible to
2016-05-15 02:06:56 +00:00
launch one kdesudo session , another one , exit the first one and the app from the second
2014-11-13 01:04:59 +02:00
session would be using kdeinit from the first one .
So the trick here is to duplicate the xauth file to another file in KDE ' s tmp
location , make the file have a consistent name so that future sessions will use it
as well , point XAUTHORITY there and never remove the file ( except for possible
tmp cleanup ) .
*/
if ( ! qgetenv ( " XAUTHORITY " ) . isEmpty ( ) ) {
QByteArray display = qgetenv ( DISPLAY ) ;
int i ;
if ( ( i = display . lastIndexOf ( ' . ' ) ) > display . lastIndexOf ( ' : ' ) & & i > = 0 )
display . truncate ( i ) ;
display . replace ( ' : ' , ' _ ' ) ;
QString xauth = s_instance - > dirs ( ) - > saveLocation ( " tmp " ) + QLatin1String ( " xauth- " )
+ QString : : number ( getuid ( ) ) + QLatin1String ( " - " ) + QString : : fromLocal8Bit ( display ) ;
KSaveFile xauthfile ( xauth ) ;
QFile xauthfrom ( QFile : : decodeName ( qgetenv ( " XAUTHORITY " ) ) ) ;
if ( ! xauthfrom . open ( QFile : : ReadOnly ) | | ! xauthfile . open ( QFile : : WriteOnly )
| | xauthfile . write ( xauthfrom . readAll ( ) ) ! = xauthfrom . size ( ) | | ! xauthfile . finalize ( ) ) {
xauthfile . abort ( ) ;
} else {
setenv ( " XAUTHORITY " , QFile : : encodeName ( xauth ) , true ) ;
}
}
}
// 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 , " kdeinit4: 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 , " kdeinit4: Can not connect to the X Server. \n " \
" kdeinit4: Might not terminate at end of session. \n " ) ;
return - 1 ;
}
# endif
extern " C " {
static void secondary_child_handler ( int )
{
waitpid ( - 1 , 0 , WNOHANG ) ;
}
}
2021-09-16 16:27:19 +03:00
int main ( int argc , char * * argv )
2014-11-13 01:04:59 +02:00
{
setlocale ( LC_ALL , " " ) ;
setlocale ( LC_NUMERIC , " C " ) ;
pid_t pid ;
bool do_fork = true ;
int launch_klauncher = 1 ;
2022-05-25 08:20:13 +03:00
int do_shutdown = 0 ;
2014-11-13 01:04:59 +02:00
d . suicide = false ;
2022-05-26 06:24:47 +03:00
// Save arguments first...
2014-11-13 01:04:59 +02:00
char * * safe_argv = ( char * * ) malloc ( sizeof ( char * ) * argc ) ;
for ( int i = 0 ; i < argc ; i + + )
{
safe_argv [ i ] = strcpy ( ( char * ) malloc ( strlen ( argv [ i ] ) + 1 ) , argv [ i ] ) ;
if ( strcmp ( safe_argv [ i ] , " --no-klauncher " ) = = 0 )
launch_klauncher = 0 ;
if ( strcmp ( safe_argv [ i ] , " --no-fork " ) = = 0 )
do_fork = false ;
if ( strcmp ( safe_argv [ i ] , " --suicide " ) = = 0 )
d . suicide = true ;
2022-05-25 08:20:13 +03:00
if ( strcmp ( safe_argv [ i ] , " --shutdown " ) = = 0 )
do_shutdown = 1 ;
2014-11-13 01:04:59 +02:00
if ( strcmp ( safe_argv [ i ] , " --version " ) = = 0 )
{
2022-05-25 08:20:13 +03:00
printf ( " Katie: %s \n " , qVersion ( ) ) ;
printf ( " KDE: %s \n " , KDE_VERSION_STRING ) ;
exit ( 0 ) ;
2014-11-13 01:04:59 +02:00
}
if ( strcmp ( safe_argv [ i ] , " --help " ) = = 0 )
{
printf ( " Usage: kdeinit4 [options] \n " ) ;
printf ( " --no-fork Do not fork \n " ) ;
2022-05-22 08:38:55 +03:00
printf ( " --no-klauncher Do not start klauncher \n " ) ;
2014-11-13 01:04:59 +02:00
printf ( " --suicide Terminate when no KDE applications are left running \n " ) ;
2022-05-22 08:38:55 +03:00
printf ( " --version Show version information \n " ) ;
2022-05-25 08:20:13 +03:00
printf ( " --shutdown Shutdown running kdeinit4 instance \n " ) ;
2014-11-13 01:04:59 +02:00
exit ( 0 ) ;
}
}
2022-05-25 08:20:13 +03:00
if ( do_shutdown ) {
KDE_signal ( SIGPIPE , SIG_IGN ) ;
s_instance = new KComponentData ( " kdeinit4 " , QByteArray ( ) , KComponentData : : SkipMainComponentRegistration ) ;
kdeinit_library_path ( ) ;
shutdown_kdeinit ( ) ;
return 0 ;
}
2014-11-13 01:04:59 +02:00
cleanup_fds ( ) ;
// Redirect stdout to stderr. We have no reason to use stdout anyway.
// This minimizes our impact on commands used in pipes.
( void ) dup2 ( 2 , 1 ) ;
if ( do_fork ) {
if ( pipe ( d . initpipe ) ! = 0 ) {
perror ( " kdeinit4: pipe failed " ) ;
return 1 ;
}
// 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 '+')
KDE_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 ;
}
2022-05-26 06:24:47 +03:00
// Make process group leader (for shutting down children later)
2022-05-25 08:20:13 +03:00
setsid ( ) ;
2014-11-13 01:04:59 +02:00
2022-05-26 06:24:47 +03:00
// Create our instance
2014-11-13 01:04:59 +02:00
s_instance = new KComponentData ( " kdeinit4 " , QByteArray ( ) , KComponentData : : SkipMainComponentRegistration ) ;
2022-05-26 06:24:47 +03:00
// Prepare to change process name
2014-11-13 01:04:59 +02:00
# ifndef SKIP_PROCTITLE
2021-09-16 16:27:19 +03:00
proctitle_init ( argc , argv ) ;
2014-11-13 01:04:59 +02:00
# endif
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 matching KComponentData)
Q_ASSERT ( ! KGlobal : : hasMainComponent ( ) ) ;
// don't change envvars before proctitle_init()
unsetenv ( " LD_BIND_NOW " ) ;
d . maxname = strlen ( argv [ 0 ] ) ;
d . launcher_pid = 0 ;
d . wrapper = - 1 ;
d . accepted_fd = - 1 ;
d . debug_wait = false ;
d . launcher_ok = false ;
children = NULL ;
init_signals ( ) ;
# ifdef Q_WS_X11
setupX ( ) ;
# endif
2022-05-26 06:24:47 +03:00
// Create socket for incoming wrapper requests
2022-05-25 08:20:13 +03:00
init_kdeinit_socket ( ) ;
2014-11-13 01:04:59 +02:00
if ( launch_klauncher )
{
start_klauncher ( ) ;
handle_requests ( d . launcher_pid ) ; // Wait for klauncher to be ready
}
# ifdef Q_WS_X11
X11fd = initXconnection ( ) ;
# endif
for ( int i = 1 ; i < argc ; i + + )
{
if ( safe_argv [ i ] [ 0 ] = = ' + ' )
{
pid = launch ( 1 , safe_argv [ i ] + 1 , 0 ) ;
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Launched '%s', pid = %ld result = %d \n " , safe_argv [ i ] + 1 , ( long ) pid , d . result ) ;
# endif
handle_requests ( pid ) ;
}
2015-08-19 01:37:57 +03:00
else if ( safe_argv [ i ] [ 0 ] = = ' - ' )
2014-11-13 01:04:59 +02:00
{
// Ignore
}
else
{
pid = launch ( 1 , safe_argv [ i ] , 0 ) ;
# ifndef NDEBUG
fprintf ( stderr , " kdeinit4: Launched '%s', pid = %ld result = %d \n " , safe_argv [ i ] , ( long ) pid , d . result ) ;
# endif
}
}
2022-05-26 06:24:47 +03:00
// Free argumentsf
2014-11-13 01:04:59 +02:00
for ( int i = 0 ; i < argc ; i + + )
{
free ( safe_argv [ i ] ) ;
}
free ( safe_argv ) ;
# ifndef SKIP_PROCTITLE
proctitle_set ( " kdeinit4 Running... " ) ;
# endif
if ( d . initpipe [ 1 ] ! = - 1 )
{
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 ;
}