• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KInit

kinit.cpp

Go to the documentation of this file.
00001 /*
00002  * This file is part of the KDE libraries
00003  * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org>
00004  *           (c) 1999 Mario Weilguni <mweilguni@sime.com>
00005  *           (c) 2001 Lubos Lunak <l.lunak@kde.org>
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Library General Public
00009  * License version 2 as published by the Free Software Foundation.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Library General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Library General Public License
00017  * along with this library; see the file COPYING.LIB.  If not, write to
00018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  */
00021 
00022 #define QT_NO_CAST_FROM_ASCII
00023 
00024 #include <config.h>
00025 #include <config-kdeinit.h>
00026 
00027 #include <sys/types.h>
00028 #include <sys/time.h>
00029 #include <sys/resource.h>
00030 #include <sys/stat.h>
00031 #include <sys/socket.h>
00032 #include <sys/un.h>
00033 #include <sys/wait.h>
00034 #ifdef HAVE_SYS_SELECT_H
00035 #include <sys/select.h>     // Needed on some systems.
00036 #endif
00037 
00038 #include <ctype.h>
00039 #include <errno.h>
00040 #include <fcntl.h>
00041 #include "proctitle.h"
00042 #include <signal.h>
00043 #include <stdio.h>
00044 #include <stdlib.h>
00045 #include <string.h>
00046 #include <unistd.h>
00047 #include <locale.h>
00048 
00049 #include <QtCore/QLibrary>
00050 #include <QtCore/QString>
00051 #include <QtCore/QFile>
00052 #include <QtCore/QDate>
00053 #include <QtCore/QFileInfo>
00054 #include <QtCore/QRegExp>
00055 #include <QtGui/QFont>
00056 #include <kcomponentdata.h>
00057 #include <klibrary.h>
00058 #include <kdemacros.h>
00059 #include <kstandarddirs.h>
00060 #include <kglobalsettings.h>
00061 #include <kglobal.h>
00062 #include <kconfig.h>
00063 #include <kapplication.h>
00064 #include <klocale.h>
00065 #include <kdebug.h>
00066 #include <kde_file.h>
00067 #include <ksavefile.h>
00068 
00069 #ifdef Q_OS_LINUX
00070 #include <sys/prctl.h>
00071 #ifndef PR_SET_NAME
00072 #define PR_SET_NAME 15
00073 #endif
00074 #endif
00075 
00076 #ifdef Q_WS_MACX
00077 #include <kkernel_mac.h>
00078 #endif
00079 
00080 #include <kdeversion.h>
00081 
00082 #include "klauncher_cmds.h"
00083 
00084 #ifdef Q_WS_X11
00085 #include <X11/Xlib.h>
00086 #include <X11/Xatom.h>
00087 #include <fixx11h.h>
00088 #include <kstartupinfo.h>
00089 #endif
00090 
00091 #ifdef Q_WS_X11
00092 static const char *extra_libs[] = {
00093     "libkio.so.5",
00094     "libkparts.so.4",
00095 #ifdef __KDE_HAVE_GCC_VISIBILITY
00096     "libplasma.so.3"
00097 #endif
00098 };
00099 #endif
00100 
00101 // #define SKIP_PROCTITLE 1
00102 
00103 extern char **environ;
00104 
00105 #ifdef Q_WS_X11
00106 static int X11fd = -1;
00107 static Display *X11display = 0;
00108 static int X11_startup_notify_fd = -1;
00109 static Display *X11_startup_notify_display = 0;
00110 #endif
00111 static KComponentData *s_instance = 0;
00112 #define MAX_SOCK_FILE 255
00113 static char sock_file[MAX_SOCK_FILE];
00114 
00115 #ifdef Q_WS_X11
00116 #define DISPLAY "DISPLAY"
00117 #elif defined(Q_WS_QWS)
00118 #define DISPLAY "QWS_DISPLAY"
00119 #elif defined(Q_WS_MACX)
00120 #define DISPLAY "MAC_DISPLAY"
00121 #elif defined(Q_WS_WIN)
00122 #define DISPLAY "WIN_DISPLAY"
00123 #else
00124 #error Use QT/X11 or QT/Embedded
00125 #endif
00126 
00127 /* Group data */
00128 static struct {
00129   int maxname;
00130   int fd[2];
00131   int launcher[2]; /* socket pair for launcher communication */
00132   int deadpipe[2]; /* pipe used to detect dead children */
00133   int initpipe[2];
00134   int wrapper; /* socket for wrapper communication */
00135   int accepted_fd; /* socket accepted and that must be closed in the child process */
00136   char result;
00137   int exit_status;
00138   pid_t fork;
00139   pid_t launcher_pid;
00140   pid_t kded_pid;
00141   int n;
00142   char **argv;
00143   int (*func)(int, char *[]);
00144   int (*launcher_func)(int);
00145   bool debug_wait;
00146   QByteArray errorMsg;
00147   bool launcher_ok;
00148   bool suicide;
00149 } d;
00150 
00151 struct child
00152 {
00153   pid_t pid;
00154   int sock; /* fd to write message when child is dead*/
00155   struct child *next;
00156 };
00157 
00158 static struct child *children;
00159 
00160 #ifdef Q_WS_X11
00161 extern "C" {
00162 int kdeinit_xio_errhandler( Display * );
00163 int kdeinit_x_errhandler( Display *, XErrorEvent *err );
00164 }
00165 #endif
00166 
00167 #ifdef KDEINIT_OOM_PROTECT
00168 static int oom_pipe = -1;
00169 #endif
00170 
00171 /*
00172  * Clean up the file descriptor table by closing all file descriptors
00173  * that are still open.
00174  *
00175  * This function is called very early in the main() function, so that
00176  * we don't leak anything that was leaked to us.
00177  */
00178 static void cleanup_fds()
00179 {
00180     int maxfd = FD_SETSIZE;
00181     struct rlimit rl;
00182     if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
00183         maxfd = rl.rlim_max;
00184     for (int fd = 3; fd < maxfd; ++fd)
00185     {
00186 #ifdef KDEINIT_OOM_PROTECT
00187        if( fd != oom_pipe )
00188 #endif
00189           close(fd);
00190     }
00191 }
00192 
00193 /*
00194  * Close fd's which are only useful for the parent process.
00195  * Restore default signal handlers.
00196  */
00197 static void close_fds()
00198 {
00199    while (struct child *child = children) {
00200       close(child->sock);
00201       children = child->next;
00202       free(child);
00203    }
00204 
00205    if (d.deadpipe[0] != -1)
00206    {
00207       close(d.deadpipe[0]);
00208       d.deadpipe[0] = -1;
00209    }
00210 
00211    if (d.deadpipe[1] != -1)
00212    {
00213       close(d.deadpipe[1]);
00214       d.deadpipe[1] = -1;
00215    }
00216 
00217    if (d.initpipe[0] != -1)
00218    {
00219       close(d.initpipe[0]);
00220       d.initpipe[0] = -1;
00221    }
00222 
00223    if (d.initpipe[1] != -1)
00224    {
00225       close(d.initpipe[1]);
00226       d.initpipe[1] = -1;
00227    }
00228 
00229    if (d.launcher[0] != -1)
00230    {
00231       close(d.launcher[0]);
00232       d.launcher[0] = -1;
00233    }
00234    if (d.wrapper != -1)
00235    {
00236       close(d.wrapper);
00237       d.wrapper = -1;
00238    }
00239    if (d.accepted_fd != -1)
00240    {
00241       close(d.accepted_fd);
00242       d.accepted_fd = -1;
00243    }
00244 #ifdef Q_WS_X11
00245    if (X11fd >= 0)
00246    {
00247       close(X11fd);
00248       X11fd = -1;
00249    }
00250    if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd )
00251    {
00252       close(X11_startup_notify_fd);
00253       X11_startup_notify_fd = -1;
00254    }
00255 #endif
00256 
00257    KDE_signal(SIGCHLD, SIG_DFL);
00258    KDE_signal(SIGPIPE, SIG_DFL);
00259 }
00260 
00261 /* Notify wrapper program that the child it started has finished. */
00262 static void child_died(pid_t exit_pid, int exit_status)
00263 {
00264    struct child *child, **childptr = &children;
00265 
00266    while ((child = *childptr))
00267    {
00268       if (child->pid == exit_pid)
00269       {
00270          /* Send a message with the return value of the child on the control socket */
00271          klauncher_header request_header;
00272          long request_data[2];
00273          request_header.cmd = LAUNCHER_CHILD_DIED;
00274          request_header.arg_length = sizeof(long) * 2;
00275          request_data[0] = exit_pid;
00276          request_data[1] = exit_status;
00277          write(child->sock, &request_header, sizeof(request_header));
00278          write(child->sock, request_data, request_header.arg_length);
00279          close(child->sock);
00280 
00281          *childptr = child->next;
00282          free(child);
00283          return;
00284       }
00285 
00286       childptr = &child->next;
00287    }
00288 }
00289 
00290 
00291 static void exitWithErrorMsg(const QString &errorMsg)
00292 {
00293    fprintf( stderr, "%s\n", errorMsg.toLocal8Bit().data() );
00294    QByteArray utf8ErrorMsg = errorMsg.toUtf8();
00295    d.result = 3; // Error with msg
00296    write(d.fd[1], &d.result, 1);
00297    int l = utf8ErrorMsg.length();
00298    write(d.fd[1], &l, sizeof(int));
00299    write(d.fd[1], utf8ErrorMsg.data(), l);
00300    close(d.fd[1]);
00301    exit(255);
00302 }
00303 
00304 static void setup_tty( const char* tty )
00305 {
00306     if( tty == NULL || *tty == '\0' )
00307         return;
00308     int fd = KDE_open( tty, O_WRONLY );
00309     if( fd < 0 )
00310     {
00311         perror( "kdeinit4: could not open() tty" );
00312         return;
00313     }
00314     if( dup2( fd, STDOUT_FILENO ) < 0 )
00315     {
00316         perror( "kdeinit4: could not dup2() stdout tty" );
00317     }
00318     if( dup2( fd, STDERR_FILENO ) < 0 )
00319     {
00320         perror( "kdeinit4: could not dup2() stderr tty" );
00321     }
00322     close( fd );
00323 }
00324 
00325 // from kdecore/netwm.cpp
00326 static int get_current_desktop( Display* disp )
00327 {
00328     int desktop = 0; // no desktop by default
00329 #ifdef Q_WS_X11 // Only X11 supports multiple desktops
00330     Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP", False );
00331     Atom type_ret;
00332     int format_ret;
00333     unsigned char *data_ret;
00334     unsigned long nitems_ret, unused;
00335     if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop,
00336         0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret )
00337         == Success)
00338     {
00339     if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1)
00340         desktop = *((long *) data_ret) + 1;
00341         if (data_ret)
00342             XFree ((char*) data_ret);
00343     }
00344 #endif
00345     return desktop;
00346 }
00347 
00348 // var has to be e.g. "DISPLAY=", i.e. with =
00349 const char* get_env_var( const char* var, int envc, const char* envs )
00350 {
00351     if( envc > 0 )
00352     { // get the var from envs
00353         const char* env_l = envs;
00354         int ln = strlen( var );
00355         for (int i = 0;  i < envc; i++)
00356         {
00357             if( strncmp( env_l, var, ln ) == 0 )
00358                 return env_l + ln;
00359             while(*env_l != 0) env_l++;
00360                 env_l++;
00361         }
00362     }
00363     return NULL;
00364 }
00365 
00366 #ifdef Q_WS_X11
00367 static void init_startup_info( KStartupInfoId& id, const char* bin,
00368     int envc, const char* envs )
00369 {
00370     const char* dpy = get_env_var( DISPLAY"=", envc, envs );
00371     // this may be called in a child, so it can't use display open using X11display
00372     // also needed for multihead
00373     X11_startup_notify_display = XOpenDisplay( dpy );
00374     if( X11_startup_notify_display == NULL )
00375         return;
00376     X11_startup_notify_fd = XConnectionNumber( X11_startup_notify_display );
00377     KStartupInfoData data;
00378     int desktop = get_current_desktop( X11_startup_notify_display );
00379     data.setDesktop( desktop );
00380     data.setBin(QFile::decodeName(bin));
00381     KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
00382     XFlush( X11_startup_notify_display );
00383 }
00384 
00385 static void complete_startup_info( KStartupInfoId& id, pid_t pid )
00386 {
00387     if( X11_startup_notify_display == NULL )
00388         return;
00389     if( pid == 0 ) // failure
00390         KStartupInfo::sendFinishX( X11_startup_notify_display, id );
00391     else
00392     {
00393         KStartupInfoData data;
00394         data.addPid( pid );
00395         data.setHostname();
00396         KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
00397     }
00398     XCloseDisplay( X11_startup_notify_display );
00399     X11_startup_notify_display = NULL;
00400     X11_startup_notify_fd = -1;
00401 }
00402 #endif
00403 
00404 QByteArray execpath_avoid_loops( const QByteArray& exec, int envc, const char* envs, bool avoid_loops )
00405 {
00406      QStringList paths;
00407      const QRegExp pathSepRegExp(QString::fromLatin1("[:\b]"));
00408      if( envc > 0 ) /* use the passed environment */
00409      {
00410          const char* path = get_env_var( "PATH=", envc, envs );
00411          if( path != NULL )
00412              paths = QFile::decodeName(path).split(pathSepRegExp);
00413      } else {
00414          paths = QString::fromLocal8Bit(qgetenv("PATH")).split(pathSepRegExp, QString::KeepEmptyParts);
00415      }
00416      QString execpath =
00417          s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(QLatin1String(":")));
00418      if (avoid_loops && !execpath.isEmpty()) {
00419          const int pos = execpath.lastIndexOf(QLatin1Char('/'));
00420          const QString bin_path = execpath.left(pos);
00421          for( QStringList::Iterator it = paths.begin();
00422               it != paths.end();
00423               ++it ) {
00424              if( *it == bin_path || *it == bin_path + QLatin1Char('/')) {
00425                  paths.erase( it );
00426                  break; // -->
00427              }
00428          }
00429          execpath = s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(QLatin1String(":")));
00430      }
00431      return QFile::encodeName(execpath);
00432 }
00433 
00434 #ifdef KDEINIT_OOM_PROTECT
00435 static void oom_protect_sighandler( int ) {
00436 }
00437 
00438 static void reset_oom_protect() {
00439    if( oom_pipe <= 0 )
00440       return;
00441    struct sigaction act, oldact;
00442    act.sa_handler = oom_protect_sighandler;
00443    act.sa_flags = 0;
00444    sigemptyset( &act.sa_mask );
00445    sigaction( SIGUSR1, &act, &oldact );
00446    sigset_t sigs, oldsigs;
00447    sigemptyset( &sigs );
00448    sigaddset( &sigs, SIGUSR1 );
00449    sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
00450    pid_t pid = getpid();
00451    if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) {
00452       sigsuspend( &oldsigs ); // wait for the signal to come
00453    } else {
00454 #ifndef NDEBUG
00455       fprintf( stderr, "Failed to reset OOM protection: %d\n", pid );
00456 #endif
00457    }
00458    sigprocmask( SIG_SETMASK, &oldsigs, NULL );
00459    sigaction( SIGUSR1, &oldact, NULL );
00460    close( oom_pipe );
00461    oom_pipe = -1;
00462 }
00463 #else
00464 static void reset_oom_protect() {
00465 }
00466 #endif
00467 
00468 static pid_t launch(int argc, const char *_name, const char *args,
00469                     const char *cwd=0, int envc=0, const char *envs=0,
00470                     bool reset_env = false,
00471                     const char *tty=0, bool avoid_loops = false,
00472                     const char* startup_id_str = "0" ) // krazy:exclude=doublequote_chars
00473 {
00474     QString lib;
00475     QByteArray name;
00476     QByteArray exec;
00477     QString libpath;
00478     QByteArray execpath;
00479 
00480     if (_name[0] != '/') {
00481         name = _name;
00482         lib = QFile::decodeName(name);
00483         exec = name;
00484         KLibrary klib(QLatin1String("libkdeinit4_") + lib, *s_instance );
00485         libpath = klib.fileName();
00486         if( libpath.isEmpty()) {
00487             KLibrary klib(lib, *s_instance);
00488             libpath = klib.fileName();
00489         }
00490         execpath = execpath_avoid_loops(exec, envc, envs, avoid_loops);
00491     } else {
00492         name = _name;
00493         lib = QFile::decodeName(name);
00494         name = name.mid(name.lastIndexOf('/') + 1);
00495         exec = _name;
00496         if (lib.endsWith(QLatin1String(".so")))
00497             libpath = lib;
00498         else {
00499             // try to match an absolute path to an executable binary (either in bin/ or in libexec/)
00500             // to a kdeinit module in the same prefix
00501             if( lib.contains( QLatin1String( "/lib" KDELIBSUFF "/kde4/libexec/" ))) {
00502                 libpath = QString( lib ).replace( QLatin1String( "/lib" KDELIBSUFF "/kde4/libexec/" ),
00503                     QLatin1String("/lib" KDELIBSUFF "/libkdeinit4_")) + QLatin1String(".so");
00504             } else if( lib.contains( QLatin1String( "/bin/" ))) {
00505                 libpath = QString( lib ).replace( QLatin1String( "/bin/" ),
00506                     QLatin1String("/lib" KDELIBSUFF "/libkdeinit4_")) + QLatin1String(".so");
00507             }
00508             // Don't confuse the user with "Could not load libkdeinit4_foo.so" if it doesn't exist
00509             if (!QFile::exists(libpath)) {
00510                 libpath.clear();
00511             }
00512             execpath = exec;
00513         }
00514     }
00515 #ifndef NDEBUG
00516     fprintf(stderr,"kdeinit4: preparing to launch %s\n", libpath.isEmpty()
00517         ? execpath.constData() : libpath.toUtf8().constData());
00518 #endif
00519     if (!args) {
00520         argc = 1;
00521     }
00522 
00523   if (0 > pipe(d.fd))
00524   {
00525      perror("kdeinit4: pipe() failed");
00526      d.result = 3;
00527      d.errorMsg = i18n("Unable to start new process.\n"
00528                        "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();
00529      d.fork = 0;
00530      return d.fork;
00531   }
00532 
00533 #ifdef Q_WS_X11
00534   KStartupInfoId startup_id;
00535   startup_id.initId( startup_id_str );
00536   if( !startup_id.none())
00537       init_startup_info( startup_id, name, envc, envs );
00538 #endif
00539   // find out these paths before forking, doing it afterwards
00540   // crashes on some platforms, notably OSX
00541   const QByteArray docPath = QFile::encodeName(KGlobalSettings::documentPath());
00542 #ifdef Q_WS_MAC
00543   const QString bundlepath = s_instance->dirs()->findExe(QFile::decodeName(execpath));
00544 #endif
00545 
00546   d.errorMsg = 0;
00547   d.fork = fork();
00548   switch(d.fork) {
00549   case -1:
00550      perror("kdeinit4: fork() failed");
00551      d.result = 3;
00552      d.errorMsg = i18n("Unable to create new process.\n"
00553                        "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();
00554      close(d.fd[0]);
00555      close(d.fd[1]);
00556      d.fork = 0;
00557      break;
00558   case 0:
00559   {
00561      close(d.fd[0]);
00562      close_fds();
00563      reset_oom_protect();
00564 
00565      // Try to chdir, either to the requested directory or to the user's document path by default.
00566      // We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist,
00567      // we still want to execute `foo` even if the chdir() failed.
00568      if (cwd && *cwd) {
00569          (void)chdir(cwd);
00570      } else {
00571          // on Maemo5, documentPath() is on the SD card, setting it as working directory would block
00572          // USB mass storage access
00573 #ifndef Q_WS_MAEMO_5
00574          (void)chdir(docPath.constData());
00575 #endif
00576      }
00577 
00578      if( reset_env ) // KWRAPPER/SHELL
00579      {
00580 
00581          QList<QByteArray> unset_envs;
00582          for( int tmp_env_count = 0;
00583               environ[tmp_env_count];
00584               tmp_env_count++)
00585              unset_envs.append( environ[ tmp_env_count ] );
00586          foreach(const QByteArray &tmp, unset_envs)
00587          {
00588              int pos = tmp.indexOf( '=' );
00589              if( pos >= 0 )
00590                  unsetenv( tmp.left( pos ));
00591          }
00592      }
00593 
00594      for (int i = 0;  i < envc; i++)
00595      {
00596         putenv((char *)envs);
00597         while(*envs != 0) envs++;
00598         envs++;
00599      }
00600 
00601 #ifdef Q_WS_X11
00602       if( startup_id.none())
00603           KStartupInfo::resetStartupEnv();
00604       else
00605           startup_id.setupStartupEnv();
00606 #endif
00607      {
00608        int r;
00609        QByteArray procTitle;
00610        d.argv = (char **) malloc(sizeof(char *) * (argc+1));
00611        d.argv[0] = (char *) _name;
00612 #ifdef Q_WS_MAC
00613        QString argvexe = s_instance->dirs()->findExe(QString::fromLatin1(d.argv[0]));
00614        if (!argvexe.isEmpty()) {
00615           QByteArray cstr = argvexe.toLocal8Bit();
00616           kDebug(7016) << "kdeinit4: launch() setting argv: " << cstr.data();
00617           d.argv[0] = strdup(cstr.data());
00618        }
00619 #endif
00620        for (int i = 1;  i < argc; i++)
00621        {
00622           d.argv[i] = (char *) args;
00623           procTitle += ' ';
00624           procTitle += (char *) args;
00625           while(*args != 0) args++;
00626           args++;
00627        }
00628        d.argv[argc] = 0;
00629 
00630 #ifndef SKIP_PROCTITLE
00631 
00632 #ifdef Q_OS_LINUX
00633        /* set the process name, so that killall works like intended */
00634        r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0);
00635        if ( r == 0 )
00636            proctitle_set( "%s [kdeinit]%s", name.data(), procTitle.data() ? procTitle.data() : "" );
00637        else
00638            proctitle_set( "kdeinit4: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
00639 #else
00640        proctitle_set( "kdeinit4: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
00641 #endif
00642 #endif
00643      }
00644 
00645      if (libpath.isEmpty() && execpath.isEmpty())
00646      {
00647         QString errorMsg = i18n("Could not find '%1' executable.", QFile::decodeName(_name));
00648         exitWithErrorMsg(errorMsg);
00649      }
00650 
00651 
00652      if ( !qgetenv("KDE_IS_PRELINKED").isEmpty() && !execpath.isEmpty())
00653          libpath.truncate(0);
00654 
00655      QLibrary l(libpath);
00656 
00657      if ( !libpath.isEmpty() )
00658      {
00659        if (!l.load() || !l.isLoaded() )
00660        {
00661           QString ltdlError (l.errorString());
00662           if (execpath.isEmpty())
00663           {
00664              // Error
00665              QString errorMsg = i18n("Could not open library '%1'.\n%2", libpath, ltdlError);
00666              exitWithErrorMsg(errorMsg);
00667           }
00668           else
00669           {
00670              // Print warning
00671              fprintf(stderr, "Could not open library %s: %s\n", qPrintable(lib),
00672                      qPrintable(ltdlError) );
00673           }
00674        }
00675      }
00676      if (!l.isLoaded())
00677      {
00678         d.result = 2; // Try execing
00679         write(d.fd[1], &d.result, 1);
00680 
00681         // We set the close on exec flag.
00682         // Closing of d.fd[1] indicates that the execvp succeeded!
00683         fcntl(d.fd[1], F_SETFD, FD_CLOEXEC);
00684 
00685         setup_tty( tty );
00686 
00687         QByteArray executable = execpath;
00688 #ifdef Q_WS_MAC
00689         if (!bundlepath.isEmpty())
00690            executable = QFile::encodeName(bundlepath);
00691 #endif
00692 
00693         if (!executable.isEmpty())
00694            execvp(executable, d.argv);
00695 
00696         d.result = 1; // Error
00697         write(d.fd[1], &d.result, 1);
00698         close(d.fd[1]);
00699         exit(255);
00700      }
00701 
00702      void * sym = l.resolve( "kdeinitmain");
00703      if (!sym )
00704         {
00705         sym = l.resolve( "kdemain" );
00706         if ( !sym )
00707            {
00708             QString ltdlError = l.errorString();
00709             fprintf(stderr, "Could not find kdemain: %s\n", qPrintable(ltdlError) );
00710               QString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2",
00711                                       libpath, ltdlError);
00712               exitWithErrorMsg(errorMsg);
00713            }
00714         }
00715 
00716      d.result = 0; // Success
00717      write(d.fd[1], &d.result, 1);
00718      close(d.fd[1]);
00719 
00720      d.func = (int (*)(int, char *[])) sym;
00721      if (d.debug_wait)
00722      {
00723         fprintf(stderr, "kdeinit4: Suspending process\n"
00724                         "kdeinit4: 'gdb kdeinit4 %d' to debug\n"
00725                         "kdeinit4: 'kill -SIGCONT %d' to continue\n",
00726                         getpid(), getpid());
00727         kill(getpid(), SIGSTOP);
00728      }
00729      else
00730      {
00731         setup_tty( tty );
00732      }
00733 
00734      exit( d.func(argc, d.argv)); /* Launch! */
00735 
00736      break;
00737   }
00738   default:
00740      close(d.fd[1]);
00741      bool exec = false;
00742      for(;;)
00743      {
00744        d.n = read(d.fd[0], &d.result, 1);
00745        if (d.n == 1)
00746        {
00747           if (d.result == 2)
00748           {
00749 #ifndef NDEBUG
00750              //fprintf(stderr, "kdeinit4: no kdeinit module, trying exec....\n");
00751 #endif
00752              exec = true;
00753              continue;
00754           }
00755           if (d.result == 3)
00756           {
00757              int l = 0;
00758              d.n = read(d.fd[0], &l, sizeof(int));
00759              if (d.n == sizeof(int))
00760              {
00761                 QByteArray tmp;
00762                 tmp.resize(l+1);
00763                 d.n = read(d.fd[0], tmp.data(), l);
00764                 tmp[l] = 0;
00765                 if (d.n == l)
00766                    d.errorMsg = tmp;
00767              }
00768           }
00769           // Finished
00770           break;
00771        }
00772        if (d.n == -1)
00773        {
00774           if (errno == ECHILD) {  // a child died.
00775              continue;
00776           }
00777           if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read
00778              continue;
00779           }
00780        }
00781        if (d.n == 0)
00782        {
00783           if (exec) {
00784              d.result = 0;
00785           } else {
00786              fprintf(stderr,"kdeinit4: (%s %s) Pipe closed unexpectedly", name.constData(), execpath.constData());
00787              perror("kdeinit4: Pipe closed unexpectedly");
00788              d.result = 1; // Error
00789           }
00790           break;
00791        }
00792        perror("kdeinit4: Error reading from pipe");
00793        d.result = 1; // Error
00794        break;
00795      }
00796      close(d.fd[0]);
00797   }
00798 #ifdef Q_WS_X11
00799   if( !startup_id.none())
00800   {
00801      if( d.fork && d.result == 0 ) // launched successfully
00802         complete_startup_info( startup_id, d.fork );
00803      else // failure, cancel ASN
00804         complete_startup_info( startup_id, 0 );
00805   }
00806 #endif
00807   return d.fork;
00808 }
00809 
00810 extern "C" {
00811 
00812 static void sig_child_handler(int)
00813 {
00814    /*
00815     * Write into the pipe of death.
00816     * This way we are sure that we return from the select()
00817     *
00818     * A signal itself causes select to return as well, but
00819     * this creates a race-condition in case the signal arrives
00820     * just before we enter the select.
00821     */
00822    char c = 0;
00823    write(d.deadpipe[1], &c, 1);
00824 }
00825 
00826 }
00827 
00828 static void init_signals()
00829 {
00830   struct sigaction act;
00831   long options;
00832 
00833   if (pipe(d.deadpipe) != 0)
00834   {
00835      perror("kdeinit4: Aborting. Can not create pipe");
00836      exit(255);
00837   }
00838 
00839   options = fcntl(d.deadpipe[0], F_GETFL);
00840   if (options == -1)
00841   {
00842      perror("kdeinit4: Aborting. Can not make pipe non-blocking");
00843      exit(255);
00844   }
00845 
00846   if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1)
00847   {
00848      perror("kdeinit4: Aborting. Can not make pipe non-blocking");
00849      exit(255);
00850   }
00851 
00852   /*
00853    * A SIGCHLD handler is installed which sends a byte into the
00854    * pipe of death. This is to ensure that a dying child causes
00855    * an exit from select().
00856    */
00857   act.sa_handler=sig_child_handler;
00858   sigemptyset(&(act.sa_mask));
00859   sigaddset(&(act.sa_mask), SIGCHLD);
00860   sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
00861   act.sa_flags = SA_NOCLDSTOP;
00862 
00863   // CC: take care of SunOS which automatically restarts interrupted system
00864   // calls (and thus does not have SA_RESTART)
00865 
00866 #ifdef SA_RESTART
00867   act.sa_flags |= SA_RESTART;
00868 #endif
00869   sigaction( SIGCHLD, &act, 0L);
00870 
00871   act.sa_handler=SIG_IGN;
00872   sigemptyset(&(act.sa_mask));
00873   sigaddset(&(act.sa_mask), SIGPIPE);
00874   sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
00875   act.sa_flags = 0;
00876   sigaction( SIGPIPE, &act, 0L);
00877 }
00878 
00879 static void init_kdeinit_socket()
00880 {
00881   struct sockaddr_un sa;
00882   kde_socklen_t socklen;
00883   long options;
00884   const QByteArray home_dir = qgetenv("HOME");
00885   int max_tries = 10;
00886   if (home_dir.isEmpty())
00887   {
00888      fprintf(stderr, "kdeinit4: Aborting. $HOME not set!");
00889      exit(255);
00890   }
00891   if (chdir(home_dir) != 0) {
00892      fprintf(stderr, "kdeinit4: Aborting. Couldn't enter '%s'!", home_dir.constData());
00893      exit(255);
00894   }
00895 
00896   {
00897      QByteArray path = home_dir;
00898      QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
00899      if (access(path.data(), R_OK|W_OK))
00900      {
00901        if (errno == ENOENT)
00902        {
00903           fprintf(stderr, "kdeinit4: Aborting. $HOME directory (%s) does not exist.\n", path.data());
00904           exit(255);
00905        }
00906        else if (readOnly.isEmpty())
00907        {
00908           fprintf(stderr, "kdeinit4: Aborting. No write access to $HOME directory (%s).\n", path.data());
00909           exit(255);
00910        }
00911      }
00912 #if 0 // obsolete in kde4. Should we check writing to another file instead?
00913      path = qgetenv("ICEAUTHORITY");
00914      if (path.isEmpty())
00915      {
00916         path = home_dir;
00917         path += "/.ICEauthority";
00918      }
00919      if (access(path.data(), R_OK|W_OK) && (errno != ENOENT))
00920      {
00921        fprintf(stderr, "kdeinit4: Aborting. No write access to '%s'.\n", path.data());
00922        exit(255);
00923      }
00924 #endif
00925   }
00926 
00931   if (access(sock_file, W_OK) == 0)
00932   {
00933      int s;
00934      struct sockaddr_un server;
00935 
00936 //     fprintf(stderr, "kdeinit4: Warning, socket_file already exists!\n");
00937      /*
00938       * create the socket stream
00939       */
00940      s = socket(PF_UNIX, SOCK_STREAM, 0);
00941      if (s < 0)
00942      {
00943         perror("socket() failed");
00944         exit(255);
00945      }
00946      server.sun_family = AF_UNIX;
00947      strcpy(server.sun_path, sock_file);
00948      socklen = sizeof(server);
00949 
00950      if(connect(s, (struct sockaddr *)&server, socklen) == 0)
00951      {
00952         fprintf(stderr, "kdeinit4: Shutting down running client.\n");
00953         klauncher_header request_header;
00954         request_header.cmd = LAUNCHER_TERMINATE_KDEINIT;
00955         request_header.arg_length = 0;
00956         write(s, &request_header, sizeof(request_header));
00957         sleep(1); // Give it some time
00958      }
00959      close(s);
00960   }
00961 
00963   unlink(sock_file);
00964 
00966   d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0);
00967   if (d.wrapper < 0)
00968   {
00969      perror("kdeinit4: Aborting. socket() failed");
00970      exit(255);
00971   }
00972 
00973   options = fcntl(d.wrapper, F_GETFL);
00974   if (options == -1)
00975   {
00976      perror("kdeinit4: Aborting. Can not make socket non-blocking");
00977      close(d.wrapper);
00978      exit(255);
00979   }
00980 
00981   if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1)
00982   {
00983      perror("kdeinit4: Aborting. Can not make socket non-blocking");
00984      close(d.wrapper);
00985      exit(255);
00986   }
00987 
00988   while (1) {
00990       socklen = sizeof(sa);
00991       memset(&sa, 0, socklen);
00992       sa.sun_family = AF_UNIX;
00993       strcpy(sa.sun_path, sock_file);
00994       if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0)
00995       {
00996           if (max_tries == 0) {
00997           perror("kdeinit4: Aborting. bind() failed");
00998           fprintf(stderr, "Could not bind to socket '%s'\n", sock_file);
00999           close(d.wrapper);
01000           exit(255);
01001       }
01002       max_tries--;
01003       } else
01004           break;
01005   }
01006 
01008   if (chmod(sock_file, 0600) != 0)
01009   {
01010      perror("kdeinit4: Aborting. Can not set permissions on socket");
01011      fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
01012      unlink(sock_file);
01013      close(d.wrapper);
01014      exit(255);
01015   }
01016 
01017   if(listen(d.wrapper, SOMAXCONN) < 0)
01018   {
01019      perror("kdeinit4: Aborting. listen() failed");
01020      unlink(sock_file);
01021      close(d.wrapper);
01022      exit(255);
01023   }
01024 }
01025 
01026 /*
01027  * Read 'len' bytes from 'sock' into buffer.
01028  * returns 0 on success, -1 on failure.
01029  */
01030 static int read_socket(int sock, char *buffer, int len)
01031 {
01032   ssize_t result;
01033   int bytes_left = len;
01034   while ( bytes_left > 0)
01035   {
01036      result = read(sock, buffer, bytes_left);
01037      if (result > 0)
01038      {
01039         buffer += result;
01040         bytes_left -= result;
01041      }
01042      else if (result == 0)
01043         return -1;
01044      else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
01045         return -1;
01046   }
01047   return 0;
01048 }
01049 
01050 static void start_klauncher()
01051 {
01052     if (socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher) < 0) {
01053         perror("kdeinit4: socketpair() failed");
01054         exit(255);
01055     }
01056     char args[32];
01057     strcpy(args, "--fd=");
01058     sprintf(args + 5, "%d", d.launcher[1]);
01059     d.launcher_pid = launch( 2, "klauncher", args );
01060     close(d.launcher[1]);
01061 #ifndef NDEBUG
01062     fprintf(stderr, "kdeinit4: Launched KLauncher, pid = %ld, result = %d\n",
01063                     (long) d.launcher_pid, d.result);
01064 #endif
01065 }
01066 
01067 static void launcher_died()
01068 {
01069    if (!d.launcher_ok)
01070    {
01071       /* This is bad. */
01072       fprintf(stderr, "kdeinit4: Communication error with launcher. Exiting!\n");
01073       ::exit(255);
01074       return;
01075    }
01076 
01077    // KLauncher died... restart
01078 #ifndef NDEBUG
01079    fprintf(stderr, "kdeinit4: KLauncher died unexpectedly.\n");
01080 #endif
01081    // Make sure it's really dead.
01082    if (d.launcher_pid)
01083    {
01084       kill(d.launcher_pid, SIGKILL);
01085       sleep(1); // Give it some time
01086    }
01087 
01088    d.launcher_ok = false;
01089    d.launcher_pid = 0;
01090    close(d.launcher[0]);
01091    d.launcher[0] = -1;
01092 
01093    start_klauncher();
01094 }
01095 
01096 static bool handle_launcher_request(int sock, const char *who)
01097 {
01098    (void)who; // for NDEBUG
01099 
01100    klauncher_header request_header;
01101    char *request_data = 0L;
01102    int result = read_socket(sock, (char *) &request_header, sizeof(request_header));
01103    if (result != 0)
01104    {
01105       return false;
01106    }
01107 
01108    if ( request_header.arg_length != 0 )
01109    {
01110        request_data = (char *) malloc(request_header.arg_length);
01111 
01112        result = read_socket(sock, request_data, request_header.arg_length);
01113        if (result != 0)
01114        {
01115            free(request_data);
01116            return false;
01117        }
01118    }
01119 
01120    //kDebug() << "Got cmd" << request_header.cmd << commandToString(request_header.cmd);
01121    if (request_header.cmd == LAUNCHER_OK)
01122    {
01123       d.launcher_ok = true;
01124    }
01125    else if (request_header.arg_length &&
01126       ((request_header.cmd == LAUNCHER_EXEC) ||
01127        (request_header.cmd == LAUNCHER_EXT_EXEC) ||
01128        (request_header.cmd == LAUNCHER_SHELL ) ||
01129        (request_header.cmd == LAUNCHER_KWRAPPER) ||
01130        (request_header.cmd == LAUNCHER_EXEC_NEW)))
01131    {
01132       pid_t pid;
01133       klauncher_header response_header;
01134       long response_data;
01135       long l;
01136       memcpy( &l, request_data, sizeof( long ));
01137       int argc = l;
01138       const char *name = request_data + sizeof(long);
01139       const char *args = name + strlen(name) + 1;
01140       const char *cwd = 0;
01141       int envc = 0;
01142       const char *envs = 0;
01143       const char *tty = 0;
01144       int avoid_loops = 0;
01145       const char *startup_id_str = "0"; // krazy:exclude=doublequote_chars
01146 
01147 #ifndef NDEBUG
01148      fprintf(stderr, "kdeinit4: Got %s '%s' from %s.\n",
01149              commandToString(request_header.cmd),
01150              name, who);
01151 #endif
01152 
01153       const char *arg_n = args;
01154       for(int i = 1; i < argc; i++)
01155       {
01156         arg_n = arg_n + strlen(arg_n) + 1;
01157       }
01158 
01159       if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER )
01160       {
01161          // Shell or kwrapper
01162          cwd = arg_n; arg_n += strlen(cwd) + 1;
01163       }
01164       if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
01165           || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
01166       {
01167          memcpy( &l, arg_n, sizeof( long ));
01168          envc = l;
01169          arg_n += sizeof(long);
01170          envs = arg_n;
01171          for(int i = 0; i < envc; i++)
01172          {
01173            arg_n = arg_n + strlen(arg_n) + 1;
01174          }
01175          if( request_header.cmd == LAUNCHER_KWRAPPER )
01176          {
01177              tty = arg_n;
01178              arg_n += strlen( tty ) + 1;
01179          }
01180       }
01181 
01182      if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
01183          || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
01184      {
01185          memcpy( &l, arg_n, sizeof( long ));
01186          avoid_loops = l;
01187          arg_n += sizeof( long );
01188      }
01189 
01190      if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
01191          || request_header.cmd == LAUNCHER_EXT_EXEC )
01192      {
01193          startup_id_str = arg_n;
01194          arg_n += strlen( startup_id_str ) + 1;
01195      }
01196 
01197      if ((request_header.arg_length > (arg_n - request_data)) &&
01198          (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ))
01199      {
01200          // Optional cwd
01201          cwd = arg_n; arg_n += strlen(cwd) + 1;
01202      }
01203 
01204      if ((arg_n - request_data) != request_header.arg_length)
01205      {
01206 #ifndef NDEBUG
01207        fprintf(stderr, "kdeinit4: EXEC request has invalid format.\n");
01208 #endif
01209        free(request_data);
01210        d.debug_wait = false;
01211        return true; // sure?
01212      }
01213 
01214       // support for the old a bit broken way of setting DISPLAY for multihead
01215       QByteArray olddisplay = qgetenv(DISPLAY);
01216       QByteArray kdedisplay = qgetenv("KDE_DISPLAY");
01217       bool reset_display = (! olddisplay.isEmpty() &&
01218                             ! kdedisplay.isEmpty() &&
01219                             olddisplay != kdedisplay);
01220 
01221       if (reset_display)
01222           setenv(DISPLAY, kdedisplay, true);
01223 
01224       pid = launch( argc, name, args, cwd, envc, envs,
01225           request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER,
01226           tty, avoid_loops, startup_id_str );
01227 
01228       if (reset_display) {
01229           unsetenv("KDE_DISPLAY");
01230           setenv(DISPLAY, olddisplay, true);
01231       }
01232 
01233       if (pid && (d.result == 0))
01234       {
01235          response_header.cmd = LAUNCHER_OK;
01236          response_header.arg_length = sizeof(response_data);
01237          response_data = pid;
01238          write(sock, &response_header, sizeof(response_header));
01239          write(sock, &response_data, response_header.arg_length);
01240 
01241          /* add new child to list */
01242          struct child *child = (struct child *) malloc(sizeof(struct child));
01243          child->pid = pid;
01244          child->sock = dup(sock);
01245          child->next = children;
01246          children = child;
01247       }
01248       else
01249       {
01250          int l = d.errorMsg.length();
01251          if (l) l++; // Include trailing null.
01252          response_header.cmd = LAUNCHER_ERROR;
01253          response_header.arg_length = l;
01254          write(sock, &response_header, sizeof(response_header));
01255          if (l)
01256             write(sock, d.errorMsg.data(), l);
01257       }
01258       d.debug_wait = false;
01259    }
01260    else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV)
01261    {
01262       const char *env_name;
01263       const char *env_value;
01264       env_name = request_data;
01265       env_value = env_name + strlen(env_name) + 1;
01266 
01267 #ifndef NDEBUG
01268       fprintf(stderr, "kdeinit4: Got SETENV '%s=%s' from %s.\n", env_name, env_value, who);
01269 #endif
01270 
01271       if ( request_header.arg_length !=
01272           (int) (strlen(env_name) + strlen(env_value) + 2))
01273       {
01274 #ifndef NDEBUG
01275          fprintf(stderr, "kdeinit4: SETENV request has invalid format.\n");
01276 #endif
01277          free(request_data);
01278          return true; // sure?
01279       }
01280       setenv( env_name, env_value, 1);
01281    }
01282    else if (request_header.cmd == LAUNCHER_TERMINATE_KDE)
01283    {
01284 #ifndef NDEBUG
01285        fprintf(stderr,"kdeinit4: terminate KDE.\n");
01286 #endif
01287 #ifdef Q_WS_X11
01288        kdeinit_xio_errhandler( 0L );
01289 #endif
01290    }
01291    else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT)
01292    {
01293 #ifndef NDEBUG
01294       fprintf(stderr,"kdeinit4: Got termination request (PID %ld).\n", (long) getpid());
01295 #endif
01296       if (d.launcher_pid) {
01297          kill(d.launcher_pid, SIGTERM);
01298          d.launcher_pid = 0;
01299          close(d.launcher[0]);
01300          d.launcher[0] = -1;
01301       }
01302       unlink(sock_file);
01303       if (children) {
01304          close(d.wrapper);
01305          d.wrapper = -1;
01306 #ifndef NDEBUG
01307          fprintf(stderr,"kdeinit4: Closed sockets, but not exiting until all children terminate.\n");
01308 #endif
01309       } else {
01310          raise(SIGTERM);
01311       }
01312    }
01313    else if (request_header.cmd == LAUNCHER_DEBUG_WAIT)
01314    {
01315 #ifndef NDEBUG
01316        fprintf(stderr,"kdeinit4: Debug wait activated.\n");
01317 #endif
01318        d.debug_wait = true;
01319    }
01320    if (request_data)
01321        free(request_data);
01322    return true;
01323 }
01324 
01325 static void handle_requests(pid_t waitForPid)
01326 {
01327    int max_sock = d.deadpipe[0];
01328    if (d.wrapper > max_sock)
01329       max_sock = d.wrapper;
01330    if (d.launcher[0] > max_sock)
01331       max_sock = d.launcher[0];
01332 #ifdef Q_WS_X11
01333    if (X11fd > max_sock)
01334       max_sock = X11fd;
01335 #endif
01336    max_sock++;
01337 
01338    while(1)
01339    {
01340       fd_set rd_set;
01341       fd_set wr_set;
01342       fd_set e_set;
01343       int result;
01344       pid_t exit_pid;
01345       int exit_status;
01346       char c;
01347 
01348       /* Flush the pipe of death */
01349       while( read(d.deadpipe[0], &c, 1) == 1)
01350         {}
01351 
01352       /* Handle dying children */
01353       do {
01354         exit_pid = waitpid(-1, &exit_status, WNOHANG);
01355         if (exit_pid > 0)
01356         {
01357 #ifndef NDEBUG
01358            fprintf(stderr, "kdeinit4: PID %ld terminated.\n", (long) exit_pid);
01359 #endif
01360            if (waitForPid && (exit_pid == waitForPid))
01361               return;
01362 
01363            if( WIFEXITED( exit_status )) // fix process return value
01364                exit_status = WEXITSTATUS(exit_status);
01365            else if( WIFSIGNALED( exit_status ))
01366                exit_status = 128 + WTERMSIG( exit_status );
01367            child_died(exit_pid, exit_status);
01368 
01369            if (d.wrapper < 0 && !children) {
01370 #ifndef NDEBUG
01371                fprintf(stderr, "kdeinit4: Last child terminated, exiting (PID %ld).\n",
01372                                (long) getpid());
01373 #endif
01374                raise(SIGTERM);
01375            }
01376         }
01377       }
01378       while( exit_pid > 0);
01379 
01380       FD_ZERO(&rd_set);
01381       FD_ZERO(&wr_set);
01382       FD_ZERO(&e_set);
01383 
01384       if (d.launcher[0] >= 0)
01385          FD_SET(d.launcher[0], &rd_set);
01386       if (d.wrapper >= 0)
01387          FD_SET(d.wrapper, &rd_set);
01388       FD_SET(d.deadpipe[0], &rd_set);
01389 #ifdef Q_WS_X11
01390       if(X11fd >= 0) FD_SET(X11fd, &rd_set);
01391 #endif
01392 
01393       result = select(max_sock, &rd_set, &wr_set, &e_set, 0);
01394       if (result < 0) {
01395           if (errno == EINTR || errno == EAGAIN)
01396               continue;
01397           perror("kdeinit4: Aborting. select() failed");
01398           return;
01399       }
01400 
01401       /* Handle wrapper request */
01402       if (d.wrapper >= 0 && FD_ISSET(d.wrapper, &rd_set))
01403       {
01404          struct sockaddr_un client;
01405          kde_socklen_t sClient = sizeof(client);
01406          int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient);
01407          if (sock >= 0)
01408          {
01409             d.accepted_fd = sock;
01410             handle_launcher_request(sock, "wrapper");
01411             close(sock);
01412             d.accepted_fd = -1;
01413          }
01414       }
01415 
01416       /* Handle launcher request */
01417       if (d.launcher[0] >= 0 && FD_ISSET(d.launcher[0], &rd_set))
01418       {
01419          if (!handle_launcher_request(d.launcher[0], "launcher"))
01420              launcher_died();
01421          if (waitForPid == d.launcher_pid)
01422             return;
01423       }
01424 
01425 #ifdef Q_WS_X11
01426       /* Look for incoming X11 events */
01427       if(X11fd >= 0 && FD_ISSET(X11fd,&rd_set)) {
01428           if (X11display != 0) {
01429         XEvent event_return;
01430         while (XPending(X11display))
01431           XNextEvent(X11display, &event_return);
01432       }
01433       }
01434 #endif
01435    }
01436 }
01437 
01438 static void kdeinit_library_path()
01439 {
01440    const QStringList ltdl_library_path =
01441      QFile::decodeName(qgetenv("LTDL_LIBRARY_PATH")).split(QLatin1Char(':'),QString::SkipEmptyParts);
01442 #ifdef Q_OS_DARWIN
01443    const QByteArray ldlibpath = qgetenv("DYLD_LIBRARY_PATH");
01444 #else
01445    const QByteArray ldlibpath = qgetenv("LD_LIBRARY_PATH");
01446 #endif
01447    const QStringList ld_library_path =
01448      QFile::decodeName(ldlibpath).split(QLatin1Char(':'),QString::SkipEmptyParts);
01449 
01450    QByteArray extra_path;
01451    const QStringList candidates = s_instance->dirs()->resourceDirs("lib");
01452    for (QStringList::ConstIterator it = candidates.begin();
01453         it != candidates.end();
01454         ++it)
01455    {
01456       QString d = *it;
01457       if (ltdl_library_path.contains(d))
01458           continue;
01459       if (ld_library_path.contains(d))
01460           continue;
01461       if (d[d.length()-1] == QLatin1Char('/'))
01462       {
01463          d.truncate(d.length()-1);
01464          if (ltdl_library_path.contains(d))
01465             continue;
01466          if (ld_library_path.contains(d))
01467             continue;
01468       }
01469       if ((d == QLatin1String("/lib")) || (d == QLatin1String("/usr/lib")))
01470          continue;
01471 
01472       QByteArray dir = QFile::encodeName(d);
01473 
01474       if (access(dir, R_OK))
01475           continue;
01476 
01477       if ( !extra_path.isEmpty())
01478          extra_path += ':';
01479       extra_path += dir;
01480    }
01481 
01482 //   if (!extra_path.isEmpty())
01483 //      lt_dlsetsearchpath(extra_path.data());
01484 
01485    QByteArray display = qgetenv(DISPLAY);
01486    if (display.isEmpty())
01487    {
01488 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
01489      fprintf(stderr, "kdeinit4: Aborting. $"DISPLAY" is not set.\n");
01490      exit(255);
01491 #endif
01492    }
01493    int i;
01494    if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0)
01495      display.truncate(i);
01496 
01497    display.replace(':','_');
01498 #ifdef __APPLE__
01499    display.replace('/','_');
01500 #endif
01501    // WARNING, if you change the socket name, adjust kwrapper too
01502    const QString socketFileName = QString::fromLatin1("kdeinit4_%1").arg(QLatin1String(display));
01503    QByteArray socketName = QFile::encodeName(KStandardDirs::locateLocal("socket", socketFileName, *s_instance));
01504    if (socketName.length() >= MAX_SOCK_FILE)
01505    {
01506      fprintf(stderr, "kdeinit4: Aborting. Socket name will be too long:\n");
01507      fprintf(stderr, "         '%s'\n", socketName.data());
01508      exit(255);
01509    }
01510    strcpy(sock_file, socketName.data());
01511 }
01512 
01513 int kdeinit_xio_errhandler( Display *disp )
01514 {
01515     // disp is 0L when KDE shuts down. We don't want those warnings then.
01516 
01517     if ( disp )
01518     qWarning( "kdeinit4: Fatal IO error: client killed" );
01519 
01520     if (sock_file[0])
01521     {
01523       unlink(sock_file);
01524     }
01525 
01526     // Don't kill our children in suicide mode, they may still be in use
01527     if (d.suicide)
01528     {
01529        if (d.launcher_pid)
01530           kill(d.launcher_pid, SIGTERM);
01531        if (d.kded_pid)
01532           kill(d.kded_pid, SIGTERM);
01533        exit( 0 );
01534     }
01535 
01536     if ( disp )
01537     qWarning( "kdeinit4: sending SIGHUP to children." );
01538 
01539     /* this should remove all children we started */
01540     KDE_signal(SIGHUP, SIG_IGN);
01541     kill(0, SIGHUP);
01542 
01543     sleep(2);
01544 
01545     if ( disp )
01546     qWarning( "kdeinit4: sending SIGTERM to children." );
01547 
01548     /* and if they don't listen to us, this should work */
01549     KDE_signal(SIGTERM, SIG_IGN);
01550     kill(0, SIGTERM);
01551 
01552     if ( disp )
01553     qWarning( "kdeinit4: Exit." );
01554 
01555     exit( 0 );
01556     return 0;
01557 }
01558 
01559 #ifdef Q_WS_X11
01560 int kdeinit_x_errhandler( Display *dpy, XErrorEvent *err )
01561 {
01562 #ifndef NDEBUG
01563     char errstr[256];
01564     // kdeinit almost doesn't use X, and therefore there shouldn't be any X error
01565     XGetErrorText( dpy, err->error_code, errstr, 256 );
01566     fprintf(stderr, "kdeinit4(%d) : KDE detected X Error: %s %d\n"
01567                     "         Major opcode: %d\n"
01568                     "         Minor opcode: %d\n"
01569                     "         Resource id:  0x%lx\n",
01570             getpid(), errstr, err->error_code, err->request_code, err->minor_code, err->resourceid );
01571 
01572     //kDebug() << kBacktrace();
01573 
01574 #else
01575     Q_UNUSED(dpy);
01576     Q_UNUSED(err);
01577 #endif
01578     return 0;
01579 }
01580 #endif
01581 
01582 #ifdef Q_WS_X11
01583 // needs to be done sooner than initXconnection() because of also opening
01584 // another X connection for startup notification purposes
01585 static void setupX()
01586 {
01587     XSetIOErrorHandler(kdeinit_xio_errhandler);
01588     XSetErrorHandler(kdeinit_x_errhandler);
01589 /*
01590     Handle the tricky case of running via kdesu/su/sudo/etc. There the usual case
01591     is that kdesu (etc.) creates a file with xauth information, sets XAUTHORITY,
01592     runs the command and removes the xauth file after the command finishes. However,
01593     dbus and kdeinit daemon currently don't clean up properly and keeping running.
01594     Which means that running a KDE app via kdesu the second time talks to kdeinit
01595     with obsolete xauth information, which makes it unable to connect to X or launch
01596     any X11 applications.
01597     Even fixing the cleanup probably wouldn't be sufficient, since it'd be possible to
01598     launch one kdesu session, another one, exit the first one and the app from the second
01599     session would be using kdeinit from the first one.
01600     So the trick here is to duplicate the xauth file to another file in KDE's tmp
01601     location, make the file have a consistent name so that future sessions will use it
01602     as well, point XAUTHORITY there and never remove the file (except for possible
01603     tmp cleanup).
01604 */
01605     if( !qgetenv( "XAUTHORITY" ).isEmpty()) {
01606         QByteArray display = qgetenv( DISPLAY );
01607         int i;
01608         if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0)
01609             display.truncate(i);
01610         display.replace(':','_');
01611 #ifdef __APPLE__
01612         display.replace('/','_');
01613 #endif
01614         QString xauth = s_instance->dirs()->saveLocation( "tmp" ) + QLatin1String( "xauth-" )
01615             + QString::number( getuid()) + QLatin1String( "-" ) + QString::fromLocal8Bit( display );
01616         KSaveFile xauthfile( xauth );
01617         QFile xauthfrom( QFile::decodeName( qgetenv( "XAUTHORITY" )));
01618         if( !xauthfrom.open( QFile::ReadOnly ) || !xauthfile.open( QFile::WriteOnly )
01619             || xauthfile.write( xauthfrom.readAll()) != xauthfrom.size() || !xauthfile.finalize()) {
01620             xauthfile.abort();
01621         } else {
01622             setenv( "XAUTHORITY", QFile::encodeName( xauth ), true );
01623         }
01624     }
01625 }
01626 
01627 // Borrowed from kdebase/kaudio/kaudioserver.cpp
01628 static int initXconnection()
01629 {
01630   X11display = XOpenDisplay(NULL);
01631   if ( X11display != 0 ) {
01632     XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \
01633         0,
01634         BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)),
01635         BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) );
01636 #ifndef NDEBUG
01637     fprintf(stderr, "kdeinit4: opened connection to %s\n", DisplayString(X11display));
01638 #endif
01639     int fd = XConnectionNumber( X11display );
01640     int on = 1;
01641     (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on));
01642     return fd;
01643   } else
01644     fprintf(stderr, "kdeinit4: Can not connect to the X Server.\n" \
01645      "kdeinit4: Might not terminate at end of session.\n");
01646 
01647   return -1;
01648 }
01649 #endif
01650 
01651 extern "C" {
01652 
01653 static void secondary_child_handler(int)
01654 {
01655    waitpid(-1, 0, WNOHANG);
01656 }
01657 
01658 }
01659 
01660 int main(int argc, char **argv, char **envp)
01661 {
01662 #ifndef _WIN32_WCE
01663     setlocale (LC_ALL, "");
01664     setlocale (LC_NUMERIC, "C");
01665 #endif
01666 
01667    pid_t pid;
01668    bool do_fork = true;
01669    int launch_klauncher = 1;
01670    int launch_kded = 1;
01671    int keep_running = 1;
01672    d.suicide = false;
01673 
01675    char **safe_argv = (char **) malloc( sizeof(char *) * argc);
01676    for(int i = 0; i < argc; i++)
01677    {
01678       safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]);
01679       if (strcmp(safe_argv[i], "--no-klauncher") == 0)
01680          launch_klauncher = 0;
01681       if (strcmp(safe_argv[i], "--no-kded") == 0)
01682          launch_kded = 0;
01683 #ifdef Q_WS_MACX
01684       // make it nofork to match KUniqueApplication, technically command-line incompatible
01685       if (strcmp(safe_argv[i], "--nofork") == 0)
01686 #else
01687       if (strcmp(safe_argv[i], "--no-fork") == 0)
01688 #endif
01689          do_fork = false;
01690       if (strcmp(safe_argv[i], "--suicide") == 0)
01691          d.suicide = true;
01692       if (strcmp(safe_argv[i], "--exit") == 0)
01693          keep_running = 0;
01694       if (strcmp(safe_argv[i], "--version") == 0)
01695       {
01696      printf("Qt: %s\n", qVersion());
01697      printf("KDE: %s\n", KDE_VERSION_STRING);
01698      exit(0);
01699       }
01700 #ifdef KDEINIT_OOM_PROTECT
01701       if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i+1<argc)
01702          oom_pipe = atol(argv[i+1]);
01703 #endif
01704       if (strcmp(safe_argv[i], "--help") == 0)
01705       {
01706         printf("Usage: kdeinit4 [options]\n");
01707      // printf("    --no-dcop         Do not start dcopserver\n");
01708 #ifdef Q_WS_MACX
01709         printf("    --nofork          Do not fork\n");
01710 #else
01711         printf("    --no-fork         Do not fork\n");
01712 #endif
01713      // printf("    --no-klauncher    Do not start klauncher\n");
01714         printf("    --no-kded         Do not start kded\n");
01715         printf("    --suicide         Terminate when no KDE applications are left running\n");
01716     printf("    --version         Show version information\n");
01717      // printf("    --exit            Terminate when kded has run\n");
01718         exit(0);
01719       }
01720    }
01721 
01722    cleanup_fds();
01723 
01724    // Redirect stdout to stderr. We have no reason to use stdout anyway.
01725    // This minimizes our impact on commands used in pipes.
01726    (void)dup2(2, 1);
01727 
01728    if (do_fork) {
01729 #ifdef Q_WS_MACX
01730       mac_fork_and_reexec_self();
01731 #else
01732       if (pipe(d.initpipe) != 0) {
01733           perror("kdeinit4: pipe failed");
01734           return 1;
01735       }
01736 
01737       // Fork here and let parent process exit.
01738       // Parent process may only exit after all required services have been
01739       // launched. (dcopserver/klauncher and services which start with '+')
01740       KDE_signal( SIGCHLD, secondary_child_handler);
01741       if (fork() > 0) // Go into background
01742       {
01743          close(d.initpipe[1]);
01744          d.initpipe[1] = -1;
01745          // wait till init is complete
01746          char c;
01747          while( read(d.initpipe[0], &c, 1) < 0)
01748             ;
01749          // then exit;
01750          close(d.initpipe[0]);
01751          d.initpipe[0] = -1;
01752          return 0;
01753       }
01754       close(d.initpipe[0]);
01755       d.initpipe[0] = -1;
01756 #endif
01757    }
01758 
01760    if(keep_running)
01761       setsid();
01762 
01764    s_instance = new KComponentData("kdeinit4", QByteArray(), KComponentData::SkipMainComponentRegistration);
01765 
01767 #ifndef SKIP_PROCTITLE
01768    proctitle_init(argc, argv, envp);
01769 #endif
01770 
01771    kdeinit_library_path();
01772    // Don't make our instance the global instance
01773    // (do it only after kdeinit_library_path, that one indirectly uses KConfig,
01774    // which seems to be buggy and always use KGlobal instead of the matching KComponentData)
01775    Q_ASSERT(!KGlobal::hasMainComponent());
01776    // don't change envvars before proctitle_init()
01777    unsetenv("LD_BIND_NOW");
01778    unsetenv("DYLD_BIND_AT_LAUNCH");
01779    KApplication::loadedByKdeinit = true;
01780 
01781    d.maxname = strlen(argv[0]);
01782    d.launcher_pid = 0;
01783    d.kded_pid = 0;
01784    d.wrapper = -1;
01785    d.accepted_fd = -1;
01786    d.debug_wait = false;
01787    d.launcher_ok = false;
01788    children = NULL;
01789    init_signals();
01790 #ifdef Q_WS_X11
01791    setupX();
01792 #endif
01793 
01794    if (keep_running)
01795    {
01796       /*
01797        * Create ~/.kde/tmp-<hostname>/kdeinit4-<display> socket for incoming wrapper
01798        * requests.
01799        */
01800       init_kdeinit_socket();
01801    }
01802 #ifdef Q_WS_X11
01803     if (!d.suicide && qgetenv("KDE_IS_PRELINKED").isEmpty()) {
01804         const int extrasCount = sizeof(extra_libs)/sizeof(extra_libs[0]);
01805         for (int i=0; i<extrasCount; i++) {
01806             QString extra = KStandardDirs::locate("lib", QLatin1String(extra_libs[i]), *s_instance);
01807 
01808             // can't use KLibLoader here as it would unload the library
01809             // again
01810             if (!extra.isEmpty()) {
01811                 QLibrary l(extra);
01812                 l.setLoadHints(QLibrary::ExportExternalSymbolsHint);
01813                 l.load();
01814             }
01815 #ifndef NDEBUG
01816             else {
01817                 fprintf( stderr, "%s was not found.\n", extra_libs[i] );
01818             }
01819 #endif
01820 
01821         }
01822     }
01823 #endif
01824    if (launch_klauncher)
01825    {
01826       start_klauncher();
01827       handle_requests(d.launcher_pid); // Wait for klauncher to be ready
01828    }
01829 
01830 #ifdef Q_WS_X11
01831    X11fd = initXconnection();
01832 #endif
01833 
01834    {
01835       QFont::initialize();
01836 #ifdef Q_WS_X11
01837       if (XSupportsLocale ())
01838       {
01839          // Similar to QApplication::create_xim()
01840      // but we need to use our own display
01841      XOpenIM (X11display, 0, 0, 0);
01842       }
01843 #endif
01844    }
01845 
01846    if (launch_kded)
01847    {
01848       setenv("KDED_STARTED_BY_KDEINIT", "1", true);
01849       pid = launch( 1, KDED_EXENAME, 0 );
01850       unsetenv("KDED_STARTED_BY_KDEINIT");
01851 #ifndef NDEBUG
01852       fprintf(stderr, "kdeinit4: Launched KDED, pid = %ld result = %d\n", (long) pid, d.result);
01853 #endif
01854       d.kded_pid = pid;
01855       handle_requests(pid);
01856    }
01857 
01858    for(int i = 1; i < argc; i++)
01859    {
01860       if (safe_argv[i][0] == '+')
01861       {
01862          pid = launch( 1, safe_argv[i]+1, 0);
01863 #ifndef NDEBUG
01864       fprintf(stderr, "kdeinit4: Launched '%s', pid = %ld result = %d\n", safe_argv[i]+1, (long) pid, d.result);
01865 #endif
01866          handle_requests(pid);
01867       }
01868       else if (safe_argv[i][0] == '-'
01869 #ifdef KDEINIT_OOM_PROTECT
01870           || isdigit(safe_argv[i][0])
01871 #endif
01872           )
01873       {
01874          // Ignore
01875       }
01876       else
01877       {
01878          pid = launch( 1, safe_argv[i], 0 );
01879 #ifndef NDEBUG
01880       fprintf(stderr, "kdeinit4: Launched '%s', pid = %ld result = %d\n", safe_argv[i], (long) pid, d.result);
01881 #endif
01882       }
01883    }
01884 
01886    for(int i = 0; i < argc; i++)
01887    {
01888       free(safe_argv[i]);
01889    }
01890    free (safe_argv);
01891 
01892 #ifndef SKIP_PROCTITLE
01893    proctitle_set("kdeinit4 Running...");
01894 #endif
01895 
01896    if (!keep_running)
01897       return 0;
01898 
01899    if (d.initpipe[1] != -1)
01900    {
01901       char c = 0;
01902       write(d.initpipe[1], &c, 1); // Kdeinit is started.
01903       close(d.initpipe[1]);
01904       d.initpipe[1] = -1;
01905    }
01906 
01907    handle_requests(0);
01908 
01909    return 0;
01910 }
01911 
01912 

KInit

Skip menu "KInit"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal