summaryrefslogtreecommitdiffstats
path: root/kinit
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kinit
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kinit')
-rw-r--r--kinit/LICENSE.setproctitle89
-rw-r--r--kinit/Mainpage.dox29
-rw-r--r--kinit/Makefile.am92
-rw-r--r--kinit/README84
-rw-r--r--kinit/README.DCOP83
-rw-r--r--kinit/README.autostart62
-rw-r--r--kinit/README.wrapper38
-rw-r--r--kinit/autostart.cpp207
-rw-r--r--kinit/autostart.h48
-rw-r--r--kinit/configure.in.in59
-rw-r--r--kinit/kdostartupconfig.cpp143
-rw-r--r--kinit/kinit.cpp1912
-rw-r--r--kinit/kioslave.cpp93
-rw-r--r--kinit/klauncher.cpp1420
-rw-r--r--kinit/klauncher.h200
-rw-r--r--kinit/klauncher_cmds.h185
-rw-r--r--kinit/klauncher_main.cpp116
-rw-r--r--kinit/kstartupconfig.cpp176
-rw-r--r--kinit/kwrapper.c25
-rw-r--r--kinit/lnusertemp.c274
-rw-r--r--kinit/setproctitle.cpp302
-rw-r--r--kinit/setproctitle.h156
-rw-r--r--kinit/shell.c25
-rw-r--r--kinit/start_kdeinit.c172
-rw-r--r--kinit/start_kdeinit_wrapper.c92
-rw-r--r--kinit/tests/Makefile.am10
-rw-r--r--kinit/tests/klaunchertest.cpp54
-rw-r--r--kinit/wrapper.c547
28 files changed, 6693 insertions, 0 deletions
diff --git a/kinit/LICENSE.setproctitle b/kinit/LICENSE.setproctitle
new file mode 100644
index 000000000..d0ff63a9c
--- /dev/null
+++ b/kinit/LICENSE.setproctitle
@@ -0,0 +1,89 @@
+ SENDMAIL LICENSE
+
+The following license terms and conditions apply, unless a different
+license is obtained from Sendmail, Inc., 1401 Park Avenue, Emeryville, CA
+94608, or by electronic mail at license@sendmail.com.
+
+License Terms:
+
+Use, Modification and Redistribution (including distribution of any
+modified or derived work) in source and binary forms is permitted only if
+each of the following conditions is met:
+
+1. Redistributions qualify as "freeware" or "Open Source Software" under
+ one of the following terms:
+
+ (a) Redistributions are made at no charge beyond the reasonable cost of
+ materials and delivery.
+
+ (b) Redistributions are accompanied by a copy of the Source Code or by an
+ irrevocable offer to provide a copy of the Source Code for up to three
+ years at the cost of materials and delivery. Such redistributions
+ must allow further use, modification, and redistribution of the Source
+ Code under substantially the same terms as this license. For the
+ purposes of redistribution "Source Code" means the complete source
+ code of sendmail including all modifications.
+
+ Other forms of redistribution are allowed only under a separate royalty-
+ free agreement permitting such redistribution subject to standard
+ commercial terms and conditions. A copy of such agreement may be
+ obtained from Sendmail, Inc. at the above address.
+
+2. Redistributions of source code must retain the copyright notices as they
+ appear in each source code file, these license terms, and the
+ disclaimer/limitation of liability set forth as paragraph 6 below.
+
+3. Redistributions in binary form must reproduce the Copyright Notice,
+ these license terms, and the disclaimer/limitation of liability set
+ forth as paragraph 6 below, in the documentation and/or other materials
+ provided with the distribution. For the purposes of binary distribution
+ the "Copyright Notice" refers to the following language:
+ "Copyright (c) 1998 Sendmail, Inc. All rights reserved."
+
+4. Neither the name of Sendmail, Inc. nor the University of California nor
+ the names of their contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission. The name "sendmail" is a trademark of Sendmail, Inc.
+
+5. All redistributions must comply with the conditions imposed by the
+ University of California on certain embedded code, whose copyright
+ notice and conditions for redistribution are as follows:
+
+ (a) Copyright (c) 1988, 1993 The Regents of the University of
+ California. All rights reserved.
+
+ (b) Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ (i) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (ii) Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ (iii) All advertising materials mentioning features or use of this
+ software must display the following acknowledgement: "This
+ product includes software developed by the University of
+ California, Berkeley and its contributors."
+
+ (iv) Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
+ SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF
+ CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+(Version 8.6, last updated 6/24/1998)
diff --git a/kinit/Mainpage.dox b/kinit/Mainpage.dox
new file mode 100644
index 000000000..e5c258575
--- /dev/null
+++ b/kinit/Mainpage.dox
@@ -0,0 +1,29 @@
+/** @mainpage KDE Initialization Routines
+
+kdeinit is a process launcher somewhat similar to the
+famous init used for booting UNIX.
+<p>
+It launches processes by forking and then loading a
+dynamic library which should contain a 'main(...)'
+function. This both increases the startup speed and
+reduces the memory consumption of KDE applications.
+
+kdeinit_wrapper, kshell and kwrapper provide methods
+of starting programs via kdeinit.
+
+@authors
+Waldo Bastian \<bastian@kde.org\><br>
+Process title changing code taken from the ProFTPD Project, adapted by Alex Merry
+
+@maintainers
+[Unknown/None]
+
+@licenses
+@gpl<br>
+Most code is @lgpl
+
+*/
+
+// DOXYGEN_REFERENCES = kdecore
+// DOXYGEN_SET_PROJECT_NAME = KInit
+// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/kinit/Makefile.am b/kinit/Makefile.am
new file mode 100644
index 000000000..1bbb44991
--- /dev/null
+++ b/kinit/Makefile.am
@@ -0,0 +1,92 @@
+# This file is part of the KDE libraries
+# Copyright (C) 1999 Waldo Bastian (bastian@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.
+
+INCLUDES = -I$(srcdir)/../libltdl/ $(all_includes) $(KDEINIT_XFT_INCLUDES)
+
+SUBDIRS = . tests
+
+bin_PROGRAMS = kdeinit kdeinit_wrapper kshell kdeinit_shutdown lnusertemp kwrapper kioslave \
+ kstartupconfig kdostartupconfig start_kdeinit start_kdeinit_wrapper
+
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = klauncher.la
+kdeinit_SOURCES = kinit.cpp setproctitle.cpp
+
+# NOTE: We link against all common libraries even if we don't need them ourselves.
+# For the future: examine if condensing the tons of *_LDFLAGS variables
+# into $(all_libraries) isn't better
+AM_LDFLAGS = $(LDFLAGS_AS_NEEDED) $(LDFLAGS_NEW_DTAGS)
+
+kdeinit_LDFLAGS = $(KDE_MT_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) $(USER_LDFLAGS) \
+ $(KDE_RPATH)
+kdeinit_LDADD = $(LIB_KPARTS)
+
+kioslave_SOURCES = kioslave.cpp
+kioslave_LDFLAGS = $(KDE_MT_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) $(USER_LDFLAGS) \
+ $(KDE_RPATH)
+kioslave_LDADD = $(LIB_KIO)
+
+kdeinit_wrapper_SOURCES = wrapper.c
+kdeinit_wrapper_LDADD = $(LIBSOCKET)
+
+kshell_SOURCES = shell.c
+kshell_LDADD = $(LIBSOCKET)
+
+kdeinit_shutdown_SOURCES = wrapper.c
+kdeinit_shutdown_LDADD = $(LIBSOCKET)
+
+lnusertemp_SOURCES = lnusertemp.c
+lnusertemp_LDFLAGS = $(KDE_RPATH)
+
+kwrapper_SOURCES = kwrapper.c
+kwrapper_LDADD = $(LIBSOCKET)
+
+klauncher_la_LDFLAGS = $(all_libraries) -module -avoid-version
+klauncher_la_LIBADD = $(LIB_KIO)
+klauncher_la_SOURCES = klauncher.cpp klauncher_main.cpp autostart.cpp
+
+kstartupconfig_SOURCES = kstartupconfig.cpp
+kstartupconfig_LDADD = $(top_builddir)/kdecore/libkdefakes.la
+kdostartupconfig_SOURCES = kdostartupconfig.cpp
+kdostartupconfig_LDFLAGS = $(KDE_MT_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) $(USER_LDFLAGS) \
+ $(KDE_RPATH)
+kdostartupconfig_LDADD = $(LIB_KDECORE)
+
+start_kdeinit_SOURCES = start_kdeinit.c
+kdeinitpath = $(bindir)/kdeinit
+start_kdeinit_CFLAGS = $(KDE_USE_FPIE) -DEXECUTE=\"$(kdeinitpath)\"
+start_kdeinit_LDFLAGS = $(KDE_USE_PIE)
+
+start_kdeinit_wrapper_SOURCES = start_kdeinit_wrapper.c
+
+METASOURCES = AUTO
+
+noinst_HEADERS = klauncher.h autostart.h klauncher_cmds.h setproctitle.h
+
+kwrapper.o: wrapper.c
+
+shell.o: wrapper.c
+
+dummy.cpp:
+ echo > dummy.cpp
+
+install-exec-hook:
+ @if test \${KDEINIT_SETUID} != 0; then \
+ (chown 0 $(DESTDIR)$(bindir)/start_kdeinit && chmod 4755 $(DESTDIR)$(bindir)/start_kdeinit) || echo "Please make start_kdeinit setuid root" ; \
+ fi
+
+include $(top_srcdir)/admin/Doxyfile.am
diff --git a/kinit/README b/kinit/README
new file mode 100644
index 000000000..ea52b0235
--- /dev/null
+++ b/kinit/README
@@ -0,0 +1,84 @@
+README
+
+kdeinit is a process launcher somewhat similar to the
+famous init used for booting UNIX.
+
+It launches processes by forking and then loading a
+dynamic library which should contain a 'main(...)'
+function.
+
+Executive summary
+=================
+
+Using kdeinit to launch KDE applications makes starting
+a typical KDE applications 2.5 times faster (100ms
+instead of 250ms on a P-III 500) It reduces memory
+consumption by approx. 350Kb per application.
+
+
+How it works
+============
+
+kdeinit is linked against all libraries a standard KDE
+application needs. With this technique starting an
+application becomes much faster because now only
+the application itself needs to be linked whereas
+otherwise both the application as well as all the libaries
+it uses need to be linked.
+
+Startup Speed
+=============
+
+Starting an application linked against libqt, libkdecore and libkdeui
+in the conventional way takes approx. 150ms on a Pentium III - 500Mhz.
+Starting the same application via kdeinit takes less than 10ms.
+
+(application without KApplication constructor, the KApplication
+constructor requires an extra 100ms in both cases)
+
+Memory Usage
+============
+
+An application linked against libqt, libkdecore and libkdeui started
+in the conventional way requires about 498Kb memory.
+(average of 10 instances) If the same application is started via
+kdeinit it requires about 142Kb. A difference of 356Kb (application
+without KApplication constructor)
+
+If we take the KApplication constructor into account, an application
+started in the conventional way takes about 679Kb memory while the same
+application started via kdeinit requires about 380Kb. Here the difference
+is somewhat less, 299Kb. This seems to be caused by the fact that the
+dynamic linker does "lazy linking". We can force the linker to link
+everything at startup by specifying "LD_BIND_NOW=true". When kdeinit is
+started with this option on, kdeinit is back to its full efficiency, an
+application with a KApplication constructor now uses 338Kb of memory.
+A difference of 341Kb with the normal case.
+
+Adapting programs to use kdeinit.
+===============================
+
+The sourcecode of a program does not require any change to take advantage
+of kdeinit. Only the makefile requires an adaption, if the Makefile.am of
+a normal program looks like this:
+
+bin_PROGRAMS = kicker
+kicker_LDADD = $(top_builddir)/libkonq/libkonq.la
+kicker_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+The following lines need to be added to make a library version useable
+by kdeinit:
+
+lib_LTLIBRARIES = kicker.la
+libkicker_la_LIBADD = $(top_builddir)/libkonq/libkonq.la
+libkicker_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -module
+
+Disadvantages
+=============
+
+The process name of applications started via kdeinit is "kdeinit". This problem
+can be corrected to a degree by changing the application name as shown
+by 'ps'. However, applications like "killall" will only see "kdeinit" as
+process name. To workaround this, use "kdekillall", from kdesdk/scripts,
+for applications started via kdeinit.
+
diff --git a/kinit/README.DCOP b/kinit/README.DCOP
new file mode 100644
index 000000000..43f951226
--- /dev/null
+++ b/kinit/README.DCOP
@@ -0,0 +1,83 @@
+"KLauncher" supports the following DCOP functions:
+
+/**
+ * Starts a program.
+ * 'envs' are environment variables that will be added
+ * to this program's environment before starting it
+ * 'startup_id' is for application startup notification,
+ * "" is the default, "0" for none
+ */
+void exec_blind(QCString name, QValueList<QCString> argList,
+ QValueList<QCString> envs, QCString startup_id );
+void exec_blind(QCString name, QValueList<QCString> argList);
+
+/**
+ * Start a service by name.
+ *
+ * 'serviceName' refers to the service name as given by
+ * the Name field in the desktop file describing the service.
+ *
+ * 'url', if not empty, will be passed to the service as
+ * argument.
+ *
+ * 'envs' are environment variables that will be added
+ * to this program's environment before starting it
+ *
+ * 'startup_id' is for application startup notification,
+ * "" is the default, "0" for none
+ */
+serviceResult start_service_by_name(QString serviceName, QStringList url,
+ QValueList<QCString> envs, QCString startup_id );
+serviceResult start_service_by_name(QString serviceName, QStringList url)
+
+/**
+ * Start a service by desktop path.
+ *
+ * 'serviceName' refers to a desktop file describing the service.
+ * This may be an absolute path or a path relative to $KDEDIRS/applnk
+ * and/or $KDEDIRS/services
+ * E.g. it should have the form "Applications/korganizer.desktop" or
+ * "/opt/kde/share/applnk/Applications/korganizer.desktop".
+ *
+ * 'url', if not empty, will be passed to the service as
+ * argument.
+ *
+ * 'envs' are environment variables that will be added
+ * to this program's environment before starting it
+ *
+ * 'startup_id' is for application startup notification,
+ * "" is the default, "0" for none
+ */
+serviceResult start_service_by_desktop_path(QString serviceName, QStringList url,
+ QValueList<QCString> envs, QCString startup_id );
+serviceResult start_service_by_desktop_path(QString serviceName, QStringList url)
+
+
+/**
+ * Start a service by desktop name.
+ *
+ * 'serviceName' refers to a desktop file describing the service.
+ * The service is looked up anywhere in $KDEDIR/applnk and/or
+ * $KDEDIR/services.
+ * E.g. it should have the form "korganizer".
+ *
+ * 'url', if not empty, will be passed to the service as
+ * argument.
+ *
+ * 'envs' are environment variables that will be added
+ * to this program's environment before starting it
+ *
+ * 'startup_id' is for application startup notification,
+ * "" is the default, "0" for none
+ */
+serviceResult start_service_by_desktop_name(QString serviceName, QStringList url,
+ QValueList<QCString> envs, QCString startup_id );
+serviceResult start_service_by_desktop_name(QString serviceName, QStringList url)
+
+struct serviceResult
+{
+ int result; // 0 means success. > 0 means error
+ QCString dcopName; // Contains DCOP name on success
+ QString error; // Contains error description on failure.
+}
+
diff --git a/kinit/README.autostart b/kinit/README.autostart
new file mode 100644
index 000000000..bbfd036ec
--- /dev/null
+++ b/kinit/README.autostart
@@ -0,0 +1,62 @@
+KDE SESSION AUTOSTART
+=====================
+
+KDE session startup occurs in the following sequence:
+
+ Window manager startup
+ Autostart phase 1
+ Session restoration
+ Autostart phase 2
+
+Applications may be scheduled to be automatically run at KDE session startup
+in either autostart phase 1 or phase 2. Autostart phase 1 is the original
+autostart phase; phase 2 was introduced in KDE 3. To run in either phase, an
+application's .desktop file must be located in a KDE autostart directory such
+as $KDEDIR/share/autostart or $KDEHOME/share/autostart. The .desktop file can
+contain the following optional entries to control its autostart:
+
+ X-KDE-autostart-condition = rcfile:group:entry:default
+
+ rcfile = name of a config file (including path if necessary)
+ group = name of a group within the config file
+ entry = name of a boolean entry within the group
+ default = true or false
+
+ Starts the application only if the specified boolean entry in the
+ specified config file has the value 'true'. If the specified entry is
+ missing from the config file, the application will only be started if
+ 'default' is 'true'.
+ If the entry is not within a group, the group entry can be left empty.
+
+ X-KDE-autostart-after = desktop_name
+
+ desktop_name = the name of another .desktop file excluding path and
+ the .desktop suffix. E.g. panel.desktop would appear
+ as 'X-KDE-autostart-after=panel'
+
+ Waits until the .desktop file specified by 'desktop_name' has been
+ autostarted. The entry is ignored if the specified application is not
+ scheduled in the same autostart phase as this .desktop file.
+
+ X-KDE-autostart-phase = phase
+
+ phase = 1 or 2
+
+ Starts the application in the autostart phase specified by 'phase'.
+ If this entry is missing or 'phase' < 1, 'phase' defaults to 1. If
+ 'phase' > 2 the application will not be autostarted since the specified
+ autostart phase will never be reached.
+
+
+ Hidden = true
+
+ Disables autostarting the application.
+
+
+KUniqueApplication and session restoration
+------------------------------------------
+
+If KUniqueApplication applications are autostarted before they are restored
+from the previous session, they will never see the session restoration command.
+So if you need to autostart a KUniqueApplication which may also be restored
+in session restoration, you should schedule it for autostart in phase 2.
diff --git a/kinit/README.wrapper b/kinit/README.wrapper
new file mode 100644
index 000000000..48bfdc7e8
--- /dev/null
+++ b/kinit/README.wrapper
@@ -0,0 +1,38 @@
+README
+
+kdeinit_wrapper, kshell and kwrapper are a programs that
+start programs via kdeinit.
+
+E.g. You can make a symbolic link from $KDEDIR/bin/konsole to
+$KDEDIR/bin/kdeinit_wrapper. Typing 'konsole' on the command line
+will then start 'konsole.la' through kdeinit instead.
+
+kdeinit_wrapper is the simplest for, it only passes the program
+and arguments to kdeinit, nothing else
+
+kshell is usually the best choice, it passes the program,
+arguments, complete environment ( $PATH, etc. ) and current
+working directory to kdeinit
+
+kwrapper tries to make the program look like it was actually
+really started directly and not via kdeinit. In addition to
+what kshell does, it also tries to redirect the program
+output to the console from which kwrapper was started, it waits
+for the program started via kdeinit to finish and only after then
+it exits ( it doesn't return its return value though ), and
+it also passes most signals it gets to the process of the started
+program ( thus allowing you to break it using Ctrl+C or stopping
+it using Ctrl+Z ). The drawbacks of this are that you'll have one
+more process running, and also the signal passing and output
+redirection may not work 100% reliably
+
+
+TODO
+====
+
+* There is no portable way to read out the complete environment and
+ pass it to kdeinit. - kdeinit should probably unset every
+ variable that's not set in the environment it gets from kshell or
+ kwrapper
+* stdout/stderr of the started application goes to the console where
+ kdeinit was started. - done, I hope it's ok
diff --git a/kinit/autostart.cpp b/kinit/autostart.cpp
new file mode 100644
index 000000000..0b39a3275
--- /dev/null
+++ b/kinit/autostart.cpp
@@ -0,0 +1,207 @@
+/*
+ *
+ * This file is part of the KDE libraries
+ * Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "autostart.h"
+
+#include <kconfig.h>
+#include <kdesktopfile.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+class AutoStartItem
+{
+public:
+ QString name;
+ QString service;
+ QString startAfter;
+ int phase;
+};
+
+class AutoStartList: public QPtrList<AutoStartItem>
+{
+public:
+ AutoStartList() { }
+};
+
+AutoStart::AutoStart( bool new_startup )
+ : m_newStartup( new_startup ), m_phase( new_startup ? -1 : 0), m_phasedone(false)
+{
+ m_startList = new AutoStartList;
+ m_startList->setAutoDelete(true);
+ KGlobal::dirs()->addResourceType("autostart", "share/autostart");
+}
+
+AutoStart::~AutoStart()
+{
+ delete m_startList;
+}
+
+void
+AutoStart::setPhase(int phase)
+{
+ if (phase > m_phase)
+ {
+ m_phase = phase;
+ m_phasedone = false;
+ }
+}
+
+void AutoStart::setPhaseDone()
+{
+ m_phasedone = true;
+}
+
+static QString extractName(QString path)
+{
+ int i = path.findRev('/');
+ if (i >= 0)
+ path = path.mid(i+1);
+ i = path.findRev('.');
+ if (i >= 0)
+ path = path.left(i);
+ return path;
+}
+
+static bool startCondition(const QString &condition)
+{
+ if (condition.isEmpty())
+ return true;
+
+ QStringList list = QStringList::split(':', condition, true);
+ if (list.count() < 4)
+ return true;
+ if (list[0].isEmpty() || list[2].isEmpty())
+ return true;
+
+ KConfig config(list[0], true, false);
+ if (!list[1].isEmpty())
+ config.setGroup(list[1]);
+
+ bool defaultValue = (list[3].lower() == "true");
+
+ return config.readBoolEntry(list[2], defaultValue);
+}
+
+void
+AutoStart::loadAutoStartList()
+{
+ QStringList files = KGlobal::dirs()->findAllResources("autostart", "*.desktop", false, true);
+
+ for(QStringList::ConstIterator it = files.begin();
+ it != files.end();
+ ++it)
+ {
+ KDesktopFile config(*it, true);
+ if (!startCondition(config.readEntry("X-KDE-autostart-condition")))
+ continue;
+ if (!config.tryExec())
+ continue;
+ if (config.readBoolEntry("Hidden", false))
+ continue;
+
+ if (config.hasKey("OnlyShowIn"))
+ {
+ if (!config.readListEntry("OnlyShowIn", ';').contains("KDE"))
+ continue;
+ }
+ if (config.hasKey("NotShowIn"))
+ {
+ if (config.readListEntry("NotShowIn", ';').contains("KDE"))
+ continue;
+ }
+
+ AutoStartItem *item = new AutoStartItem;
+ item->name = extractName(*it);
+ item->service = *it;
+ item->startAfter = config.readEntry("X-KDE-autostart-after");
+ if( m_newStartup )
+ {
+ item->phase = config.readNumEntry("X-KDE-autostart-phase", 2);
+ if (item->phase < 0)
+ item->phase = 0;
+ }
+ else
+ {
+ item->phase = config.readNumEntry("X-KDE-autostart-phase", 1);
+ if (item->phase < 1)
+ item->phase = 1;
+ }
+ m_startList->append(item);
+ }
+}
+
+QString
+AutoStart::startService()
+{
+ if (m_startList->isEmpty())
+ return 0;
+
+ while(!m_started.isEmpty())
+ {
+
+ // Check for items that depend on previously started items
+ QString lastItem = m_started[0];
+ for(AutoStartItem *item = m_startList->first();
+ item; item = m_startList->next())
+ {
+ if (item->phase == m_phase
+ && item->startAfter == lastItem)
+ {
+ m_started.prepend(item->name);
+ QString service = item->service;
+ m_startList->remove();
+ return service;
+ }
+ }
+ m_started.remove(m_started.begin());
+ }
+
+ // Check for items that don't depend on anything
+ AutoStartItem *item;
+ for(item = m_startList->first();
+ item; item = m_startList->next())
+ {
+ if (item->phase == m_phase
+ && item->startAfter.isEmpty())
+ {
+ m_started.prepend(item->name);
+ QString service = item->service;
+ m_startList->remove();
+ return service;
+ }
+ }
+
+ // Just start something in this phase
+ for(item = m_startList->first();
+ item; item = m_startList->next())
+ {
+ if (item->phase == m_phase)
+ {
+ m_started.prepend(item->name);
+ QString service = item->service;
+ m_startList->remove();
+ return service;
+ }
+ }
+
+ return 0;
+}
diff --git a/kinit/autostart.h b/kinit/autostart.h
new file mode 100644
index 000000000..a10bfa0dd
--- /dev/null
+++ b/kinit/autostart.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2001 Waldo Bastian <bastian@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.
+*/
+
+#ifndef _AUTOSTART_H_
+#define _AUTOSTART_H_
+
+#include <qstringlist.h>
+
+class AutoStartList;
+
+class AutoStart
+{
+public:
+ AutoStart( bool new_startup );
+ ~AutoStart();
+
+ void loadAutoStartList();
+ QString startService();
+ void setPhase(int phase);
+ void setPhaseDone();
+ int phase() const { return m_phase; }
+ bool phaseDone() const { return m_phasedone; }
+
+private:
+ bool m_newStartup;
+ AutoStartList *m_startList;
+ QStringList m_started;
+ int m_phase;
+ bool m_phasedone;
+};
+
+#endif
diff --git a/kinit/configure.in.in b/kinit/configure.in.in
new file mode 100644
index 000000000..2ba692872
--- /dev/null
+++ b/kinit/configure.in.in
@@ -0,0 +1,59 @@
+KDEINIT_USE_XFT=1
+dnl check if Qt is linked against Xft
+KDE_CHECK_LIB(qt-mt,XftInit,[],[KDEINIT_USE_XFT=])
+
+dnl Xft requires freetype to compile
+KDE_FIND_PATH(fontconfig-config, FONTCONFIG_CONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/bin /usr/local/bin /opt/local/bin], [ KDE_FIND_PATH(pkg-config, PKGCONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/bin /usr/local/bin /opt/local/bin], [AC_MSG_WARN([Could not find neither pkg-config nor fontconfig-config, check http://www.fontconfig.org/ ])
+])
+])
+
+if test -n "$PKGCONFIG"; then
+ vers=`$PKGCONFIG fontconfig --modversion 2>/dev/null | sed -e 's/libfontconfig //' | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'`
+ if test -n "$vers" && test "$vers" -ge 1000000
+ then
+ FONTCONFIG_CFLAGS="`$PKGCONFIG fontconfig --cflags`"
+ KDEINIT_FONTCONFIG=1
+ fi
+fi
+
+if test -n "$FONTCONFIG_CONFIG"; then
+ vers=`$FONTCONFIG_CONFIG --version 2>/dev/null | sed -e 's/libfontconfig //' | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'`
+ if test -n "$vers" && test "$vers" -ge 1000000
+ then
+ FONTCONFIG_CFLAGS="`$FONTCONFIG_CONFIG --cflags`"
+ KDEINIT_FONTCONFIG=1
+ fi
+fi
+
+KDE_FIND_PATH(freetype-config, KDEINIT_FREETYPE_CONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/bin /usr/local/bin /opt/local/bin])
+if test -n "$KDEINIT_FREETYPE_CONFIG"; then
+ KDEINIT_XFT_INCLUDES="`$KDEINIT_FREETYPE_CONFIG --cflags` $FONTCONFIG_CFLAGS"
+ kdeinit_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $KDEINIT_XFT_INCLUDES $FONTCONFIG_CFLAGS"
+ KDE_CHECK_HEADER(X11/Xft/Xft.h,[],[KDEINIT_USE_XFT=])
+ CPPFLAGS=$kdeinit_cppflags
+else
+ KDEINIT_USE_XFT=
+fi
+
+if test -n "$KDEINIT_USE_XFT"; then
+ AC_DEFINE(KDEINIT_USE_XFT,1,[Use Xft preinitialization in kdeinit])
+fi
+if test -n "$KDEINIT_FONTCONFIG"; then
+ AC_DEFINE(KDEINIT_USE_FONTCONFIG,1,[Use FontConfig in kdeinit])
+fi
+AC_SUBST(KDEINIT_XFT_INCLUDES)
+
+AC_MSG_CHECKING(whether to make kdeinit setuid root in order to protect it from bad Linux OOM-killer)
+kdeinit_setuid=
+case $target_os in
+ linux*)
+ AC_MSG_RESULT(yes)
+ KDEINIT_SETUID=1
+ AC_DEFINE(KDEINIT_OOM_PROTECT,1,[Enable prevention against poor Linux OOM-killer])
+ ;;
+ *) AC_MSG_RESULT(no)
+ KDEINIT_SETUID=0
+ ;;
+esac
+AC_SUBST(KDEINIT_SETUID)
diff --git a/kinit/kdostartupconfig.cpp b/kinit/kdostartupconfig.cpp
new file mode 100644
index 000000000..a0e9ccf43
--- /dev/null
+++ b/kinit/kdostartupconfig.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+
+ Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+****************************************************************************/
+
+#undef QT_NO_CAST_ASCII
+
+// See description in kstartupconfig.cpp .
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+QString get_entry( QString* ll )
+ {
+ QString& l = *ll;
+ l = l.stripWhiteSpace();
+ if( l.isEmpty())
+ return QString::null;
+ QString ret;
+ if( l[ 0 ] == '\'' )
+ {
+ unsigned int pos = 1;
+ while( pos < l.length() && l[ pos ] != '\'' )
+ ret += l[ pos++ ];
+ if( pos >= l.length())
+ {
+ *ll = QString::null;
+ return QString::null;
+ }
+ *ll = l.mid( pos + 1 );
+ return ret;
+ }
+ unsigned int pos = 0;
+ while( pos < l.length() && l[ pos ] != ' ' )
+ ret += l[ pos++ ];
+ *ll = l.mid( pos );
+ return ret;
+ }
+
+int main()
+ {
+ KInstance inst( "kdostartupconfig" );
+ kdDebug() << "Running kdostartupconfig." << endl;
+ QString keysname = locateLocal( "config", "startupconfigkeys" );
+ QFile keys( keysname );
+ if( !keys.open( IO_ReadOnly ))
+ return 3;
+ QFile f1( locateLocal( "config", "startupconfig" ));
+ if( !f1.open( IO_WriteOnly ))
+ return 4;
+ QFile f2( locateLocal( "config", "startupconfigfiles" ));
+ if( !f2.open( IO_WriteOnly ))
+ return 5;
+ QTextStream startupconfig( &f1 );
+ QTextStream startupconfigfiles( &f2 );
+ startupconfig << "#! /bin/sh\n";
+ for(;;)
+ {
+ QString line;
+ if( keys.readLine( line, 1024 ) < 0 )
+ break;
+ line = line.stripWhiteSpace();
+ if( line.isEmpty())
+ break;
+ QString tmp = line;
+ QString file, group, key, def;
+ file = get_entry( &tmp );
+ group = get_entry( &tmp );
+ key = get_entry( &tmp );
+ def = get_entry( &tmp );
+ if( file.isEmpty() || group.isEmpty())
+ return 6;
+ if( group.left( 1 ) == "[" && group.right( 1 ) == "]" )
+ { // whole config group
+ KConfig cfg( file );
+ group = group.mid( 1, group.length() - 2 );
+ QMap< QString, QString > entries = cfg.entryMap( group );
+ startupconfig << "# " << line << "\n";
+ for( QMap< QString, QString >::ConstIterator it = entries.begin();
+ it != entries.end();
+ ++it )
+ {
+ QString key = it.key();
+ QString value = *it;
+ startupconfig << file.replace( ' ', '_' ).lower()
+ << "_" << group.replace( ' ', '_' ).lower()
+ << "_" << key.replace( ' ', '_' ).lower()
+ << "=\"" << value.replace( "\"", "\\\"" ) << "\"\n";
+ }
+ }
+ else
+ { // a single key
+ if( key.isEmpty())
+ return 7;
+ KConfig cfg( file );
+ cfg.setGroup( group );
+ QString value = cfg.readEntry( key, def );
+ startupconfig << "# " << line << "\n";
+ startupconfig << file.replace( ' ', '_' ).lower()
+ << "_" << group.replace( ' ', '_' ).lower()
+ << "_" << key.replace( ' ', '_' ).lower()
+ << "=\"" << value.replace( "\"", "\\\"" ) << "\"\n";
+ }
+ startupconfigfiles << line << endl;
+ // use even currently non-existing paths in $KDEDIRS
+ QStringList dirs = QStringList::split( KPATH_SEPARATOR, KGlobal::dirs()->kfsstnd_prefixes());
+ for( QStringList::ConstIterator it = dirs.begin();
+ it != dirs.end();
+ ++it )
+ {
+ QString cfg = *it + "share/config/" + file;
+ if( KStandardDirs::exists( cfg ))
+ startupconfigfiles << cfg << "\n";
+ else
+ startupconfigfiles << "!" << cfg << "\n";
+ }
+ startupconfigfiles << "*\n";
+ }
+ return 0;
+ }
diff --git a/kinit/kinit.cpp b/kinit/kinit.cpp
new file mode 100644
index 000000000..8b654b038
--- /dev/null
+++ b/kinit/kinit.cpp
@@ -0,0 +1,1912 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org>
+ * (c) 1999 Mario Weilguni <mweilguni@sime.com>
+ * (c) 2001 Lubos Lunak <l.lunak@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <setproctitle.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <locale.h>
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+#include <qfont.h>
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <klibloader.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+#ifdef Q_OS_LINUX
+#include <sys/prctl.h>
+#ifndef PR_SET_NAME
+#define PR_SET_NAME 15
+#endif
+#endif
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#include <kstartupinfo.h> // schroder
+#endif
+
+#include <kdeversion.h>
+
+#include "ltdl.h"
+#include "klauncher_cmds.h"
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+//#undef K_WS_QTONLY
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#endif
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LTDL_GLOBAL DL_GLOBAL
+# else
+# define LTDL_GLOBAL 0
+# endif
+#endif
+
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+#include <X11/Xft/Xft.h>
+extern "C" FcBool XftInitFtLibrary (void);
+#include <fontconfig/fontconfig.h>
+#endif
+
+extern char **environ;
+
+extern int lt_dlopen_flag;
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+static int X11fd = -1;
+static Display *X11display = 0;
+static int X11_startup_notify_fd = -1;
+static Display *X11_startup_notify_display = 0;
+#endif
+static const KInstance *s_instance = 0;
+#define MAX_SOCK_FILE 255
+static char sock_file[MAX_SOCK_FILE];
+static char sock_file_old[MAX_SOCK_FILE];
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+#define DISPLAY "DISPLAY"
+#elif defined(Q_WS_QWS)
+#define DISPLAY "QWS_DISPLAY"
+#elif defined(Q_WS_MACX)
+#define DISPLAY "MAC_DISPLAY"
+#elif defined(K_WS_QTONLY)
+#define DISPLAY "QT_DISPLAY"
+#else
+#error Use QT/X11 or QT/Embedded
+#endif
+
+/* Group data */
+static struct {
+ int maxname;
+ int fd[2];
+ int launcher[2]; /* socket pair for launcher communication */
+ int deadpipe[2]; /* pipe used to detect dead children */
+ int initpipe[2];
+ int wrapper; /* socket for wrapper communication */
+ int wrapper_old; /* old socket for wrapper communication */
+ char result;
+ int exit_status;
+ pid_t fork;
+ pid_t launcher_pid;
+ pid_t my_pid;
+ int n;
+ lt_dlhandle handle;
+ lt_ptr sym;
+ char **argv;
+ int (*func)(int, char *[]);
+ int (*launcher_func)(int);
+ bool debug_wait;
+ int lt_dlopen_flag;
+ QCString errorMsg;
+ bool launcher_ok;
+ bool suicide;
+} d;
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+extern "C" {
+int kdeinit_xio_errhandler( Display * );
+int kdeinit_x_errhandler( Display *, XErrorEvent *err );
+}
+#endif
+
+/* These are to link libkparts even if 'smart' linker is used */
+#include <kparts/plugin.h>
+extern "C" KParts::Plugin* _kinit_init_kparts() { return new KParts::Plugin(); }
+/* These are to link libkio even if 'smart' linker is used */
+#include <kio/authinfo.h>
+extern "C" KIO::AuthInfo* _kioslave_init_kio() { return new KIO::AuthInfo(); }
+
+/*
+ * Close fd's which are only useful for the parent process.
+ * Restore default signal handlers.
+ */
+static void close_fds()
+{
+ if (d.deadpipe[0] != -1)
+ {
+ close(d.deadpipe[0]);
+ d.deadpipe[0] = -1;
+ }
+
+ if (d.deadpipe[1] != -1)
+ {
+ close(d.deadpipe[1]);
+ d.deadpipe[1] = -1;
+ }
+
+ if (d.initpipe[0] != -1)
+ {
+ close(d.initpipe[0]);
+ d.initpipe[0] = -1;
+ }
+
+ if (d.initpipe[1] != -1)
+ {
+ close(d.initpipe[1]);
+ d.initpipe[1] = -1;
+ }
+
+ if (d.launcher_pid)
+ {
+ close(d.launcher[0]);
+ d.launcher_pid = 0;
+ }
+ if (d.wrapper)
+ {
+ close(d.wrapper);
+ d.wrapper = 0;
+ }
+ if (d.wrapper_old)
+ {
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ }
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if (X11fd >= 0)
+ {
+ close(X11fd);
+ X11fd = -1;
+ }
+ if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd )
+ {
+ close(X11_startup_notify_fd);
+ X11_startup_notify_fd = -1;
+ }
+#endif
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+}
+
+static void exitWithErrorMsg(const QString &errorMsg)
+{
+ fprintf( stderr, "%s\n", errorMsg.local8Bit().data() );
+ QCString utf8ErrorMsg = errorMsg.utf8();
+ d.result = 3; // Error with msg
+ write(d.fd[1], &d.result, 1);
+ int l = utf8ErrorMsg.length();
+ write(d.fd[1], &l, sizeof(int));
+ write(d.fd[1], utf8ErrorMsg.data(), l);
+ close(d.fd[1]);
+ exit(255);
+}
+
+static void setup_tty( const char* tty )
+{
+ if( tty == NULL || *tty == '\0' )
+ return;
+ int fd = open( tty, O_WRONLY );
+ if( fd < 0 )
+ {
+ perror( "kdeinit: couldn't open() tty" );
+ return;
+ }
+ if( dup2( fd, STDOUT_FILENO ) < 0 )
+ {
+ perror( "kdeinit: couldn't dup2() tty" );
+ close( fd );
+ return;
+ }
+ if( dup2( fd, STDERR_FILENO ) < 0 )
+ {
+ perror( "kdeinit: couldn't dup2() tty" );
+ close( fd );
+ return;
+ }
+ close( fd );
+}
+
+// from kdecore/netwm.cpp
+static int get_current_desktop( Display* disp )
+{
+ int desktop = 0; // no desktop by default
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11 // Only X11 supports multiple desktops
+ Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP", False );
+ Atom type_ret;
+ int format_ret;
+ unsigned char *data_ret;
+ unsigned long nitems_ret, unused;
+ if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop,
+ 0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret )
+ == Success)
+ {
+ if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1)
+ desktop = *((long *) data_ret) + 1;
+ if (data_ret)
+ XFree ((char*) data_ret);
+ }
+#endif
+ return desktop;
+}
+
+// var has to be e.g. "DISPLAY=", i.e. with =
+const char* get_env_var( const char* var, int envc, const char* envs )
+{
+ if( envc > 0 )
+ { // get the var from envs
+ const char* env_l = envs;
+ int ln = strlen( var );
+ for (int i = 0; i < envc; i++)
+ {
+ if( strncmp( env_l, var, ln ) == 0 )
+ return env_l + ln;
+ while(*env_l != 0) env_l++;
+ env_l++;
+ }
+ }
+ return NULL;
+}
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded
+static void init_startup_info( KStartupInfoId& id, const char* bin,
+ int envc, const char* envs )
+{
+ const char* dpy = get_env_var( DISPLAY"=", envc, envs );
+ // this may be called in a child, so it can't use display open using X11display
+ // also needed for multihead
+ X11_startup_notify_display = XOpenDisplay( dpy );
+ if( X11_startup_notify_display == NULL )
+ return;
+ X11_startup_notify_fd = XConnectionNumber( X11_startup_notify_display );
+ KStartupInfoData data;
+ int desktop = get_current_desktop( X11_startup_notify_display );
+ data.setDesktop( desktop );
+ data.setBin( bin );
+ KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
+ XFlush( X11_startup_notify_display );
+}
+
+static void complete_startup_info( KStartupInfoId& id, pid_t pid )
+{
+ if( X11_startup_notify_display == NULL )
+ return;
+ if( pid == 0 ) // failure
+ KStartupInfo::sendFinishX( X11_startup_notify_display, id );
+ else
+ {
+ KStartupInfoData data;
+ data.addPid( pid );
+ data.setHostname();
+ KStartupInfo::sendChangeX( X11_startup_notify_display, id, data );
+ }
+ XCloseDisplay( X11_startup_notify_display );
+ X11_startup_notify_display = NULL;
+ X11_startup_notify_fd = -1;
+}
+#endif
+
+QCString execpath_avoid_loops( const QCString& exec, int envc, const char* envs, bool avoid_loops )
+{
+ QStringList paths;
+ if( envc > 0 ) /* use the passed environment */
+ {
+ const char* path = get_env_var( "PATH=", envc, envs );
+ if( path != NULL )
+ paths = QStringList::split( QRegExp( "[:\b]" ), path, true );
+ }
+ else
+ paths = QStringList::split( QRegExp( "[:\b]" ), getenv( "PATH" ), true );
+ QCString execpath = QFile::encodeName(
+ s_instance->dirs()->findExe( exec, paths.join( QString( ":" ))));
+ if( avoid_loops && !execpath.isEmpty())
+ {
+ int pos = execpath.findRev( '/' );
+ QString bin_path = execpath.left( pos );
+ for( QStringList::Iterator it = paths.begin();
+ it != paths.end();
+ ++it )
+ if( ( *it ) == bin_path || ( *it ) == bin_path + '/' )
+ {
+ paths.remove( it );
+ break; // -->
+ }
+ execpath = QFile::encodeName(
+ s_instance->dirs()->findExe( exec, paths.join( QString( ":" ))));
+ }
+ return execpath;
+}
+
+#ifdef KDEINIT_OOM_PROTECT
+static int oom_pipe = -1;
+
+static void oom_protect_sighandler( int ) {
+}
+
+static void reset_oom_protect() {
+ if( oom_pipe <= 0 )
+ return;
+ struct sigaction act, oldact;
+ act.sa_handler = oom_protect_sighandler;
+ act.sa_flags = 0;
+ sigemptyset( &act.sa_mask );
+ sigaction( SIGUSR1, &act, &oldact );
+ sigset_t sigs, oldsigs;
+ sigemptyset( &sigs );
+ sigaddset( &sigs, SIGUSR1 );
+ sigprocmask( SIG_BLOCK, &sigs, &oldsigs );
+ pid_t pid = getpid();
+ if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) {
+ sigsuspend( &oldsigs ); // wait for the signal to come
+ }
+ sigprocmask( SIG_SETMASK, &oldsigs, NULL );
+ sigaction( SIGUSR1, &oldact, NULL );
+ close( oom_pipe );
+ oom_pipe = -1;
+}
+#else
+static void reset_oom_protect() {
+}
+#endif
+
+static pid_t launch(int argc, const char *_name, const char *args,
+ const char *cwd=0, int envc=0, const char *envs=0,
+ bool reset_env = false,
+ const char *tty=0, bool avoid_loops = false,
+ const char* startup_id_str = "0" )
+{
+ int launcher = 0;
+ QCString lib;
+ QCString name;
+ QCString exec;
+
+ if (strcmp(_name, "klauncher") == 0) {
+ /* klauncher is launched in a special way:
+ * It has a communication socket on LAUNCHER_FD
+ */
+ if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher))
+ {
+ perror("kdeinit: socketpair() failed!\n");
+ exit(255);
+ }
+ launcher = 1;
+ }
+
+ QCString libpath;
+ QCString execpath;
+ if (_name[0] != '/')
+ {
+ /* Relative name without '.la' */
+ name = _name;
+ lib = name + ".la";
+ exec = name;
+ libpath = QFile::encodeName(KLibLoader::findLibrary( lib, s_instance ));
+ execpath = execpath_avoid_loops( exec, envc, envs, avoid_loops );
+ }
+ else
+ {
+ lib = _name;
+ name = _name;
+ name = name.mid( name.findRev('/') + 1);
+ exec = _name;
+ if (lib.right(3) == ".la")
+ libpath = lib;
+ else
+ execpath = exec;
+ }
+ if (!args)
+ {
+ argc = 1;
+ }
+
+ if (0 > pipe(d.fd))
+ {
+ perror("kdeinit: pipe() failed!\n");
+ d.result = 3;
+ d.errorMsg = i18n("Unable to start new process.\n"
+ "The system may have reached the maximum number of open files possible or the maximum number of open files that you are allowed to use has been reached.").utf8();
+ close(d.fd[0]);
+ close(d.fd[1]);
+ d.fork = 0;
+ return d.fork;
+ }
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ KStartupInfoId startup_id;
+ startup_id.initId( startup_id_str );
+ if( !startup_id.none())
+ init_startup_info( startup_id, name, envc, envs );
+#endif
+
+ d.errorMsg = 0;
+ d.fork = fork();
+ switch(d.fork) {
+ case -1:
+ perror("kdeinit: fork() failed!\n");
+ d.result = 3;
+ d.errorMsg = i18n("Unable to create new process.\n"
+ "The system may have reached the maximum number of processes possible or the maximum number of processes that you are allowed to use has been reached.").utf8();
+ close(d.fd[0]);
+ close(d.fd[1]);
+ d.fork = 0;
+ break;
+ case 0:
+ /** Child **/
+ close(d.fd[0]);
+ close_fds();
+ if (launcher)
+ {
+ if (d.fd[1] == LAUNCHER_FD)
+ {
+ d.fd[1] = dup(d.fd[1]); // Evacuate from LAUNCHER_FD
+ }
+ if (d.launcher[1] != LAUNCHER_FD)
+ {
+ dup2( d.launcher[1], LAUNCHER_FD); // Make sure the socket has fd LAUNCHER_FD
+ close( d.launcher[1] );
+ }
+ close( d.launcher[0] );
+ }
+ reset_oom_protect();
+
+ if (cwd && *cwd)
+ chdir(cwd);
+
+ if( reset_env ) // KWRAPPER/SHELL
+ {
+
+ QStrList unset_envs;
+ for( int tmp_env_count = 0;
+ environ[tmp_env_count];
+ tmp_env_count++)
+ unset_envs.append( environ[ tmp_env_count ] );
+ for( QStrListIterator it( unset_envs );
+ it.current() != NULL ;
+ ++it )
+ {
+ QCString tmp( it.current());
+ int pos = tmp.find( '=' );
+ if( pos >= 0 )
+ unsetenv( tmp.left( pos ));
+ }
+ }
+
+ for (int i = 0; i < envc; i++)
+ {
+ putenv((char *)envs);
+ while(*envs != 0) envs++;
+ envs++;
+ }
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if( startup_id.none())
+ KStartupInfo::resetStartupEnv();
+ else
+ startup_id.setupStartupEnv();
+#endif
+ {
+ int r;
+ QCString procTitle;
+ d.argv = (char **) malloc(sizeof(char *) * (argc+1));
+ d.argv[0] = (char *) _name;
+ for (int i = 1; i < argc; i++)
+ {
+ d.argv[i] = (char *) args;
+ procTitle += " ";
+ procTitle += (char *) args;
+ while(*args != 0) args++;
+ args++;
+ }
+ d.argv[argc] = 0;
+
+ /** Give the process a new name **/
+#ifdef Q_OS_LINUX
+ /* set the process name, so that killall works like intended */
+ r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0);
+ if ( r == 0 )
+ kdeinit_setproctitle( "%s [kdeinit]%s", name.data(), procTitle.data() ? procTitle.data() : "" );
+ else
+ kdeinit_setproctitle( "kdeinit: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
+#else
+ kdeinit_setproctitle( "kdeinit: %s%s", name.data(), procTitle.data() ? procTitle.data() : "" );
+#endif
+ }
+
+ d.handle = 0;
+ if (libpath.isEmpty() && execpath.isEmpty())
+ {
+ QString errorMsg = i18n("Could not find '%1' executable.").arg(QFile::decodeName(_name));
+ exitWithErrorMsg(errorMsg);
+ }
+
+ if ( getenv("KDE_IS_PRELINKED") && !execpath.isEmpty() && !launcher)
+ libpath.truncate(0);
+
+ if ( !libpath.isEmpty() )
+ {
+ d.handle = lt_dlopen( QFile::encodeName(libpath) );
+ if (!d.handle )
+ {
+ const char * ltdlError = lt_dlerror();
+ if (execpath.isEmpty())
+ {
+ // Error
+ QString errorMsg = i18n("Could not open library '%1'.\n%2").arg(QFile::decodeName(libpath))
+ .arg(ltdlError ? QFile::decodeName(ltdlError) : i18n("Unknown error"));
+ exitWithErrorMsg(errorMsg);
+ }
+ else
+ {
+ // Print warning
+ fprintf(stderr, "Could not open library %s: %s\n", lib.data(), ltdlError != 0 ? ltdlError : "(null)" );
+ }
+ }
+ }
+ lt_dlopen_flag = d.lt_dlopen_flag;
+ if (!d.handle )
+ {
+ d.result = 2; // Try execing
+ write(d.fd[1], &d.result, 1);
+
+ // We set the close on exec flag.
+ // Closing of d.fd[1] indicates that the execvp succeeded!
+ fcntl(d.fd[1], F_SETFD, FD_CLOEXEC);
+
+ setup_tty( tty );
+
+ execvp(execpath.data(), d.argv);
+ d.result = 1; // Error
+ write(d.fd[1], &d.result, 1);
+ close(d.fd[1]);
+ exit(255);
+ }
+
+ d.sym = lt_dlsym( d.handle, "kdeinitmain");
+ if (!d.sym )
+ {
+ d.sym = lt_dlsym( d.handle, "kdemain" );
+ if ( !d.sym )
+ {
+#if ! KDE_IS_VERSION( 3, 90, 0 )
+ d.sym = lt_dlsym( d.handle, "main");
+#endif
+ if (!d.sym )
+ {
+ const char * ltdlError = lt_dlerror();
+ fprintf(stderr, "Could not find kdemain: %s\n", ltdlError != 0 ? ltdlError : "(null)" );
+ QString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2").arg(libpath)
+ .arg(ltdlError ? QFile::decodeName(ltdlError) : i18n("Unknown error"));
+ exitWithErrorMsg(errorMsg);
+ }
+ }
+ }
+
+ d.result = 0; // Success
+ write(d.fd[1], &d.result, 1);
+ close(d.fd[1]);
+
+ d.func = (int (*)(int, char *[])) d.sym;
+ if (d.debug_wait)
+ {
+ fprintf(stderr, "kdeinit: Suspending process\n"
+ "kdeinit: 'gdb kdeinit %d' to debug\n"
+ "kdeinit: 'kill -SIGCONT %d' to continue\n",
+ getpid(), getpid());
+ kill(getpid(), SIGSTOP);
+ }
+ else
+ {
+ setup_tty( tty );
+ }
+
+ exit( d.func(argc, d.argv)); /* Launch! */
+
+ break;
+ default:
+ /** Parent **/
+ close(d.fd[1]);
+ if (launcher)
+ {
+ close(d.launcher[1]);
+ d.launcher_pid = d.fork;
+ }
+ bool exec = false;
+ for(;;)
+ {
+ d.n = read(d.fd[0], &d.result, 1);
+ if (d.n == 1)
+ {
+ if (d.result == 2)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "Could not load library! Trying exec....\n");
+#endif
+ exec = true;
+ continue;
+ }
+ if (d.result == 3)
+ {
+ int l = 0;
+ d.n = read(d.fd[0], &l, sizeof(int));
+ if (d.n == sizeof(int))
+ {
+ QCString tmp;
+ tmp.resize(l+1);
+ d.n = read(d.fd[0], tmp.data(), l);
+ tmp[l] = 0;
+ if (d.n == l)
+ d.errorMsg = tmp;
+ }
+ }
+ // Finished
+ break;
+ }
+ if (d.n == -1)
+ {
+ if (errno == ECHILD) { // a child died.
+ continue;
+ }
+ if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read
+ continue;
+ }
+ }
+ if (exec)
+ {
+ d.result = 0;
+ break;
+ }
+ if (d.n == 0)
+ {
+ perror("kdeinit: Pipe closed unexpectedly");
+ d.result = 1; // Error
+ break;
+ }
+ perror("kdeinit: Error reading from pipe");
+ d.result = 1; // Error
+ break;
+ }
+ close(d.fd[0]);
+ if (launcher && (d.result == 0))
+ {
+ // Trader launched successful
+ d.launcher_pid = d.fork;
+ }
+ }
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if( !startup_id.none())
+ {
+ if( d.fork && d.result == 0 ) // launched successfully
+ complete_startup_info( startup_id, d.fork );
+ else // failure, cancel ASN
+ complete_startup_info( startup_id, 0 );
+ }
+#endif
+ return d.fork;
+}
+
+static void sig_child_handler(int)
+{
+ /*
+ * Write into the pipe of death.
+ * This way we are sure that we return from the select()
+ *
+ * A signal itself causes select to return as well, but
+ * this creates a race-condition in case the signal arrives
+ * just before we enter the select.
+ */
+ char c = 0;
+ write(d.deadpipe[1], &c, 1);
+}
+
+static void init_signals()
+{
+ struct sigaction act;
+ long options;
+
+ if (pipe(d.deadpipe) != 0)
+ {
+ perror("kdeinit: Aborting. Can't create pipe: ");
+ exit(255);
+ }
+
+ options = fcntl(d.deadpipe[0], F_GETFL);
+ if (options == -1)
+ {
+ perror("kdeinit: Aborting. Can't make pipe non-blocking: ");
+ exit(255);
+ }
+
+ if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1)
+ {
+ perror("kdeinit: Aborting. Can't make pipe non-blocking: ");
+ exit(255);
+ }
+
+ /*
+ * A SIGCHLD handler is installed which sends a byte into the
+ * pipe of death. This is to ensure that a dying child causes
+ * an exit from select().
+ */
+ act.sa_handler=sig_child_handler;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGCHLD);
+ sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
+ act.sa_flags = SA_NOCLDSTOP;
+
+ // CC: take care of SunOS which automatically restarts interrupted system
+ // calls (and thus does not have SA_RESTART)
+
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+ sigaction( SIGCHLD, &act, 0L);
+
+ act.sa_handler=SIG_IGN;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGPIPE);
+ sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L);
+ act.sa_flags = 0;
+ sigaction( SIGPIPE, &act, 0L);
+}
+
+static void init_kdeinit_socket()
+{
+ struct sockaddr_un sa;
+ struct sockaddr_un sa_old;
+ kde_socklen_t socklen;
+ long options;
+ const char *home_dir = getenv("HOME");
+ int max_tries = 10;
+ if (!home_dir || !home_dir[0])
+ {
+ fprintf(stderr, "kdeinit: Aborting. $HOME not set!");
+ exit(255);
+ }
+ chdir(home_dir);
+
+ {
+ QCString path = home_dir;
+ QCString readOnly = getenv("KDE_HOME_READONLY");
+ if (access(path.data(), R_OK|W_OK))
+ {
+ if (errno == ENOENT)
+ {
+ fprintf(stderr, "kdeinit: Aborting. $HOME directory (%s) does not exist.\n", path.data());
+ exit(255);
+ }
+ else if (readOnly.isEmpty())
+ {
+ fprintf(stderr, "kdeinit: Aborting. No write access to $HOME directory (%s).\n", path.data());
+ exit(255);
+ }
+ }
+ path = getenv("ICEAUTHORITY");
+ if (path.isEmpty())
+ {
+ path = home_dir;
+ path += "/.ICEauthority";
+ }
+ if (access(path.data(), R_OK|W_OK) && (errno != ENOENT))
+ {
+ fprintf(stderr, "kdeinit: Aborting. No write access to '%s'.\n", path.data());
+ exit(255);
+ }
+ }
+
+ /** Test if socket file is already present
+ * note that access() resolves symlinks, and so we check the actual
+ * socket file if it exists
+ */
+ if (access(sock_file, W_OK) == 0)
+ {
+ int s;
+ struct sockaddr_un server;
+
+// fprintf(stderr, "kdeinit: Warning, socket_file already exists!\n");
+ /*
+ * create the socket stream
+ */
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ perror("socket() failed: ");
+ exit(255);
+ }
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, sock_file);
+ socklen = sizeof(server);
+
+ if(connect(s, (struct sockaddr *)&server, socklen) == 0)
+ {
+ fprintf(stderr, "kdeinit: Shutting down running client.\n");
+ klauncher_header request_header;
+ request_header.cmd = LAUNCHER_TERMINATE_KDEINIT;
+ request_header.arg_length = 0;
+ write(s, &request_header, sizeof(request_header));
+ sleep(1); // Give it some time
+ }
+ close(s);
+ }
+
+ /** Delete any stale socket file (and symlink) **/
+ unlink(sock_file);
+ unlink(sock_file_old);
+
+ /** create socket **/
+ d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (d.wrapper < 0)
+ {
+ perror("kdeinit: Aborting. socket() failed: ");
+ exit(255);
+ }
+
+ options = fcntl(d.wrapper, F_GETFL);
+ if (options == -1)
+ {
+ perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper);
+ exit(255);
+ }
+
+ if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1)
+ {
+ perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper);
+ exit(255);
+ }
+
+ while (1) {
+ /** bind it **/
+ socklen = sizeof(sa);
+ memset(&sa, 0, socklen);
+ sa.sun_family = AF_UNIX;
+ strcpy(sa.sun_path, sock_file);
+ if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0)
+ {
+ if (max_tries == 0) {
+ perror("kdeinit: Aborting. bind() failed: ");
+ fprintf(stderr, "Could not bind to socket '%s'\n", sock_file);
+ close(d.wrapper);
+ exit(255);
+ }
+ max_tries--;
+ } else
+ break;
+ }
+
+ /** set permissions **/
+ if (chmod(sock_file, 0600) != 0)
+ {
+ perror("kdeinit: Aborting. Can't set permissions on socket: ");
+ fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
+ unlink(sock_file);
+ close(d.wrapper);
+ exit(255);
+ }
+
+ if(listen(d.wrapper, SOMAXCONN) < 0)
+ {
+ perror("kdeinit: Aborting. listen() failed: ");
+ unlink(sock_file);
+ close(d.wrapper);
+ exit(255);
+ }
+
+ /** create compatibility socket **/
+ d.wrapper_old = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (d.wrapper_old < 0)
+ {
+ // perror("kdeinit: Aborting. socket() failed: ");
+ return;
+ }
+
+ options = fcntl(d.wrapper_old, F_GETFL);
+ if (options == -1)
+ {
+ // perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+
+ if (fcntl(d.wrapper_old, F_SETFL, options | O_NONBLOCK) == -1)
+ {
+ // perror("kdeinit: Aborting. Can't make socket non-blocking: ");
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+
+ max_tries = 10;
+ while (1) {
+ /** bind it **/
+ socklen = sizeof(sa_old);
+ memset(&sa_old, 0, socklen);
+ sa_old.sun_family = AF_UNIX;
+ strcpy(sa_old.sun_path, sock_file_old);
+ if(bind(d.wrapper_old, (struct sockaddr *)&sa_old, socklen) != 0)
+ {
+ if (max_tries == 0) {
+ // perror("kdeinit: Aborting. bind() failed: ");
+ fprintf(stderr, "Could not bind to socket '%s'\n", sock_file_old);
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+ max_tries--;
+ } else
+ break;
+ }
+
+ /** set permissions **/
+ if (chmod(sock_file_old, 0600) != 0)
+ {
+ fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file);
+ unlink(sock_file_old);
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ return;
+ }
+
+ if(listen(d.wrapper_old, SOMAXCONN) < 0)
+ {
+ // perror("kdeinit: Aborting. listen() failed: ");
+ unlink(sock_file_old);
+ close(d.wrapper_old);
+ d.wrapper_old = 0;
+ }
+}
+
+/*
+ * Read 'len' bytes from 'sock' into buffer.
+ * returns 0 on success, -1 on failure.
+ */
+static int read_socket(int sock, char *buffer, int len)
+{
+ ssize_t result;
+ int bytes_left = len;
+ while ( bytes_left > 0)
+ {
+ result = read(sock, buffer, bytes_left);
+ if (result > 0)
+ {
+ buffer += result;
+ bytes_left -= result;
+ }
+ else if (result == 0)
+ return -1;
+ else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
+ return -1;
+ }
+ return 0;
+}
+
+static void WaitPid( pid_t waitForPid)
+{
+ int result;
+ while(1)
+ {
+ result = waitpid(waitForPid, &d.exit_status, 0);
+ if ((result == -1) && (errno == ECHILD))
+ return;
+ }
+}
+
+static void launcher_died()
+{
+ if (!d.launcher_ok)
+ {
+ /* This is bad. */
+ fprintf(stderr, "kdeinit: Communication error with launcher. Exiting!\n");
+ ::exit(255);
+ return;
+ }
+
+ // KLauncher died... restart
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: KLauncher died unexpectedly.\n");
+#endif
+ // Make sure it's really dead.
+ if (d.launcher_pid)
+ {
+ kill(d.launcher_pid, SIGKILL);
+ sleep(1); // Give it some time
+ }
+
+ d.launcher_ok = false;
+ d.launcher_pid = 0;
+ close(d.launcher[0]);
+ d.launcher[0] = -1;
+
+ pid_t pid = launch( 1, "klauncher", 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Relaunching KLauncher, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+}
+
+static void handle_launcher_request(int sock = -1)
+{
+ bool launcher = false;
+ if (sock < 0)
+ {
+ sock = d.launcher[0];
+ launcher = true;
+ }
+
+ klauncher_header request_header;
+ char *request_data = 0L;
+ int result = read_socket(sock, (char *) &request_header, sizeof(request_header));
+ if (result != 0)
+ {
+ if (launcher)
+ launcher_died();
+ return;
+ }
+
+ if ( request_header.arg_length != 0 )
+ {
+ request_data = (char *) malloc(request_header.arg_length);
+
+ result = read_socket(sock, request_data, request_header.arg_length);
+ if (result != 0)
+ {
+ if (launcher)
+ launcher_died();
+ free(request_data);
+ return;
+ }
+ }
+
+ if (request_header.cmd == LAUNCHER_OK)
+ {
+ d.launcher_ok = true;
+ }
+ else if (request_header.arg_length &&
+ ((request_header.cmd == LAUNCHER_EXEC) ||
+ (request_header.cmd == LAUNCHER_EXT_EXEC) ||
+ (request_header.cmd == LAUNCHER_SHELL ) ||
+ (request_header.cmd == LAUNCHER_KWRAPPER) ||
+ (request_header.cmd == LAUNCHER_EXEC_NEW)))
+ {
+ pid_t pid;
+ klauncher_header response_header;
+ long response_data;
+ long l;
+ memcpy( &l, request_data, sizeof( long ));
+ int argc = l;
+ const char *name = request_data + sizeof(long);
+ const char *args = name + strlen(name) + 1;
+ const char *cwd = 0;
+ int envc = 0;
+ const char *envs = 0;
+ const char *tty = 0;
+ int avoid_loops = 0;
+ const char *startup_id_str = "0";
+
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Got %s '%s' from %s.\n",
+ (request_header.cmd == LAUNCHER_EXEC ? "EXEC" :
+ (request_header.cmd == LAUNCHER_EXT_EXEC ? "EXT_EXEC" :
+ (request_header.cmd == LAUNCHER_EXEC_NEW ? "EXEC_NEW" :
+ (request_header.cmd == LAUNCHER_SHELL ? "SHELL" : "KWRAPPER" )))),
+ name, launcher ? "launcher" : "socket" );
+#endif
+
+ const char *arg_n = args;
+ for(int i = 1; i < argc; i++)
+ {
+ arg_n = arg_n + strlen(arg_n) + 1;
+ }
+
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER )
+ {
+ // Shell or kwrapper
+ cwd = arg_n; arg_n += strlen(cwd) + 1;
+ }
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
+ || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
+ {
+ memcpy( &l, arg_n, sizeof( long ));
+ envc = l;
+ arg_n += sizeof(long);
+ envs = arg_n;
+ for(int i = 0; i < envc; i++)
+ {
+ arg_n = arg_n + strlen(arg_n) + 1;
+ }
+ if( request_header.cmd == LAUNCHER_KWRAPPER )
+ {
+ tty = arg_n;
+ arg_n += strlen( tty ) + 1;
+ }
+ }
+
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
+ || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )
+ {
+ memcpy( &l, arg_n, sizeof( long ));
+ avoid_loops = l;
+ arg_n += sizeof( long );
+ }
+
+ if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER
+ || request_header.cmd == LAUNCHER_EXT_EXEC )
+ {
+ startup_id_str = arg_n;
+ arg_n += strlen( startup_id_str ) + 1;
+ }
+
+ if ((request_header.arg_length > (arg_n - request_data)) &&
+ (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ))
+ {
+ // Optional cwd
+ cwd = arg_n; arg_n += strlen(cwd) + 1;
+ }
+
+ if ((arg_n - request_data) != request_header.arg_length)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: EXEC request has invalid format.\n");
+#endif
+ free(request_data);
+ d.debug_wait = false;
+ return;
+ }
+
+ // support for the old a bit broken way of setting DISPLAY for multihead
+ QCString olddisplay = getenv(DISPLAY);
+ QCString kdedisplay = getenv("KDE_DISPLAY");
+ bool reset_display = (! olddisplay.isEmpty() &&
+ ! kdedisplay.isEmpty() &&
+ olddisplay != kdedisplay);
+
+ if (reset_display)
+ setenv(DISPLAY, kdedisplay, true);
+
+ pid = launch( argc, name, args, cwd, envc, envs,
+ request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER,
+ tty, avoid_loops, startup_id_str );
+
+ if (reset_display) {
+ unsetenv("KDE_DISPLAY");
+ setenv(DISPLAY, olddisplay, true);
+ }
+
+ if (pid && (d.result == 0))
+ {
+ response_header.cmd = LAUNCHER_OK;
+ response_header.arg_length = sizeof(response_data);
+ response_data = pid;
+ write(sock, &response_header, sizeof(response_header));
+ write(sock, &response_data, response_header.arg_length);
+ }
+ else
+ {
+ int l = d.errorMsg.length();
+ if (l) l++; // Include trailing null.
+ response_header.cmd = LAUNCHER_ERROR;
+ response_header.arg_length = l;
+ write(sock, &response_header, sizeof(response_header));
+ if (l)
+ write(sock, d.errorMsg.data(), l);
+ }
+ d.debug_wait = false;
+ }
+ else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV)
+ {
+ const char *env_name;
+ const char *env_value;
+ env_name = request_data;
+ env_value = env_name + strlen(env_name) + 1;
+
+#ifndef NDEBUG
+ if (launcher)
+ fprintf(stderr, "kdeinit: Got SETENV '%s=%s' from klauncher.\n", env_name, env_value);
+ else
+ fprintf(stderr, "kdeinit: Got SETENV '%s=%s' from socket.\n", env_name, env_value);
+#endif
+
+ if ( request_header.arg_length !=
+ (int) (strlen(env_name) + strlen(env_value) + 2))
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: SETENV request has invalid format.\n");
+#endif
+ free(request_data);
+ return;
+ }
+ setenv( env_name, env_value, 1);
+ }
+ else if (request_header.cmd == LAUNCHER_TERMINATE_KDE)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,"kdeinit: terminate KDE.\n");
+#endif
+#ifdef Q_WS_X11
+ kdeinit_xio_errhandler( 0L );
+#endif
+ }
+ else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,"kdeinit: Killing kdeinit/klauncher.\n");
+#endif
+ if (d.launcher_pid)
+ kill(d.launcher_pid, SIGTERM);
+ if (d.my_pid)
+ kill(d.my_pid, SIGTERM);
+ }
+ else if (request_header.cmd == LAUNCHER_DEBUG_WAIT)
+ {
+#ifndef NDEBUG
+ fprintf(stderr,"kdeinit: Debug wait activated.\n");
+#endif
+ d.debug_wait = true;
+ }
+ if (request_data)
+ free(request_data);
+}
+
+static void handle_requests(pid_t waitForPid)
+{
+ int max_sock = d.wrapper;
+ if (d.wrapper_old > max_sock)
+ max_sock = d.wrapper_old;
+ if (d.launcher_pid && (d.launcher[0] > max_sock))
+ max_sock = d.launcher[0];
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef _WS_X11
+ if (X11fd > max_sock)
+ max_sock = X11fd;
+#endif
+ max_sock++;
+
+ while(1)
+ {
+ fd_set rd_set;
+ fd_set wr_set;
+ fd_set e_set;
+ int result;
+ pid_t exit_pid;
+ char c;
+
+ /* Flush the pipe of death */
+ while( read(d.deadpipe[0], &c, 1) == 1);
+
+ /* Handle dying children */
+ do {
+ exit_pid = waitpid(-1, 0, WNOHANG);
+ if (exit_pid > 0)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: PID %ld terminated.\n", (long) exit_pid);
+#endif
+ if (waitForPid && (exit_pid == waitForPid))
+ return;
+
+ if (d.launcher_pid)
+ {
+ // TODO send process died message
+ klauncher_header request_header;
+ long request_data[2];
+ request_header.cmd = LAUNCHER_DIED;
+ request_header.arg_length = sizeof(long) * 2;
+ request_data[0] = exit_pid;
+ request_data[1] = 0; /* not implemented yet */
+ write(d.launcher[0], &request_header, sizeof(request_header));
+ write(d.launcher[0], request_data, request_header.arg_length);
+ }
+ }
+ }
+ while( exit_pid > 0);
+
+ FD_ZERO(&rd_set);
+ FD_ZERO(&wr_set);
+ FD_ZERO(&e_set);
+
+ if (d.launcher_pid)
+ {
+ FD_SET(d.launcher[0], &rd_set);
+ }
+ FD_SET(d.wrapper, &rd_set);
+ if (d.wrapper_old)
+ {
+ FD_SET(d.wrapper_old, &rd_set);
+ }
+ FD_SET(d.deadpipe[0], &rd_set);
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if(X11fd >= 0) FD_SET(X11fd, &rd_set);
+#endif
+
+ result = select(max_sock, &rd_set, &wr_set, &e_set, 0);
+
+ /* Handle wrapper request */
+ if ((result > 0) && (FD_ISSET(d.wrapper, &rd_set)))
+ {
+ struct sockaddr_un client;
+ kde_socklen_t sClient = sizeof(client);
+ int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient);
+ if (sock >= 0)
+ {
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+ if( FcGetVersion() < 20390 && !FcConfigUptoDate(NULL))
+ FcInitReinitialize();
+#endif
+ if (fork() == 0)
+ {
+ close_fds();
+ reset_oom_protect();
+ handle_launcher_request(sock);
+ exit(255); /* Terminate process. */
+ }
+ close(sock);
+ }
+ }
+ if ((result > 0) && (FD_ISSET(d.wrapper_old, &rd_set)))
+ {
+ struct sockaddr_un client;
+ kde_socklen_t sClient = sizeof(client);
+ int sock = accept(d.wrapper_old, (struct sockaddr *)&client, &sClient);
+ if (sock >= 0)
+ {
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+ if( FcGetVersion() < 20390 && !FcConfigUptoDate(NULL))
+ FcInitReinitialize();
+#endif
+ if (fork() == 0)
+ {
+ close_fds();
+ reset_oom_protect();
+ handle_launcher_request(sock);
+ exit(255); /* Terminate process. */
+ }
+ close(sock);
+ }
+ }
+
+ /* Handle launcher request */
+ if ((result > 0) && (d.launcher_pid) && (FD_ISSET(d.launcher[0], &rd_set)))
+ {
+ handle_launcher_request();
+ if (waitForPid == d.launcher_pid)
+ return;
+ }
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+ /* Look for incoming X11 events */
+ if((result > 0) && (X11fd >= 0))
+ {
+ if(FD_ISSET(X11fd,&rd_set))
+ {
+ if (X11display != 0) {
+ XEvent event_return;
+ while (XPending(X11display))
+ XNextEvent(X11display, &event_return);
+ }
+ }
+ }
+#endif
+ }
+}
+
+static void kdeinit_library_path()
+{
+ QStringList ltdl_library_path =
+ QStringList::split(':', QFile::decodeName(getenv("LTDL_LIBRARY_PATH")));
+ QStringList ld_library_path =
+ QStringList::split(':', QFile::decodeName(getenv("LD_LIBRARY_PATH")));
+
+ QCString extra_path;
+ QStringList candidates = s_instance->dirs()->resourceDirs("lib");
+ for (QStringList::ConstIterator it = candidates.begin();
+ it != candidates.end();
+ it++)
+ {
+ QString d = *it;
+ if (ltdl_library_path.contains(d))
+ continue;
+ if (ld_library_path.contains(d))
+ continue;
+ if (d[d.length()-1] == '/')
+ {
+ d.truncate(d.length()-1);
+ if (ltdl_library_path.contains(d))
+ continue;
+ if (ld_library_path.contains(d))
+ continue;
+ }
+ if ((d == "/lib") || (d == "/usr/lib"))
+ continue;
+
+ QCString dir = QFile::encodeName(d);
+
+ if (access(dir, R_OK))
+ continue;
+
+ if ( !extra_path.isEmpty())
+ extra_path += ":";
+ extra_path += dir;
+ }
+
+ if (lt_dlinit())
+ {
+ const char * ltdlError = lt_dlerror();
+ fprintf(stderr, "can't initialize dynamic loading: %s\n", ltdlError != 0 ? ltdlError : "(null)" );
+ }
+ if (!extra_path.isEmpty())
+ lt_dlsetsearchpath(extra_path.data());
+
+ QCString display = getenv(DISPLAY);
+ if (display.isEmpty())
+ {
+ fprintf(stderr, "kdeinit: Aborting. $"DISPLAY" is not set.\n");
+ exit(255);
+ }
+ int i;
+ if((i = display.findRev('.')) > display.findRev(':') && i >= 0)
+ display.truncate(i);
+
+ QCString socketName = QFile::encodeName(locateLocal("socket", QString("kdeinit-%1").arg(display), s_instance));
+ if (socketName.length() >= MAX_SOCK_FILE)
+ {
+ fprintf(stderr, "kdeinit: Aborting. Socket name will be too long:\n");
+ fprintf(stderr, " '%s'\n", socketName.data());
+ exit(255);
+ }
+ strcpy(sock_file_old, socketName.data());
+
+ display.replace(":","_");
+ socketName = QFile::encodeName(locateLocal("socket", QString("kdeinit_%1").arg(display), s_instance));
+ if (socketName.length() >= MAX_SOCK_FILE)
+ {
+ fprintf(stderr, "kdeinit: Aborting. Socket name will be too long:\n");
+ fprintf(stderr, " '%s'\n", socketName.data());
+ exit(255);
+ }
+ strcpy(sock_file, socketName.data());
+}
+
+int kdeinit_xio_errhandler( Display *disp )
+{
+ // disp is 0L when KDE shuts down. We don't want those warnings then.
+
+ if ( disp )
+ qWarning( "kdeinit: Fatal IO error: client killed" );
+
+ if (sock_file[0])
+ {
+ /** Delete any stale socket file **/
+ unlink(sock_file);
+ }
+ if (sock_file_old[0])
+ {
+ /** Delete any stale socket file **/
+ unlink(sock_file_old);
+ }
+
+ // Don't kill our children in suicide mode, they may still be in use
+ if (d.suicide)
+ {
+ if (d.launcher_pid)
+ kill(d.launcher_pid, SIGTERM);
+ exit( 0 );
+ }
+
+ if ( disp )
+ qWarning( "kdeinit: sending SIGHUP to children." );
+
+ /* this should remove all children we started */
+ signal(SIGHUP, SIG_IGN);
+ kill(0, SIGHUP);
+
+ sleep(2);
+
+ if ( disp )
+ qWarning( "kdeinit: sending SIGTERM to children." );
+
+ /* and if they don't listen to us, this should work */
+ signal(SIGTERM, SIG_IGN);
+ kill(0, SIGTERM);
+
+ if ( disp )
+ qWarning( "kdeinit: Exit." );
+
+ exit( 0 );
+ return 0;
+}
+
+#ifdef Q_WS_X11
+int kdeinit_x_errhandler( Display *dpy, XErrorEvent *err )
+{
+#ifndef NDEBUG
+ char errstr[256];
+ // kdeinit almost doesn't use X, and therefore there shouldn't be any X error
+ XGetErrorText( dpy, err->error_code, errstr, 256 );
+ fprintf(stderr, "kdeinit: KDE detected X Error: %s %d\n"
+ " Major opcode: %d\n"
+ " Minor opcode: %d\n"
+ " Resource id: 0x%lx\n",
+ errstr, err->error_code, err->request_code, err->minor_code, err->resourceid );
+#else
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+#endif
+ return 0;
+}
+#endif
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+// needs to be done sooner than initXconnection() because of also opening
+// another X connection for startup notification purposes
+static void setupX()
+{
+ XSetIOErrorHandler(kdeinit_xio_errhandler);
+ XSetErrorHandler(kdeinit_x_errhandler);
+}
+
+// Borrowed from kdebase/kaudio/kaudioserver.cpp
+static int initXconnection()
+{
+ X11display = XOpenDisplay(NULL);
+ if ( X11display != 0 ) {
+ XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \
+ 0,
+ BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)),
+ BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: opened connection to %s\n", DisplayString(X11display));
+#endif
+ int fd = XConnectionNumber( X11display );
+ int on = 1;
+ (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on));
+ return fd;
+ } else
+ fprintf(stderr, "kdeinit: Can't connect to the X Server.\n" \
+ "kdeinit: Might not terminate at end of session.\n");
+
+ return -1;
+}
+#endif
+
+#ifdef __KCC
+/* One of my horrible hacks. KCC includes in each "main" function a call
+ to _main(), which is provided by the C++ runtime system. It is
+ responsible for calling constructors for some static objects. That must
+ be done only once, so _main() is guarded against multiple calls.
+ For unknown reasons the designers of KAI's libKCC decided it would be
+ a good idea to actually abort() when it's called multiple times, instead
+ of ignoring further calls. This breaks our mechanism of KLM's, because
+ most KLM's have a main() function which is called from us.
+ The "solution" is to simply define our own _main(), which ignores multiple
+ calls, which is easy, and which does the same work as KAI'c _main(),
+ which is difficult. Currently (KAI 4.0f) it only calls __call_ctors(void)
+ (a C++ function), but if that changes we need to change our's too.
+ (matz) */
+/*
+ Those 'unknown reasons' are C++ standard forbidding recursive calls to main()
+ or any means that would possibly allow that (e.g. taking address of main()).
+ The correct solution is not using main() as entry point for kdeinit modules,
+ but only kdemain().
+*/
+extern "C" void _main(void);
+extern "C" void __call_ctors__Fv(void);
+static int main_called = 0;
+void _main(void)
+{
+ if (main_called)
+ return;
+ main_called = 1;
+ __call_ctors__Fv ();
+}
+#endif
+
+static void secondary_child_handler(int)
+{
+ waitpid(-1, 0, WNOHANG);
+}
+
+int main(int argc, char **argv, char **envp)
+{
+ int i;
+ pid_t pid;
+ int launch_dcop = 1;
+ int launch_klauncher = 1;
+ int launch_kded = 1;
+ int keep_running = 1;
+ int new_startup = 0;
+ d.suicide = false;
+
+ /** Save arguments first... **/
+ char **safe_argv = (char **) malloc( sizeof(char *) * argc);
+ for(i = 0; i < argc; i++)
+ {
+ safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]);
+ if (strcmp(safe_argv[i], "--no-dcop") == 0)
+ launch_dcop = 0;
+ if (strcmp(safe_argv[i], "--no-klauncher") == 0)
+ launch_klauncher = 0;
+ if (strcmp(safe_argv[i], "--no-kded") == 0)
+ launch_kded = 0;
+ if (strcmp(safe_argv[i], "--suicide") == 0)
+ d.suicide = true;
+ if (strcmp(safe_argv[i], "--exit") == 0)
+ keep_running = 0;
+ if (strcmp(safe_argv[i], "--new-startup") == 0)
+ new_startup = 1;
+#ifdef KDEINIT_OOM_PROTECT
+ if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i+1<argc)
+ oom_pipe = atol(argv[i+1]);
+#endif
+ if (strcmp(safe_argv[i], "--help") == 0)
+ {
+ printf("Usage: kdeinit [options]\n");
+ // printf(" --no-dcop Do not start dcopserver\n");
+ // printf(" --no-klauncher Do not start klauncher\n");
+ printf(" --no-kded Do not start kded\n");
+ printf(" --suicide Terminate when no KDE applications are left running\n");
+ // printf(" --exit Terminate when kded has run\n");
+ exit(0);
+ }
+ }
+
+ pipe(d.initpipe);
+
+ // Fork here and let parent process exit.
+ // Parent process may only exit after all required services have been
+ // launched. (dcopserver/klauncher and services which start with '+')
+ signal( SIGCHLD, secondary_child_handler);
+ if (fork() > 0) // Go into background
+ {
+ close(d.initpipe[1]);
+ d.initpipe[1] = -1;
+ // wait till init is complete
+ char c;
+ while( read(d.initpipe[0], &c, 1) < 0);
+ // then exit;
+ close(d.initpipe[0]);
+ d.initpipe[0] = -1;
+ return 0;
+ }
+ close(d.initpipe[0]);
+ d.initpipe[0] = -1;
+ d.my_pid = getpid();
+
+ /** Make process group leader (for shutting down children later) **/
+ if(keep_running)
+ setsid();
+
+ /** Create our instance **/
+ s_instance = new KInstance("kdeinit");
+
+ /** Prepare to change process name **/
+ kdeinit_initsetproctitle(argc, argv, envp);
+ kdeinit_library_path();
+ // Don't make our instance the global instance
+ // (do it only after kdeinit_library_path, that one indirectly uses KConfig,
+ // which seems to be buggy and always use KGlobal instead of the maching KInstance)
+ KGlobal::_instance = 0L;
+ // don't change envvars before kdeinit_initsetproctitle()
+ unsetenv("LD_BIND_NOW");
+ unsetenv("DYLD_BIND_AT_LAUNCH");
+ KApplication::loadedByKdeinit = true;
+
+ d.maxname = strlen(argv[0]);
+ d.launcher_pid = 0;
+ d.wrapper = 0;
+ d.wrapper_old = 0;
+ d.debug_wait = false;
+ d.launcher_ok = false;
+ d.lt_dlopen_flag = lt_dlopen_flag;
+ lt_dlopen_flag |= LTDL_GLOBAL;
+ init_signals();
+#ifdef Q_WS_X11
+ setupX();
+#endif
+
+ if (keep_running)
+ {
+ /*
+ * Create ~/.kde/tmp-<hostname>/kdeinit-<display> socket for incoming wrapper
+ * requests.
+ */
+ init_kdeinit_socket();
+ }
+
+ if (launch_dcop)
+ {
+ if (d.suicide)
+ pid = launch( 3, "dcopserver", "--nosid\0--suicide" );
+ else
+ pid = launch( 2, "dcopserver", "--nosid" );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched DCOPServer, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+ WaitPid(pid);
+ if (!WIFEXITED(d.exit_status) || (WEXITSTATUS(d.exit_status) != 0))
+ {
+ fprintf(stderr, "kdeinit: DCOPServer could not be started, aborting.\n");
+ exit(1);
+ }
+ }
+#ifndef __CYGWIN__
+ if (!d.suicide && !getenv("KDE_IS_PRELINKED"))
+ {
+ QString konq = locate("lib", "libkonq.la", s_instance);
+ if (!konq.isEmpty())
+ (void) lt_dlopen(QFile::encodeName(konq).data());
+ }
+#endif
+ if (launch_klauncher)
+ {
+ if( new_startup )
+ pid = launch( 2, "klauncher", "--new-startup" );
+ else
+ pid = launch( 1, "klauncher", 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched KLauncher, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+ handle_requests(pid); // Wait for klauncher to be ready
+ }
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ X11fd = initXconnection();
+#endif
+
+ {
+#if defined(KDEINIT_USE_XFT) && defined(KDEINIT_USE_FONTCONFIG)
+ if( FcGetVersion() < 20390 )
+ {
+ XftInit(0);
+ XftInitFtLibrary();
+ }
+#endif
+ QFont::initialize();
+ setlocale (LC_ALL, "");
+ setlocale (LC_NUMERIC, "C");
+#ifdef Q_WS_X11
+ if (XSupportsLocale ())
+ {
+ // Similar to QApplication::create_xim()
+ // but we need to use our own display
+ XOpenIM (X11display, 0, 0, 0);
+ }
+#endif
+ }
+
+ if (launch_kded)
+ {
+ if( new_startup )
+ pid = launch( 2, "kded", "--new-startup" );
+ else
+ pid = launch( 1, "kded", 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched KDED, pid = %ld result = %d\n", (long) pid, d.result);
+#endif
+ handle_requests(pid);
+ }
+
+ for(i = 1; i < argc; i++)
+ {
+ if (safe_argv[i][0] == '+')
+ {
+ pid = launch( 1, safe_argv[i]+1, 0);
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched '%s', pid = %ld result = %d\n", safe_argv[i]+1, (long) pid, d.result);
+#endif
+ handle_requests(pid);
+ }
+ else if (safe_argv[i][0] == '-'
+#ifdef KDEINIT_OOM_PROTECT
+ || isdigit(safe_argv[i][0])
+#endif
+ )
+ {
+ // Ignore
+ }
+ else
+ {
+ pid = launch( 1, safe_argv[i], 0 );
+#ifndef NDEBUG
+ fprintf(stderr, "kdeinit: Launched '%s', pid = %ld result = %d\n", safe_argv[i], (long) pid, d.result);
+#endif
+ }
+ }
+
+ /** Free arguments **/
+ for(i = 0; i < argc; i++)
+ {
+ free(safe_argv[i]);
+ }
+ free (safe_argv);
+
+ kdeinit_setproctitle("kdeinit Running...");
+
+ if (!keep_running)
+ return 0;
+
+ char c = 0;
+ write(d.initpipe[1], &c, 1); // Kdeinit is started.
+ close(d.initpipe[1]);
+ d.initpipe[1] = -1;
+
+ handle_requests(0);
+
+ return 0;
+}
+
diff --git a/kinit/kioslave.cpp b/kinit/kioslave.cpp
new file mode 100644
index 000000000..0195f2867
--- /dev/null
+++ b/kinit/kioslave.cpp
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org>
+ * (c) 1999 Mario Weilguni <mweilguni@sime.com>
+ * (c) 2001 Lubos Lunak <l.lunak@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <qstring.h>
+
+#include "ltdl.h"
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+
+#ifdef RTLD_GLOBAL
+# define LTDL_GLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LTDL_GLOBAL DL_GLOBAL
+# else
+# define LTDL_GLOBAL 0
+# endif
+#endif
+
+/* These are to link libkio even if 'smart' linker is used */
+#include <kio/authinfo.h>
+extern "C" KIO::AuthInfo* _kioslave_init_kio() { return new KIO::AuthInfo(); }
+
+int main(int argc, char **argv)
+{
+ if (argc < 5)
+ {
+ fprintf(stderr, "Usage: kioslave <slave-lib> <protocol> <klauncher-socket> <app-socket>\n\nThis program is part of KDE.\n");
+ exit(1);
+ }
+ QCString libpath = argv[1];
+
+ if (libpath.isEmpty())
+ {
+ fprintf(stderr, "library path is empty.\n");
+ exit(1);
+ }
+ lt_dlinit();
+
+ lt_dlhandle handle = lt_dlopen( libpath.data() );
+ if (!handle )
+ {
+ const char * ltdlError = lt_dlerror();
+ fprintf(stderr, "could not open %s: %s", libpath.data(), ltdlError != 0 ? ltdlError : "(null)" );
+ exit(1);
+ }
+
+ lt_ptr sym = lt_dlsym( handle, "kdemain");
+ if (!sym )
+ {
+ sym = lt_dlsym( handle, "main");
+ if (!sym )
+ {
+ const char * ltdlError = lt_dlerror();
+ fprintf(stderr, "Could not find main: %s\n", ltdlError != 0 ? ltdlError : "(null)" );
+ exit(1);
+ }
+ }
+
+ int (*func)(int, char *[]) = (int (*)(int, char *[])) sym;
+
+ exit( func(argc-1, argv+1)); /* Launch! */
+}
diff --git a/kinit/klauncher.cpp b/kinit/klauncher.cpp
new file mode 100644
index 000000000..ae4692d60
--- /dev/null
+++ b/kinit/klauncher.cpp
@@ -0,0 +1,1420 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 Waldo Bastian <bastian@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.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include <qfile.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kprotocolmanager.h>
+#include <kprotocolinfo.h>
+#include <krun.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+#include <kurl.h>
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#include <kstartupinfo.h> // schroder
+#endif
+
+
+#include "kio/global.h"
+#include "kio/connection.h"
+#include "kio/slaveinterface.h"
+
+#include "klauncher.h"
+#include "klauncher_cmds.h"
+
+//#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+#ifdef Q_WS_X11
+//#undef K_WS_QTONLY
+#include <X11/Xlib.h> // schroder
+#endif
+
+// Dispose slaves after being idle for SLAVE_MAX_IDLE seconds
+#define SLAVE_MAX_IDLE 30
+
+using namespace KIO;
+
+template class QPtrList<KLaunchRequest>;
+template class QPtrList<IdleSlave>;
+
+IdleSlave::IdleSlave(KSocket *socket)
+{
+ mConn.init(socket);
+ mConn.connect(this, SLOT(gotInput()));
+ mConn.send( CMD_SLAVE_STATUS );
+ mPid = 0;
+ mBirthDate = time(0);
+ mOnHold = false;
+}
+
+void
+IdleSlave::gotInput()
+{
+ int cmd;
+ QByteArray data;
+ if (mConn.read( &cmd, data) == -1)
+ {
+ // Communication problem with slave.
+ kdError(7016) << "SlavePool: No communication with slave." << endl;
+ delete this;
+ }
+ else if (cmd == MSG_SLAVE_ACK)
+ {
+ delete this;
+ }
+ else if (cmd != MSG_SLAVE_STATUS)
+ {
+ kdError(7016) << "SlavePool: Unexpected data from slave." << endl;
+ delete this;
+ }
+ else
+ {
+ QDataStream stream( data, IO_ReadOnly );
+ pid_t pid;
+ QCString protocol;
+ QString host;
+ Q_INT8 b;
+ stream >> pid >> protocol >> host >> b;
+// Overload with (bool) onHold, (KURL) url.
+ if (!stream.atEnd())
+ {
+ KURL url;
+ stream >> url;
+ mOnHold = true;
+ mUrl = url;
+ }
+
+ mPid = pid;
+ mConnected = (b != 0);
+ mProtocol = protocol;
+ mHost = host;
+ emit statusUpdate(this);
+ }
+}
+
+void
+IdleSlave::connect(const QString &app_socket)
+{
+ QByteArray data;
+ QDataStream stream( data, IO_WriteOnly);
+ stream << app_socket;
+ mConn.send( CMD_SLAVE_CONNECT, data );
+ // Timeout!
+}
+
+void
+IdleSlave::reparseConfiguration()
+{
+ mConn.send( CMD_REPARSECONFIGURATION );
+}
+
+bool
+IdleSlave::match(const QString &protocol, const QString &host, bool connected)
+{
+ if (mOnHold) return false;
+ if (protocol != mProtocol) return false;
+ if (host.isEmpty()) return true;
+ if (host != mHost) return false;
+ if (!connected) return true;
+ if (!mConnected) return false;
+ return true;
+}
+
+bool
+IdleSlave::onHold(const KURL &url)
+{
+ if (!mOnHold) return false;
+ return (url == mUrl);
+}
+
+int
+IdleSlave::age(time_t now)
+{
+ return (int) difftime(now, mBirthDate);
+}
+
+KLauncher::KLauncher(int _kdeinitSocket, bool new_startup)
+ : KApplication( false, false ), // No Styles, No GUI
+ DCOPObject("klauncher"),
+ kdeinitSocket(_kdeinitSocket), mAutoStart( new_startup ),
+ dontBlockReading(false), newStartup( new_startup )
+{
+#ifdef Q_WS_X11
+ mCached_dpy = NULL;
+#endif
+ connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart()));
+ requestList.setAutoDelete(true);
+ mSlaveWaitRequest.setAutoDelete(true);
+ dcopClient()->setNotifications( true );
+ connect(dcopClient(), SIGNAL( applicationRegistered( const QCString &)),
+ this, SLOT( slotAppRegistered( const QCString &)));
+ dcopClient()->connectDCOPSignal( "DCOPServer", "", "terminateKDE()",
+ objId(), "terminateKDE()", false );
+
+ QString prefix = locateLocal("socket", "klauncher");
+ KTempFile domainname(prefix, QString::fromLatin1(".slave-socket"));
+ if (domainname.status() != 0)
+ {
+ // Sever error!
+ qDebug("KLauncher: Fatal error, can't create tempfile!");
+ ::exit(1);
+ }
+ mPoolSocketName = domainname.name();
+#ifdef __CYGWIN__
+ domainname.close();
+ domainname.unlink();
+#endif
+ mPoolSocket = new KServerSocket(QFile::encodeName(mPoolSocketName));
+ connect(mPoolSocket, SIGNAL(accepted( KSocket *)),
+ SLOT(acceptSlave(KSocket *)));
+
+ connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout()));
+
+ kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read);
+ connect(kdeinitNotifier, SIGNAL( activated( int )),
+ this, SLOT( slotKDEInitData( int )));
+ kdeinitNotifier->setEnabled( true );
+ lastRequest = 0;
+ bProcessingQueue = false;
+
+ mSlaveDebug = getenv("KDE_SLAVE_DEBUG_WAIT");
+ if (!mSlaveDebug.isEmpty())
+ {
+ qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", mSlaveDebug.data());
+ }
+ mSlaveValgrind = getenv("KDE_SLAVE_VALGRIND");
+ if (!mSlaveValgrind.isEmpty())
+ {
+ mSlaveValgrindSkin = getenv("KDE_SLAVE_VALGRIND_SKIN");
+ qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", mSlaveValgrind.data());
+ }
+ klauncher_header request_header;
+ request_header.cmd = LAUNCHER_OK;
+ request_header.arg_length = 0;
+ write(kdeinitSocket, &request_header, sizeof(request_header));
+}
+
+KLauncher::~KLauncher()
+{
+ close();
+}
+
+void KLauncher::close()
+{
+ if (!mPoolSocketName.isEmpty())
+ {
+ QCString filename = QFile::encodeName(mPoolSocketName);
+ unlink(filename.data());
+ }
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if( mCached_dpy != NULL )
+ XCloseDisplay( mCached_dpy );
+#endif
+}
+
+void
+KLauncher::destruct(int exit_code)
+{
+ if (kapp) ((KLauncher*)kapp)->close();
+ // We don't delete kapp here, that's intentional.
+ ::exit(exit_code);
+}
+
+bool
+KLauncher::process(const QCString &fun, const QByteArray &data,
+ QCString &replyType, QByteArray &replyData)
+{
+ if ((fun == "exec_blind(QCString,QValueList<QCString>)")
+ || (fun == "exec_blind(QCString,QValueList<QCString>,QValueList<QCString>,QCString)"))
+ {
+ QDataStream stream(data, IO_ReadOnly);
+ replyType = "void";
+ QCString name;
+ QValueList<QCString> arg_list;
+ QCString startup_id = "0";
+ QValueList<QCString> envs;
+ stream >> name >> arg_list;
+ if( fun == "exec_blind(QCString,QValueList<QCString>,QValueList<QCString>,QCString)" )
+ stream >> envs >> startup_id;
+ kdDebug(7016) << "KLauncher: Got exec_blind('" << name << "', ...)" << endl;
+ exec_blind( name, arg_list, envs, startup_id);
+ return true;
+ }
+ if ((fun == "start_service_by_name(QString,QStringList)") ||
+ (fun == "start_service_by_desktop_path(QString,QStringList)")||
+ (fun == "start_service_by_desktop_name(QString,QStringList)")||
+ (fun == "kdeinit_exec(QString,QStringList)") ||
+ (fun == "kdeinit_exec_wait(QString,QStringList)") ||
+ (fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString)") ||
+ (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString)")||
+ (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString)") ||
+ (fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString,bool)") ||
+ (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString,bool)")||
+ (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString,bool)") ||
+ (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>)") ||
+ (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>)") ||
+ (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>,QCString)") ||
+ (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>,QCString)"))
+ {
+ QDataStream stream(data, IO_ReadOnly);
+ bool bNoWait = false;
+ QString serviceName;
+ QStringList urls;
+ QValueList<QCString> envs;
+ QCString startup_id = "";
+ DCOPresult.result = -1;
+ DCOPresult.dcopName = 0;
+ DCOPresult.error = QString::null;
+ DCOPresult.pid = 0;
+ stream >> serviceName >> urls;
+ if ((fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString,bool)") ||
+ (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString,bool)")||
+ (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString,bool)"))
+ stream >> envs >> startup_id >> bNoWait;
+ else if ((fun == "start_service_by_name(QString,QStringList,QValueList<QCString>,QCString)") ||
+ (fun == "start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString)")||
+ (fun == "start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString)"))
+ stream >> envs >> startup_id;
+ else if ((fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>)") ||
+ (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>)"))
+ stream >> envs;
+ else if ((fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>,QCString)") ||
+ (fun == "kdeinit_exec_wait(QString,QStringList,QValueList<QCString>,QCString)"))
+ stream >> envs >> startup_id;
+ bool finished;
+ if (strncmp(fun, "start_service_by_name(", 22) == 0)
+ {
+ kdDebug(7016) << "KLauncher: Got start_service_by_name('" << serviceName << "', ...)" << endl;
+ finished = start_service_by_name(serviceName, urls, envs, startup_id, bNoWait);
+ }
+ else if (strncmp(fun, "start_service_by_desktop_path(", 30) == 0)
+ {
+ kdDebug(7016) << "KLauncher: Got start_service_by_desktop_path('" << serviceName << "', ...)" << endl;
+ finished = start_service_by_desktop_path(serviceName, urls, envs, startup_id, bNoWait);
+ }
+ else if (strncmp(fun, "start_service_by_desktop_name(", 30) == 0)
+ {
+ kdDebug(7016) << "KLauncher: Got start_service_by_desktop_name('" << serviceName << "', ...)" << endl;
+ finished = start_service_by_desktop_name(serviceName, urls, envs, startup_id, bNoWait );
+ }
+ else if ((fun == "kdeinit_exec(QString,QStringList)")
+ || (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>)")
+ || (fun == "kdeinit_exec(QString,QStringList,QValueList<QCString>,QCString)"))
+ {
+ kdDebug(7016) << "KLauncher: Got kdeinit_exec('" << serviceName << "', ...)" << endl;
+ finished = kdeinit_exec(serviceName, urls, envs, startup_id, false);
+ }
+ else
+ {
+ kdDebug(7016) << "KLauncher: Got kdeinit_exec_wait('" << serviceName << "', ...)" << endl;
+ finished = kdeinit_exec(serviceName, urls, envs, startup_id, true);
+ }
+ if (!finished)
+ {
+ replyType = "serviceResult";
+ QDataStream stream2(replyData, IO_WriteOnly);
+ stream2 << DCOPresult.result << DCOPresult.dcopName << DCOPresult.error << DCOPresult.pid;
+ }
+ return true;
+ }
+ else if (fun == "requestSlave(QString,QString,QString)")
+ {
+ QDataStream stream(data, IO_ReadOnly);
+ QString protocol;
+ QString host;
+ QString app_socket;
+ stream >> protocol >> host >> app_socket;
+ replyType = "QString";
+ QString error;
+ pid_t pid = requestSlave(protocol, host, app_socket, error);
+ QDataStream stream2(replyData, IO_WriteOnly);
+ stream2 << pid << error;
+ return true;
+ }
+ else if (fun == "requestHoldSlave(KURL,QString)")
+ {
+ QDataStream stream(data, IO_ReadOnly);
+ KURL url;
+ QString app_socket;
+ stream >> url >> app_socket;
+ replyType = "pid_t";
+ pid_t pid = requestHoldSlave(url, app_socket);
+ QDataStream stream2(replyData, IO_WriteOnly);
+ stream2 << pid;
+ return true;
+ }
+ else if (fun == "waitForSlave(pid_t)")
+ {
+ QDataStream stream(data, IO_ReadOnly);
+ pid_t pid;
+ stream >> pid;
+ waitForSlave(pid);
+ replyType = "void";
+ return true;
+
+ }
+ else if (fun == "setLaunchEnv(QCString,QCString)")
+ {
+ QDataStream stream(data, IO_ReadOnly);
+ QCString name;
+ QCString value;
+ stream >> name >> value;
+ setLaunchEnv(name, value);
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "reparseConfiguration()")
+ {
+ KGlobal::config()->reparseConfiguration();
+ kdDebug(7016) << "KLauncher::process : reparseConfiguration" << endl;
+ KProtocolManager::reparseConfiguration();
+ IdleSlave *slave;
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ slave->reparseConfiguration();
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "terminateKDE()")
+ {
+ ::signal( SIGHUP, SIG_IGN);
+ ::signal( SIGTERM, SIG_IGN);
+ kdDebug() << "KLauncher::process ---> terminateKDE" << endl;
+ klauncher_header request_header;
+ request_header.cmd = LAUNCHER_TERMINATE_KDE;
+ request_header.arg_length = 0;
+ write(kdeinitSocket, &request_header, sizeof(request_header));
+ destruct(0);
+ }
+ else if (fun == "autoStart()")
+ {
+ kdDebug() << "KLauncher::process ---> autoStart" << endl;
+ autoStart(1);
+ replyType = "void";
+ return true;
+ }
+ else if (fun == "autoStart(int)")
+ {
+ kdDebug() << "KLauncher::process ---> autoStart(int)" << endl;
+ QDataStream stream(data, IO_ReadOnly);
+ int phase;
+ stream >> phase;
+ autoStart(phase);
+ replyType = "void";
+ return true;
+ }
+
+ if (DCOPObject::process(fun, data, replyType, replyData))
+ {
+ return true;
+ }
+ kdWarning(7016) << "Got unknown DCOP function: " << fun << endl;
+ return false;
+}
+
+QCStringList
+KLauncher::interfaces()
+{
+ QCStringList ifaces = DCOPObject::interfaces();
+ ifaces += "KLauncher";
+ return ifaces;
+}
+
+QCStringList
+KLauncher::functions()
+{
+ QCStringList funcs = DCOPObject::functions();
+ funcs << "void exec_blind(QCString,QValueList<QCString>)";
+ funcs << "void exec_blind(QCString,QValueList<QCString>,QValueList<QCString>,QCString)";
+ funcs << "serviceResult start_service_by_name(QString,QStringList)";
+ funcs << "serviceResult start_service_by_desktop_path(QString,QStringList)";
+ funcs << "serviceResult start_service_by_desktop_name(QString,QStringList)";
+ funcs << "serviceResult kdeinit_exec(QString,QStringList)";
+ funcs << "serviceResult kdeinit_exec_wait(QString,QStringList)";
+ funcs << "serviceResult start_service_by_name(QString,QStringList,QValueList<QCString>,QCString)";
+ funcs << "serviceResult start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString)";
+ funcs << "serviceResult start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString)";
+ funcs << "serviceResult start_service_by_name(QString,QStringList,QValueList<QCString>,QCString,bool)";
+ funcs << "serviceResult start_service_by_desktop_path(QString,QStringList,QValueList<QCString>,QCString,bool)";
+ funcs << "serviceResult start_service_by_desktop_name(QString,QStringList,QValueList<QCString>,QCString,bool)";
+ funcs << "serviceResult kdeinit_exec(QString,QStringList,QValueList<QCString>)";
+ funcs << "serviceResult kdeinit_exec_wait(QString,QStringList,QValueList<QCString>)";
+ funcs << "QString requestSlave(QString,QString,QString)";
+ funcs << "pid_t requestHoldSlave(KURL,QString)";
+ funcs << "void waitForSlave(pid_t)";
+ funcs << "void setLaunchEnv(QCString,QCString)";
+ funcs << "void reparseConfiguration()";
+// funcs << "void terminateKDE()";
+ funcs << "void autoStart()";
+ funcs << "void autoStart(int)";
+ return funcs;
+}
+
+void KLauncher::setLaunchEnv(const QCString &name, const QCString &_value)
+{
+ QCString value(_value);
+ if (value.isNull())
+ value = "";
+ klauncher_header request_header;
+ QByteArray requestData(name.length()+value.length()+2);
+ memcpy(requestData.data(), name.data(), name.length()+1);
+ memcpy(requestData.data()+name.length()+1, value.data(), value.length()+1);
+ request_header.cmd = LAUNCHER_SETENV;
+ request_header.arg_length = requestData.size();
+ write(kdeinitSocket, &request_header, sizeof(request_header));
+ write(kdeinitSocket, requestData.data(), request_header.arg_length);
+}
+
+/*
+ * Read 'len' bytes from 'sock' into buffer.
+ * returns -1 on failure, 0 on no data.
+ */
+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))
+ return -1;
+ }
+ return 0;
+}
+
+
+void
+KLauncher::slotKDEInitData(int)
+{
+ klauncher_header request_header;
+ QByteArray requestData;
+ if( dontBlockReading )
+ {
+ // in case we get a request to start an application and data arrive
+ // to kdeinitSocket at the same time, requestStart() will already
+ // call slotKDEInitData(), so we must check there's still something
+ // to read, otherwise this would block
+ fd_set in;
+ timeval tm = { 0, 0 };
+ FD_ZERO ( &in );
+ FD_SET( kdeinitSocket, &in );
+ select( kdeinitSocket + 1, &in, 0, 0, &tm );
+ if( !FD_ISSET( kdeinitSocket, &in ))
+ return;
+ }
+ dontBlockReading = false;
+ int result = read_socket(kdeinitSocket, (char *) &request_header,
+ sizeof( request_header));
+ if (result == -1)
+ {
+ kdDebug() << "Exiting on read_socket errno: " << errno << endl;
+ ::signal( SIGHUP, SIG_IGN);
+ ::signal( SIGTERM, SIG_IGN);
+ destruct(255); // Exit!
+ }
+ requestData.resize(request_header.arg_length);
+ result = read_socket(kdeinitSocket, (char *) requestData.data(),
+ request_header.arg_length);
+
+ if (request_header.cmd == LAUNCHER_DIED)
+ {
+ long *request_data;
+ request_data = (long *) requestData.data();
+ processDied(request_data[0], request_data[1]);
+ return;
+ }
+ if (lastRequest && (request_header.cmd == LAUNCHER_OK))
+ {
+ long *request_data;
+ request_data = (long *) requestData.data();
+ lastRequest->pid = (pid_t) (*request_data);
+ kdDebug(7016) << lastRequest->name << " (pid " << lastRequest->pid <<
+ ") up and running." << endl;
+ switch(lastRequest->dcop_service_type)
+ {
+ case KService::DCOP_None:
+ {
+ lastRequest->status = KLaunchRequest::Running;
+ break;
+ }
+
+ case KService::DCOP_Unique:
+ {
+ lastRequest->status = KLaunchRequest::Launching;
+ break;
+ }
+
+ case KService::DCOP_Wait:
+ {
+ lastRequest->status = KLaunchRequest::Launching;
+ break;
+ }
+
+ case KService::DCOP_Multi:
+ {
+ lastRequest->status = KLaunchRequest::Launching;
+ break;
+ }
+ }
+ lastRequest = 0;
+ return;
+ }
+ if (lastRequest && (request_header.cmd == LAUNCHER_ERROR))
+ {
+ lastRequest->status = KLaunchRequest::Error;
+ if (!requestData.isEmpty())
+ lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data());
+ lastRequest = 0;
+ return;
+ }
+
+ kdWarning(7016) << "Unexpected command from KDEInit (" << (unsigned int) request_header.cmd
+ << ")" << endl;
+}
+
+void
+KLauncher::processDied(pid_t pid, long /* exitStatus */)
+{
+ KLaunchRequest *request = requestList.first();
+ for(; request; request = requestList.next())
+ {
+ if (request->pid == pid)
+ {
+ if (request->dcop_service_type == KService::DCOP_Wait)
+ request->status = KLaunchRequest::Done;
+ else if ((request->dcop_service_type == KService::DCOP_Unique) &&
+ (dcopClient()->isApplicationRegistered(request->dcop_name)))
+ request->status = KLaunchRequest::Running;
+ else
+ request->status = KLaunchRequest::Error;
+ requestDone(request);
+ return;
+ }
+ }
+}
+
+void
+KLauncher::slotAppRegistered(const QCString &appId)
+{
+ const char *cAppId = appId.data();
+ if (!cAppId) return;
+
+ KLaunchRequest *request = requestList.first();
+ KLaunchRequest *nextRequest;
+ for(; request; request = nextRequest)
+ {
+ nextRequest = requestList.next();
+ if (request->status != KLaunchRequest::Launching)
+ continue;
+
+ // For unique services check the requested service name first
+ if ((request->dcop_service_type == KService::DCOP_Unique) &&
+ ((appId == request->dcop_name) ||
+ dcopClient()->isApplicationRegistered(request->dcop_name)))
+ {
+ request->status = KLaunchRequest::Running;
+ requestDone(request);
+ continue;
+ }
+
+ const char *rAppId = request->dcop_name.data();
+ if (!rAppId) continue;
+
+ int l = strlen(rAppId);
+ if ((strncmp(rAppId, cAppId, l) == 0) &&
+ ((cAppId[l] == '\0') || (cAppId[l] == '-')))
+ {
+ request->dcop_name = appId;
+ request->status = KLaunchRequest::Running;
+ requestDone(request);
+ continue;
+ }
+ }
+}
+
+void
+KLauncher::autoStart(int phase)
+{
+ if( mAutoStart.phase() >= phase )
+ return;
+ mAutoStart.setPhase(phase);
+ if( newStartup )
+ {
+ if (phase == 0)
+ mAutoStart.loadAutoStartList();
+ }
+ else
+ {
+ if (phase == 1)
+ mAutoStart.loadAutoStartList();
+ }
+ mAutoTimer.start(0, true);
+}
+
+void
+KLauncher::slotAutoStart()
+{
+ KService::Ptr s;
+ do
+ {
+ QString service = mAutoStart.startService();
+ if (service.isEmpty())
+ {
+ // Done
+ if( !mAutoStart.phaseDone())
+ {
+ mAutoStart.setPhaseDone();
+ // Emit signal
+ if( newStartup )
+ {
+ QCString autoStartSignal;
+ autoStartSignal.sprintf( "autoStart%dDone()", mAutoStart.phase());
+ emitDCOPSignal(autoStartSignal, QByteArray());
+ }
+ else
+ {
+ QCString autoStartSignal( "autoStartDone()" );
+ int phase = mAutoStart.phase();
+ if ( phase > 1 )
+ autoStartSignal.sprintf( "autoStart%dDone()", phase );
+ emitDCOPSignal(autoStartSignal, QByteArray());
+ }
+ }
+ return;
+ }
+ s = new KService(service);
+ }
+ while (!start_service(s, QStringList(), QValueList<QCString>(), "0", false, true));
+ // Loop till we find a service that we can start.
+}
+
+void
+KLauncher::requestDone(KLaunchRequest *request)
+{
+ if ((request->status == KLaunchRequest::Running) ||
+ (request->status == KLaunchRequest::Done))
+ {
+ DCOPresult.result = 0;
+ DCOPresult.dcopName = request->dcop_name;
+ DCOPresult.error = QString::null;
+ DCOPresult.pid = request->pid;
+ }
+ else
+ {
+ DCOPresult.result = 1;
+ DCOPresult.dcopName = "";
+ DCOPresult.error = i18n("KDEInit could not launch '%1'.").arg(request->name);
+ if (!request->errorMsg.isEmpty())
+ DCOPresult.error += ":\n" + request->errorMsg;
+ DCOPresult.pid = 0;
+
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11
+ if (!request->startup_dpy.isEmpty())
+ {
+ Display* dpy = NULL;
+ if( (mCached_dpy != NULL) &&
+ (request->startup_dpy == XDisplayString( mCached_dpy )))
+ dpy = mCached_dpy;
+ if( dpy == NULL )
+ dpy = XOpenDisplay( request->startup_dpy );
+ if( dpy )
+ {
+ KStartupInfoId id;
+ id.initId( request->startup_id );
+ KStartupInfo::sendFinishX( dpy, id );
+ if( mCached_dpy != dpy && mCached_dpy != NULL )
+ XCloseDisplay( mCached_dpy );
+ mCached_dpy = dpy;
+ }
+ }
+#endif
+ }
+
+ if (request->autoStart)
+ {
+ mAutoTimer.start(0, true);
+ }
+
+ if (request->transaction)
+ {
+ QByteArray replyData;
+ QCString replyType;
+ replyType = "serviceResult";
+ QDataStream stream2(replyData, IO_WriteOnly);
+ stream2 << DCOPresult.result << DCOPresult.dcopName << DCOPresult.error << DCOPresult.pid;
+ dcopClient()->endTransaction( request->transaction,
+ replyType, replyData);
+ }
+ requestList.removeRef( request );
+}
+
+void
+KLauncher::requestStart(KLaunchRequest *request)
+{
+ requestList.append( request );
+ // Send request to kdeinit.
+ klauncher_header request_header;
+ QByteArray requestData;
+ int length = 0;
+ length += sizeof(long); // Nr of. Args
+ length += request->name.length() + 1; // Cmd
+ for(QValueList<QCString>::Iterator it = request->arg_list.begin();
+ it != request->arg_list.end();
+ it++)
+ {
+ length += (*it).length() + 1; // Args...
+ }
+ length += sizeof(long); // Nr of. envs
+ for(QValueList<QCString>::ConstIterator it = request->envs.begin();
+ it != request->envs.end();
+ it++)
+ {
+ length += (*it).length() + 1; // Envs...
+ }
+ length += sizeof( long ); // avoid_loops
+#ifdef Q_WS_X11
+ bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0";
+ if( startup_notify )
+ length += request->startup_id.length() + 1;
+#endif
+ if (!request->cwd.isEmpty())
+ length += request->cwd.length() + 1;
+
+ requestData.resize( length );
+
+ char *p = requestData.data();
+ long l = request->arg_list.count()+1;
+ memcpy(p, &l, sizeof(long));
+ p += sizeof(long);
+ strcpy(p, request->name.data());
+ p += strlen(p) + 1;
+ for(QValueList<QCString>::Iterator it = request->arg_list.begin();
+ it != request->arg_list.end();
+ it++)
+ {
+ strcpy(p, (*it).data());
+ p += strlen(p) + 1;
+ }
+ l = request->envs.count();
+ memcpy(p, &l, sizeof(long));
+ p += sizeof(long);
+ for(QValueList<QCString>::ConstIterator it = request->envs.begin();
+ it != request->envs.end();
+ it++)
+ {
+ strcpy(p, (*it).data());
+ p += strlen(p) + 1;
+ }
+ l = 0; // avoid_loops, always false here
+ memcpy(p, &l, sizeof(long));
+ p += sizeof(long);
+#ifdef Q_WS_X11
+ if( startup_notify )
+ {
+ strcpy(p, request->startup_id.data());
+ p += strlen( p ) + 1;
+ }
+#endif
+ if (!request->cwd.isEmpty())
+ {
+ strcpy(p, request->cwd.data());
+ p += strlen( p ) + 1;
+ }
+#ifdef Q_WS_X11
+ request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW;
+#else
+ request_header.cmd = LAUNCHER_EXEC_NEW;
+#endif
+ request_header.arg_length = length;
+ write(kdeinitSocket, &request_header, sizeof(request_header));
+ write(kdeinitSocket, requestData.data(), request_header.arg_length);
+
+ // Wait for pid to return.
+ lastRequest = request;
+ dontBlockReading = false;
+ do {
+ slotKDEInitData( kdeinitSocket );
+ }
+ while (lastRequest != 0);
+ dontBlockReading = true;
+}
+
+void
+KLauncher::exec_blind( const QCString &name, const QValueList<QCString> &arg_list,
+ const QValueList<QCString> &envs, const QCString& startup_id )
+{
+ KLaunchRequest *request = new KLaunchRequest;
+ request->autoStart = false;
+ request->name = name;
+ request->arg_list = arg_list;
+ request->dcop_name = 0;
+ request->dcop_service_type = KService::DCOP_None;
+ request->pid = 0;
+ request->status = KLaunchRequest::Launching;
+ request->transaction = 0; // No confirmation is send
+ request->envs = envs;
+ // Find service, if any - strip path if needed
+ KService::Ptr service = KService::serviceByDesktopName( name.mid( name.findRev( '/' ) + 1 ));
+ if (service != NULL)
+ send_service_startup_info( request, service,
+ startup_id, QValueList< QCString >());
+ else // no .desktop file, no startup info
+ cancel_service_startup_info( request, startup_id, envs );
+
+ requestStart(request);
+ // We don't care about this request any longer....
+ requestDone(request);
+}
+
+
+bool
+KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind)
+{
+ KService::Ptr service = 0;
+ // Find service
+ service = KService::serviceByName(serviceName);
+ if (!service)
+ {
+ DCOPresult.result = ENOENT;
+ DCOPresult.error = i18n("Could not find service '%1'.").arg(serviceName);
+ cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
+ return false;
+ }
+ return start_service(service, urls, envs, startup_id, blind);
+}
+
+bool
+KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind)
+{
+ KService::Ptr service = 0;
+ // Find service
+ if (serviceName[0] == '/')
+ {
+ // Full path
+ service = new KService(serviceName);
+ }
+ else
+ {
+ service = KService::serviceByDesktopPath(serviceName);
+ }
+ if (!service)
+ {
+ DCOPresult.result = ENOENT;
+ DCOPresult.error = i18n("Could not find service '%1'.").arg(serviceName);
+ cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
+ return false;
+ }
+ return start_service(service, urls, envs, startup_id, blind);
+}
+
+bool
+KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind)
+{
+ KService::Ptr service = 0;
+ // Find service
+ service = KService::serviceByDesktopName(serviceName);
+ if (!service)
+ {
+ DCOPresult.result = ENOENT;
+ DCOPresult.error = i18n("Could not find service '%1'.").arg(serviceName);
+ cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
+ return false;
+ }
+ return start_service(service, urls, envs, startup_id, blind);
+}
+
+bool
+KLauncher::start_service(KService::Ptr service, const QStringList &_urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind, bool autoStart)
+{
+ QStringList urls = _urls;
+ if (!service->isValid())
+ {
+ DCOPresult.result = ENOEXEC;
+ DCOPresult.error = i18n("Service '%1' is malformatted.").arg(service->desktopEntryPath());
+ cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any
+ return false;
+ }
+ KLaunchRequest *request = new KLaunchRequest;
+ request->autoStart = autoStart;
+
+ if ((urls.count() > 1) && !service->allowMultipleFiles())
+ {
+ // We need to launch the application N times. That sucks.
+ // We ignore the result for application 2 to N.
+ // For the first file we launch the application in the
+ // usual way. The reported result is based on this
+ // application.
+ QStringList::ConstIterator it = urls.begin();
+ for(++it;
+ it != urls.end();
+ ++it)
+ {
+ QStringList singleUrl;
+ singleUrl.append(*it);
+ QCString startup_id2 = startup_id;
+ if( !startup_id2.isEmpty() && startup_id2 != "0" )
+ startup_id2 = "0"; // can't use the same startup_id several times
+ start_service( service, singleUrl, envs, startup_id2, true);
+ }
+ QString firstURL = *(urls.begin());
+ urls.clear();
+ urls.append(firstURL);
+ }
+ createArgs(request, service, urls);
+
+ // We must have one argument at least!
+ if (!request->arg_list.count())
+ {
+ DCOPresult.result = ENOEXEC;
+ DCOPresult.error = i18n("Service '%1' is malformatted.").arg(service->desktopEntryPath());
+ delete request;
+ cancel_service_startup_info( NULL, startup_id, envs );
+ return false;
+ }
+
+ request->name = request->arg_list.first();
+ request->arg_list.remove(request->arg_list.begin());
+
+ request->dcop_service_type = service->DCOPServiceType();
+
+ if ((request->dcop_service_type == KService::DCOP_Unique) ||
+ (request->dcop_service_type == KService::DCOP_Multi))
+ {
+ QVariant v = service->property("X-DCOP-ServiceName");
+ if (v.isValid())
+ request->dcop_name = v.toString().utf8();
+ if (request->dcop_name.isEmpty())
+ {
+ request->dcop_name = QFile::encodeName(KRun::binaryName(service->exec(), true));
+ }
+ }
+
+ request->pid = 0;
+ request->transaction = 0;
+ request->envs = envs;
+ send_service_startup_info( request, service, startup_id, envs );
+
+ // Request will be handled later.
+ if (!blind && !autoStart)
+ {
+ request->transaction = dcopClient()->beginTransaction();
+ }
+ queueRequest(request);
+ return true;
+}
+
+void
+KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QCString& startup_id,
+ const QValueList<QCString> &envs )
+{
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11 // KStartup* isn't implemented for Qt/Embedded yet
+ request->startup_id = "0";
+ if( startup_id == "0" )
+ return;
+ bool silent;
+ QCString wmclass;
+ if( !KRun::checkStartupNotify( QString::null, service, &silent, &wmclass ))
+ return;
+ KStartupInfoId id;
+ id.initId( startup_id );
+ const char* dpy_str = NULL;
+ for( QValueList<QCString>::ConstIterator it = envs.begin();
+ it != envs.end();
+ ++it )
+ if( strncmp( *it, "DISPLAY=", 8 ) == 0 )
+ dpy_str = static_cast< const char* >( *it ) + 8;
+ Display* dpy = NULL;
+ if( dpy_str != NULL && mCached_dpy != NULL
+ && qstrcmp( dpy_str, XDisplayString( mCached_dpy )) == 0 )
+ dpy = mCached_dpy;
+ if( dpy == NULL )
+ dpy = XOpenDisplay( dpy_str );
+ request->startup_id = id.id();
+ if( dpy == NULL )
+ {
+ cancel_service_startup_info( request, startup_id, envs );
+ return;
+ }
+
+ request->startup_dpy = dpy_str;
+
+ KStartupInfoData data;
+ data.setName( service->name());
+ data.setIcon( service->icon());
+ data.setDescription( i18n( "Launching %1" ).arg( service->name()));
+ if( !wmclass.isEmpty())
+ data.setWMClass( wmclass );
+ if( silent )
+ data.setSilent( KStartupInfoData::Yes );
+ // the rest will be sent by kdeinit
+ KStartupInfo::sendStartupX( dpy, id, data );
+ if( mCached_dpy != dpy && mCached_dpy != NULL )
+ XCloseDisplay( mCached_dpy );
+ mCached_dpy = dpy;
+ return;
+#else
+ return;
+#endif
+}
+
+void
+KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QCString& startup_id,
+ const QValueList<QCString> &envs )
+{
+#if defined Q_WS_X11 && ! defined K_WS_QTONLY
+//#ifdef Q_WS_X11 // KStartup* isn't implemented for Qt/Embedded yet
+ if( request != NULL )
+ request->startup_id = "0";
+ if( !startup_id.isEmpty() && startup_id != "0" )
+ {
+ const char* dpy_str = NULL;
+ for( QValueList<QCString>::ConstIterator it = envs.begin();
+ it != envs.end();
+ ++it )
+ if( strncmp( *it, "DISPLAY=", 8 ) == 0 )
+ dpy_str = static_cast< const char* >( *it ) + 8;
+ Display* dpy = NULL;
+ if( dpy_str != NULL && mCached_dpy != NULL
+ && qstrcmp( dpy_str, XDisplayString( mCached_dpy )) == 0 )
+ dpy = mCached_dpy;
+ if( dpy == NULL )
+ dpy = XOpenDisplay( dpy_str );
+ if( dpy == NULL )
+ return;
+ KStartupInfoId id;
+ id.initId( startup_id );
+ KStartupInfo::sendFinishX( dpy, id );
+ if( mCached_dpy != dpy && mCached_dpy != NULL )
+ XCloseDisplay( mCached_dpy );
+ mCached_dpy = dpy;
+ }
+#endif
+}
+
+bool
+KLauncher::kdeinit_exec(const QString &app, const QStringList &args,
+ const QValueList<QCString> &envs, QCString startup_id, bool wait)
+{
+ KLaunchRequest *request = new KLaunchRequest;
+ request->autoStart = false;
+
+ for(QStringList::ConstIterator it = args.begin();
+ it != args.end();
+ it++)
+ {
+ QString arg = *it;
+ request->arg_list.append(arg.local8Bit());
+ }
+
+ request->name = app.local8Bit();
+
+ if (wait)
+ request->dcop_service_type = KService::DCOP_Wait;
+ else
+ request->dcop_service_type = KService::DCOP_None;
+ request->dcop_name = 0;
+ request->pid = 0;
+#ifdef Q_WS_X11
+ request->startup_id = startup_id;
+#endif
+ request->envs = envs;
+ if( app != "kbuildsycoca" ) // avoid stupid loop
+ {
+ // Find service, if any - strip path if needed
+ KService::Ptr service = KService::serviceByDesktopName( app.mid( app.findRev( '/' ) + 1 ));
+ if (service != NULL)
+ send_service_startup_info( request, service,
+ startup_id, QValueList< QCString >());
+ else // no .desktop file, no startup info
+ cancel_service_startup_info( request, startup_id, envs );
+ }
+ request->transaction = dcopClient()->beginTransaction();
+ queueRequest(request);
+ return true;
+}
+
+void
+KLauncher::queueRequest(KLaunchRequest *request)
+{
+ requestQueue.append( request );
+ if (!bProcessingQueue)
+ {
+ bProcessingQueue = true;
+ QTimer::singleShot(0, this, SLOT( slotDequeue() ));
+ }
+}
+
+void
+KLauncher::slotDequeue()
+{
+ do {
+ KLaunchRequest *request = requestQueue.take(0);
+ // process request
+ request->status = KLaunchRequest::Launching;
+ requestStart(request);
+ if (request->status != KLaunchRequest::Launching)
+ {
+ // Request handled.
+ requestDone( request );
+ continue;
+ }
+ } while(requestQueue.count());
+ bProcessingQueue = false;
+}
+
+void
+KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service ,
+ const QStringList &urls)
+{
+ QStringList params = KRun::processDesktopExec(*service, urls, false);
+
+ for(QStringList::ConstIterator it = params.begin();
+ it != params.end(); ++it)
+ {
+ request->arg_list.append((*it).local8Bit());
+ }
+ request->cwd = QFile::encodeName(service->path());
+}
+
+///// IO-Slave functions
+
+pid_t
+KLauncher::requestHoldSlave(const KURL &url, const QString &app_socket)
+{
+ IdleSlave *slave;
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ {
+ if (slave->onHold(url))
+ break;
+ }
+ if (slave)
+ {
+ mSlaveList.removeRef(slave);
+ slave->connect(app_socket);
+ return slave->pid();
+ }
+ return 0;
+}
+
+
+pid_t
+KLauncher::requestSlave(const QString &protocol,
+ const QString &host,
+ const QString &app_socket,
+ QString &error)
+{
+ IdleSlave *slave;
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ {
+ if (slave->match(protocol, host, true))
+ break;
+ }
+ if (!slave)
+ {
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ {
+ if (slave->match(protocol, host, false))
+ break;
+ }
+ }
+ if (!slave)
+ {
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ {
+ if (slave->match(protocol, QString::null, false))
+ break;
+ }
+ }
+ if (slave)
+ {
+ mSlaveList.removeRef(slave);
+ slave->connect(app_socket);
+ return slave->pid();
+ }
+
+ QString _name = KProtocolInfo::exec(protocol);
+ if (_name.isEmpty())
+ {
+ error = i18n("Unknown protocol '%1'.\n").arg(protocol);
+ return 0;
+ }
+
+ QCString name = _name.latin1(); // ex: "kio_ftp"
+ QCString arg1 = protocol.latin1();
+ QCString arg2 = QFile::encodeName(mPoolSocketName);
+ QCString arg3 = QFile::encodeName(app_socket);
+ QValueList<QCString> arg_list;
+ arg_list.append(arg1);
+ arg_list.append(arg2);
+ arg_list.append(arg3);
+
+// kdDebug(7016) << "KLauncher: launching new slave " << _name << " with protocol=" << protocol << endl;
+ if (mSlaveDebug == arg1)
+ {
+ klauncher_header request_header;
+ request_header.cmd = LAUNCHER_DEBUG_WAIT;
+ request_header.arg_length = 0;
+ write(kdeinitSocket, &request_header, sizeof(request_header));
+ }
+ if (mSlaveValgrind == arg1)
+ {
+ arg_list.prepend(QFile::encodeName(KLibLoader::findLibrary(name)));
+ arg_list.prepend(QFile::encodeName(locate("exe", "kioslave")));
+ name = "valgrind";
+ if (!mSlaveValgrindSkin.isEmpty()) {
+ arg_list.prepend(QCString("--tool=") + mSlaveValgrindSkin);
+ } else
+ arg_list.prepend("--tool=memcheck");
+ }
+
+ KLaunchRequest *request = new KLaunchRequest;
+ request->autoStart = false;
+ request->name = name;
+ request->arg_list = arg_list;
+ request->dcop_name = 0;
+ request->dcop_service_type = KService::DCOP_None;
+ request->pid = 0;
+#ifdef Q_WS_X11
+ request->startup_id = "0";
+#endif
+ request->status = KLaunchRequest::Launching;
+ request->transaction = 0; // No confirmation is send
+ requestStart(request);
+ pid_t pid = request->pid;
+
+// kdDebug(7016) << "Slave launched, pid = " << pid << endl;
+
+ // We don't care about this request any longer....
+ requestDone(request);
+ if (!pid)
+ {
+ error = i18n("Error loading '%1'.\n").arg(name);
+ }
+ return pid;
+}
+
+void
+KLauncher::waitForSlave(pid_t pid)
+{
+ IdleSlave *slave;
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ {
+ if (slave->pid() == pid)
+ return; // Already here.
+ }
+ SlaveWaitRequest *waitRequest = new SlaveWaitRequest;
+ waitRequest->transaction = dcopClient()->beginTransaction();
+ waitRequest->pid = pid;
+ mSlaveWaitRequest.append(waitRequest);
+}
+
+void
+KLauncher::acceptSlave(KSocket *slaveSocket)
+{
+ IdleSlave *slave = new IdleSlave(slaveSocket);
+ // Send it a SLAVE_STATUS command.
+ mSlaveList.append(slave);
+ connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone()));
+ connect(slave, SIGNAL(statusUpdate(IdleSlave *)),
+ this, SLOT(slotSlaveStatus(IdleSlave *)));
+ if (!mTimer.isActive())
+ {
+ mTimer.start(1000*10);
+ }
+}
+
+void
+KLauncher::slotSlaveStatus(IdleSlave *slave)
+{
+ SlaveWaitRequest *waitRequest = mSlaveWaitRequest.first();
+ while(waitRequest)
+ {
+ if (waitRequest->pid == slave->pid())
+ {
+ QByteArray replyData;
+ QCString replyType;
+ replyType = "void";
+ dcopClient()->endTransaction( waitRequest->transaction, replyType, replyData);
+ mSlaveWaitRequest.removeRef(waitRequest);
+ waitRequest = mSlaveWaitRequest.current();
+ }
+ else
+ {
+ waitRequest = mSlaveWaitRequest.next();
+ }
+ }
+}
+
+void
+KLauncher::slotSlaveGone()
+{
+ IdleSlave *slave = (IdleSlave *) sender();
+ mSlaveList.removeRef(slave);
+ if ((mSlaveList.count() == 0) && (mTimer.isActive()))
+ {
+ mTimer.stop();
+ }
+}
+
+void
+KLauncher::idleTimeout()
+{
+ bool keepOneFileSlave=true;
+ time_t now = time(0);
+ IdleSlave *slave;
+ for(slave = mSlaveList.first(); slave; slave = mSlaveList.next())
+ {
+ if ((slave->protocol()=="file") && (keepOneFileSlave))
+ keepOneFileSlave=false;
+ else if (slave->age(now) > SLAVE_MAX_IDLE)
+ {
+ // killing idle slave
+ delete slave;
+ }
+ }
+}
+
+#include "klauncher.moc"
diff --git a/kinit/klauncher.h b/kinit/klauncher.h
new file mode 100644
index 000000000..a9ed80f3b
--- /dev/null
+++ b/kinit/klauncher.h
@@ -0,0 +1,200 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 Waldo Bastian <bastian@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.
+*/
+
+#ifndef _KLAUNCHER_H_
+#define _KLAUNCHER_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qsocketnotifier.h>
+#include <qptrlist.h>
+#include <qtimer.h>
+
+#include <dcopclient.h>
+#include <kio/connection.h>
+#include <ksock.h>
+#include <kurl.h>
+#include <kuniqueapplication.h>
+
+#include <kservice.h>
+
+#include "autostart.h"
+
+class IdleSlave : public QObject
+{
+ Q_OBJECT
+public:
+ IdleSlave(KSocket *socket);
+ bool match( const QString &protocol, const QString &host, bool connected);
+ void connect( const QString &app_socket);
+ pid_t pid() const { return mPid;}
+ int age(time_t now);
+ void reparseConfiguration();
+ bool onHold(const KURL &url);
+ QString protocol() const {return mProtocol;}
+
+signals:
+ void statusUpdate(IdleSlave *);
+
+protected slots:
+ void gotInput();
+
+protected:
+ KIO::Connection mConn;
+ QString mProtocol;
+ QString mHost;
+ bool mConnected;
+ pid_t mPid;
+ time_t mBirthDate;
+ bool mOnHold;
+ KURL mUrl;
+};
+
+class SlaveWaitRequest
+{
+public:
+ pid_t pid;
+ DCOPClientTransaction *transaction;
+};
+
+class KLaunchRequest
+{
+public:
+ QCString name;
+ QValueList<QCString> arg_list;
+ QCString dcop_name;
+ enum status_t { Init = 0, Launching, Running, Error, Done };
+ pid_t pid;
+ status_t status;
+ DCOPClientTransaction *transaction;
+ KService::DCOPServiceType_t dcop_service_type;
+ bool autoStart;
+ QString errorMsg;
+#ifdef Q_WS_X11
+ QCString startup_id; // "" is the default, "0" for none
+ QCString startup_dpy; // Display to send startup notification to.
+#endif
+ QValueList<QCString> envs; // env. variables to be app's environment
+ QCString cwd;
+};
+
+struct serviceResult
+{
+ int result; // 0 means success. > 0 means error (-1 means pending)
+ QCString dcopName; // Contains DCOP name on success
+ QString error; // Contains error description on failure.
+ pid_t pid;
+};
+
+class KLauncher : public KApplication, public DCOPObject
+{
+ Q_OBJECT
+
+public:
+ KLauncher(int _kdeinitSocket, bool new_startup);
+
+ ~KLauncher();
+
+ void close();
+ static void destruct(int exit_code); // exit!
+
+ // DCOP
+ virtual bool process(const QCString &fun, const QByteArray &data,
+ QCString &replyType, QByteArray &replyData);
+ virtual QCStringList functions();
+ virtual QCStringList interfaces();
+
+protected:
+ void processDied(pid_t pid, long exitStatus);
+
+ void requestStart(KLaunchRequest *request);
+ void requestDone(KLaunchRequest *request);
+
+ void setLaunchEnv(const QCString &name, const QCString &value);
+ void exec_blind(const QCString &name, const QValueList<QCString> &arg_list,
+ const QValueList<QCString> &envs, const QCString& startup_id = "" );
+ bool start_service(KService::Ptr service, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id = "",
+ bool blind = false, bool autoStart = false );
+ bool start_service_by_name(const QString &serviceName, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind);
+ bool start_service_by_desktop_path(const QString &serviceName, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind);
+ bool start_service_by_desktop_name(const QString &serviceName, const QStringList &urls,
+ const QValueList<QCString> &envs, const QCString& startup_id, bool blind);
+ bool kdeinit_exec(const QString &app, const QStringList &args,
+ const QValueList<QCString> &envs, QCString startup_id, bool wait);
+
+ void waitForSlave(pid_t pid);
+
+ void autoStart(int phase);
+
+ void createArgs( KLaunchRequest *request, const KService::Ptr service,
+ const QStringList &url);
+
+ pid_t requestHoldSlave(const KURL &url, const QString &app_socket);
+ pid_t requestSlave(const QString &protocol, const QString &host,
+ const QString &app_socket, QString &error);
+
+
+ void queueRequest(KLaunchRequest *);
+
+ void send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QCString& startup_id,
+ const QValueList<QCString> &envs );
+ void cancel_service_startup_info( KLaunchRequest *request, const QCString& startup_id,
+ const QValueList<QCString> &envs );
+
+public slots:
+ void slotAutoStart();
+ void slotDequeue();
+ void slotKDEInitData(int);
+ void slotAppRegistered(const QCString &appId);
+ void slotSlaveStatus(IdleSlave *);
+ void acceptSlave( KSocket *);
+ void slotSlaveGone();
+ void idleTimeout();
+
+protected:
+ QPtrList<KLaunchRequest> requestList; // Requests being handled
+ QPtrList<KLaunchRequest> requestQueue; // Requests waiting to being handled
+ int kdeinitSocket;
+ QSocketNotifier *kdeinitNotifier;
+ serviceResult DCOPresult;
+ KLaunchRequest *lastRequest;
+ QPtrList<SlaveWaitRequest> mSlaveWaitRequest;
+ QString mPoolSocketName;
+ KServerSocket *mPoolSocket;
+ QPtrList<IdleSlave> mSlaveList;
+ QTimer mTimer;
+ QTimer mAutoTimer;
+ bool bProcessingQueue;
+ AutoStart mAutoStart;
+ QCString mSlaveDebug;
+ QCString mSlaveValgrind;
+ QCString mSlaveValgrindSkin;
+ bool dontBlockReading;
+ bool newStartup;
+#ifdef Q_WS_X11
+ Display *mCached_dpy;
+#endif
+};
+#endif
diff --git a/kinit/klauncher_cmds.h b/kinit/klauncher_cmds.h
new file mode 100644
index 000000000..ff2993ad0
--- /dev/null
+++ b/kinit/klauncher_cmds.h
@@ -0,0 +1,185 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 Waldo Bastian <bastian@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.
+*/
+
+#ifndef _KLAUNCHER_CMDS_H_
+#define _KLAUNCHER_CMDS_H_
+
+typedef struct
+{
+ long cmd;
+ long arg_length;
+} klauncher_header;
+
+/* Launcher commands: */
+
+#define LAUNCHER_EXEC 1
+/*
+ * LAUNCHER_EXEC
+ *
+ * Start a new process. Try using LAUNCHER_EXEC_NEW instead.
+ * There will be no app startup notification.
+ *
+ * long argc: number of arguments
+ * char *args: arguments, argument 0 is the program to start.
+ */
+
+
+#define LAUNCHER_SETENV 2
+/*
+ * LAUNCHER_SETENV
+ *
+ * Change environment of future processes launched via kdeinit.
+ * DON'T use this if you want to change environment only for one
+ * application you're going to start.
+ *
+ * char *env_name;
+ * char *env_value;
+ */
+
+#define LAUNCHER_DIED 3
+/*
+ * LAUNCHER_DIED
+ *
+ * Notification A child of kdeinit died.
+ *
+ * long pid;
+ * long exit_code;
+ */
+
+#define LAUNCHER_OK 4
+/*
+ * LAUNCHER_OK
+ *
+ * Notification Last process launched ok.
+ *
+ * long pid;
+ */
+
+#define LAUNCHER_ERROR 5
+/*
+ * LAUNCHER_ERROR
+ *
+ * Notification Last process could not be launched.
+ *
+ * char *error msg (utf8)
+ */
+
+#define LAUNCHER_SHELL 6
+/*
+ * LAUNCHER_SHELL
+ *
+ * Start a new process and use given environment.
+ * Starts app-startup notification.
+ *
+ * long argc: number of arguments
+ * char *args: arguments, argument 0 is the program to start.
+ * char *cwd: Working directory.
+ * long envc: number of environment vars
+ * char *envs: environment strings.
+ * int avoid_loops : avoid using the first path in $PATH where
+ * this process binary is found in order to avoid
+ * infinite loop by binary->kdeinit_wrapper link in $PATH
+ * char* startup_id: app startup notification id, "0" for none,
+ * "" ( empty string ) is the default
+ */
+
+#define LAUNCHER_TERMINATE_KDE 7
+
+/*
+ * LAUNCHER_TERMINATE_KDEINIT
+ *
+ * Suicide is painless
+ */
+#define LAUNCHER_TERMINATE_KDEINIT 8
+
+#define LAUNCHER_DEBUG_WAIT 9
+/*
+ * LAUNCHER_DEBUG_WAIT
+ *
+ * Next process started will do a sleep(1000000)
+ * before calling main()/kdemain()
+ *
+ * (Used for debugging io-slaves)
+ */
+
+#define LAUNCHER_EXT_EXEC 10
+/*
+ * LAUNCHER_EXT_EXEC
+ *
+ * Start a new process. The given environment variables will
+ * be added to its environment before starting it.
+ * Starts app-startup notification.
+ *
+ * long argc: number of arguments
+ * char *args: arguments, argument 0 is the program to start.
+ * long envc: number of environment vars
+ * char *envs: environment strings.
+ * int avoid_loops : avoid using the first path in $PATH where
+ * this process binary is found in order to avoid
+ * infinite loop by binary->kdeinit_wrapper link in $PATH
+ * char* startup_id: app startup notification id, "0" for none,
+ * "" ( empty string ) is the default
+ *
+ */
+
+
+#define LAUNCHER_KWRAPPER 11
+/*
+ * LAUNCHER_KWRAPPER
+ *
+ * Start a new process, use given environment, pass signals and output.
+ * Starts app-startup notification.
+ *
+ * long argc: number of arguments
+ * char *args: arguments, argument 0 is the program to start.
+ * char *cwd: Working directory.
+ * long envc: number of environment vars
+ * char *envs: environment strings.
+ * char *tty: tty to redirect stdout/stderr to.
+ * int avoid_loops : avoid using the first path in $PATH where
+ * this process binary is found in order to avoid
+ * infinite loop by binary->kdeinit_wrapper link in $PATH
+ * char* startup_id: app startup notification id, "0" for none,
+ * "" ( empty string ) is the default
+ */
+
+#define LAUNCHER_EXEC_NEW 12
+/*
+ * LAUNCHER_EXEC_NEW
+ *
+ * Start a new process. An improved version of LAUNCHER_EXEC.
+ * The given environment variables will be added
+ * to its environment before starting it.
+ * There will be no app startup notification.
+ *
+ * long argc: number of arguments
+ * char *args: arguments, argument 0 is the program to start.
+ * long envc: number of environment vars
+ * char *envs: environment strings.
+ * int avoid_loops : avoid using the first path in $PATH where
+ * this process binary is found in order to avoid
+ * infinite loop by binary->kdeinit_wrapper link in $PATH
+ */
+
+#define LAUNCHER_FD 42
+/*
+ * File descriptor to use for communication with kdeinit.
+ */
+
+#endif
diff --git a/kinit/klauncher_main.cpp b/kinit/klauncher_main.cpp
new file mode 100644
index 000000000..fd5ea813e
--- /dev/null
+++ b/kinit/klauncher_main.cpp
@@ -0,0 +1,116 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 Waldo Bastian <bastian@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.
+*/
+
+#include "config.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "kapplication.h"
+#include "klauncher.h"
+#include "kcmdlineargs.h"
+#include "kcrash.h"
+#include "kdebug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <qcstring.h>
+#include <klocale.h>
+
+#include "klauncher_cmds.h"
+
+static void sig_handler(int sig_num)
+{
+ // No recursion
+ signal( SIGHUP, SIG_IGN);
+ signal( SIGTERM, SIG_IGN);
+fprintf(stderr, "klauncher: Exiting on signal %d\n", sig_num);
+ KLauncher::destruct(255);
+}
+
+static KCmdLineOptions options[] =
+{
+ { "new-startup", "Internal", 0 },
+ KCmdLineLastOption
+};
+
+extern "C" KDE_EXPORT int kdemain( int argc, char**argv )
+{
+ // Started via kdeinit.
+ if (fcntl(LAUNCHER_FD, F_GETFD) == -1)
+ {
+ fprintf(stderr, "%s", i18n("klauncher: This program is not supposed to be started manually.\n"
+ "klauncher: It is started automatically by kdeinit.\n").local8Bit().data());
+ return 1;
+ }
+
+ QCString cname = KApplication::launcher();
+ char *name = cname.data();
+ KCmdLineArgs::init(argc, argv, name, "KLauncher", "A service launcher.",
+ "v1.0");
+
+ KLauncher::addCmdLineOptions();
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ // WABA: Make sure not to enable session management.
+ putenv(strdup("SESSION_MANAGER="));
+
+ // Allow the locale to initialize properly
+ KLocale::setMainCatalogue("kdelibs");
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ int maxTry = 3;
+ while(true)
+ {
+ QCString dcopName = KApplication::dcopClient()->registerAs(name, false);
+ if (dcopName.isEmpty())
+ {
+ kdWarning() << "DCOP communication problem!" << endl;
+ return 1;
+ }
+ if (dcopName == cname)
+ break; // Good!
+
+ if (--maxTry == 0)
+ {
+ kdWarning() << "Another instance of klauncher is already running!" << endl;
+ return 1;
+ }
+
+ // Wait a bit...
+ kdWarning() << "Waiting for already running klauncher to exit." << endl;
+ sleep(1);
+
+ // Try again...
+ }
+
+ KLauncher *launcher = new KLauncher(LAUNCHER_FD, args->isSet("new-startup"));
+ launcher->dcopClient()->setDefaultObject( name );
+ launcher->dcopClient()->setDaemonMode( true );
+
+ KCrash::setEmergencySaveFunction(sig_handler);
+ signal( SIGHUP, sig_handler);
+ signal( SIGPIPE, SIG_IGN);
+ signal( SIGTERM, sig_handler);
+
+ launcher->exec();
+ return 0;
+}
+
diff --git a/kinit/kstartupconfig.cpp b/kinit/kstartupconfig.cpp
new file mode 100644
index 000000000..8bb60c127
--- /dev/null
+++ b/kinit/kstartupconfig.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+
+ Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+****************************************************************************/
+
+/*
+
+This utility helps to have some configuration options available in startkde
+without the need to launch anything linked to KDE libraries (which may need
+some time to load).
+
+The configuration options are written to $KDEHOME/share/config/startupconfigkeys,
+one option per line, as <file> <group> <key> <default>. It is possible to
+use ' for quoting multiword entries. Values of these options will be written
+to $KDEHOME/share/config/startupconfig as a shell script that will set
+the values to shell variables, named <file>_<group>_<key> (all spaces replaced
+by underscores, everything lowercase). So e.g. line
+"ksplashrc KSplash Theme Default" may result in "ksplashrc_ksplash_theme=Default".
+
+In order to real a whole group it is possible to use <file> <[group]>, e.g.
+"ksplashrc [KSplash]", which will set shell variables for all keys in the group.
+It is not possible to specify default values, but since the configuration options
+are processed in the order they are specified this can be solved by first
+specifying a group and then all the entries that need default values.
+
+When a kconf_update script is used to update such option, kstartupconfig is run
+before kconf_update and therefore cannot see the change in time. To avoid this
+problem, together with the kconf_update script also the matching global config
+file should be updated (any change, kstartupconfig will see the timestamp change).
+
+Note that the kdeglobals config file is not used as a depedendency for other config
+files.
+
+Since the checking is timestamp-based, config files that are frequently updated
+should not be used.
+
+Kstartupconfig works by storing every line from startupconfigkeys in file startupconfigfiles
+followed by paths of all files that are relevant to the option. Non-existent files
+have '!' prepended (for the case they'll be later created), the list of files is
+terminated by line containing '*'. If the timestamps of all relevant files are older
+than the timestamp of the startupconfigfile file, there's no need to update anything.
+Otherwise kdostartupconfig is launched to create or update all the necessary files
+(which already requires loading KDE libraries, but this case should be rare).
+
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+int main()
+ {
+ char kdehome[ 1024 ];
+ if( getenv( "KDEHOME" ))
+ strlcpy( kdehome, getenv( "KDEHOME" ), 1024 );
+ else if( getenv( "HOME" ))
+ {
+ strlcpy( kdehome, getenv( "HOME" ), 1024 );
+ strlcat( kdehome, "/.kde", 1024 );
+ }
+ else
+ return 1;
+ char filename[ 1024 ];
+ strlcpy( filename, kdehome, 1024 );
+ strlcat( filename, "/share/config/startupconfig", 1024 );
+ if( access( filename, R_OK ) != 0 )
+ {
+ int ret = system( "kdostartupconfig" );
+ return WEXITSTATUS( ret );
+ }
+ strlcpy( filename, kdehome, 1024 );
+ strlcat( filename, "/share/config/startupconfigfiles", 1024 );
+ struct stat st;
+ if( stat( filename, &st ) != 0 )
+ {
+ int ret = system( "kdostartupconfig" );
+ return WEXITSTATUS( ret );
+ }
+ time_t config_time = st.st_mtime;
+ FILE* config = fopen( filename, "r" );
+ if( config == NULL )
+ {
+ int ret = system( "kdostartupconfig" );
+ return WEXITSTATUS( ret );
+ }
+ strlcpy( filename, kdehome, 1024 );
+ strlcat( filename, "/share/config/startupconfigkeys", 1024 );
+ FILE* keys = fopen( filename, "r" );
+ if( keys == NULL )
+ {
+ fclose( config );
+ return 2;
+ }
+ bool need_update = true;
+ for(;;)
+ {
+ char keyline[ 1024 ];
+ if( fgets( keyline, 1023, keys ) == NULL )
+ {
+ need_update = false;
+ break;
+ }
+ if( char* nl = strchr( keyline, '\n' ))
+ *nl = '\0';
+ char line[ 1024 ];
+ if( fgets( line, 1023, config ) == NULL )
+ break;
+ if( char* nl = strchr( line, '\n' ))
+ *nl = '\0';
+ if( strcmp( keyline, line ) != 0 )
+ break;
+ bool ok = false;
+ for(;;)
+ {
+ if( fgets( line, 1023, config ) == NULL )
+ break;
+ if( char* nl = strchr( line, '\n' ))
+ *nl = '\0';
+ if( *line == '\0' )
+ break;
+ if( *line == '*' )
+ {
+ ok = true;
+ break;
+ }
+ if( *line == '!' )
+ {
+ if( access( line + 1, R_OK ) == 0 )
+ break; // file now exists -> update
+ }
+ else
+ {
+ struct stat st;
+ if( stat( line, &st ) != 0 )
+ break;
+ if( st.st_mtime > config_time )
+ break;
+ }
+ }
+ if( !ok )
+ break;
+ }
+ fclose( keys );
+ fclose( config );
+ if( need_update )
+ {
+ int ret = system( "kdostartupconfig" );
+ return WEXITSTATUS( ret );
+ }
+ return 0;
+ }
diff --git a/kinit/kwrapper.c b/kinit/kwrapper.c
new file mode 100644
index 000000000..68f27a57a
--- /dev/null
+++ b/kinit/kwrapper.c
@@ -0,0 +1,25 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 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.
+*/
+
+/* this should just save me from duplicating the code */
+
+#define WE_ARE_KWRAPPER
+#include "wrapper.c"
diff --git a/kinit/lnusertemp.c b/kinit/lnusertemp.c
new file mode 100644
index 000000000..70457f74f
--- /dev/null
+++ b/kinit/lnusertemp.c
@@ -0,0 +1,274 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 2000 Waldo Bastian <bastian@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.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+int check_tmp_dir(const char *tmp_dir);
+int create_link(const char *file, const char *tmp_dir);
+int build_link(const char *tmp_prefix, const char *kde_prefix);
+
+int check_tmp_dir(const char *tmp_dir)
+{
+ int result;
+ struct stat stat_buf;
+ result = lstat(tmp_dir, &stat_buf);
+ if ((result == -1) && (errno == ENOENT))
+ {
+ result = mkdir(tmp_dir, 0700);
+ if (result == -1)
+ {
+ fprintf(stderr, "Error: Can not create directory \"%s\".\n", tmp_dir);
+ return 1;
+ }
+ result = stat(tmp_dir, &stat_buf);
+ }
+ if ((result == -1) || (!S_ISDIR(stat_buf.st_mode)))
+ {
+ fprintf(stderr, "Error: \"%s\" is not a directory.\n", tmp_dir);
+ return 1;
+ }
+
+ if (stat_buf.st_uid != getuid())
+ {
+ fprintf(stderr, "Error: \"%s\" is owned by uid %d instead of uid %d.\n", tmp_dir, stat_buf.st_uid, getuid());
+ return 1;
+ }
+ return 0;
+}
+
+int create_link(const char *file, const char *tmp_dir)
+{
+ int result;
+ result = check_tmp_dir(tmp_dir);
+ if (result)
+ {
+ return result;
+ }
+ result = symlink(tmp_dir, file);
+ if (result == -1)
+ {
+ fprintf(stderr, "Error: Can not create link from \"%s\" to \"%s\"\n", file, tmp_dir);
+ return 1;
+ }
+ /*printf("Created link from \"%s\" to \"%s\"\n", file, tmp_dir);*/
+ return 0;
+}
+
+
+int build_link(const char *tmp_prefix, const char *kde_prefix)
+{
+ struct passwd *pw_ent;
+ char kde_tmp_dir[PATH_MAX+1];
+ char user_tmp_dir[PATH_MAX+1];
+ char tmp_buf[PATH_MAX+1];
+ int uid = getuid();
+ const char *home_dir = getenv("HOME");
+ const char *kde_home = uid ? getenv("KDEHOME") : getenv("KDEROOTHOME");
+ int result;
+ struct stat stat_buf;
+
+ kde_tmp_dir[0] = 0;
+
+ pw_ent = getpwuid(uid);
+ if (!pw_ent)
+ {
+ fprintf(stderr, "Error: Can not find password entry for uid %d.\n", getuid());
+ return 1;
+ }
+
+ strncpy(user_tmp_dir, tmp_prefix, PATH_MAX);
+ user_tmp_dir[ PATH_MAX ] = '\0';
+ strncat(user_tmp_dir, pw_ent->pw_name, PATH_MAX - strlen(tmp_prefix));
+
+ if (!kde_home || !kde_home[0])
+ {
+ kde_home = "~/.kde/";
+ }
+
+ if (kde_home[0] == '~')
+ {
+ if (uid == 0)
+ {
+ home_dir = pw_ent->pw_dir ? pw_ent->pw_dir : "/root";
+ }
+ if (!home_dir || !home_dir[0])
+ {
+ fprintf(stderr, "Aborting. $HOME not set!\n");
+ return 1;
+ }
+ if (strlen(home_dir) > (PATH_MAX-100))
+ {
+ fprintf(stderr, "Aborting. Home directory path too long!\n");
+ return 1;
+ }
+ kde_home++;
+ strncpy(kde_tmp_dir, home_dir, PATH_MAX);
+ kde_tmp_dir[ PATH_MAX ] = '\0';
+ }
+ strncat(kde_tmp_dir, kde_home, PATH_MAX - strlen(kde_tmp_dir));
+
+ /** Strip trailing '/' **/
+ if ( kde_tmp_dir[strlen(kde_tmp_dir)-1] == '/')
+ kde_tmp_dir[strlen(kde_tmp_dir)-1] = 0;
+
+ result = stat(kde_tmp_dir, &stat_buf);
+ if ((result == -1) && (errno == ENOENT))
+ {
+ result = mkdir(kde_tmp_dir, 0700);
+ }
+ if (result == -1)
+ {
+ perror("mkdir failed: ");
+ return 1;
+ }
+
+ strncat(kde_tmp_dir, kde_prefix, PATH_MAX - strlen(kde_tmp_dir));
+ if (gethostname(kde_tmp_dir+strlen(kde_tmp_dir), PATH_MAX - strlen(kde_tmp_dir) - 1) != 0)
+ {
+ perror("Aborting. Could not determine hostname: ");
+ exit(255);
+ }
+ kde_tmp_dir[sizeof(kde_tmp_dir)-1] = '\0';
+
+ result = lstat(kde_tmp_dir, &stat_buf);
+ if ((result == 0) && (S_ISDIR(stat_buf.st_mode)))
+ {
+ /* $KDEHOME/tmp is a normal directory. Do nothing. */
+ /*printf("Directory \"%s\" already exists.\n", kde_tmp_dir);*/
+ return 0;
+ }
+ if ((result == -1) && (errno == ENOENT))
+ {
+ /*printf("Creating link %s.\n", kde_tmp_dir);*/
+ result = create_link(kde_tmp_dir, user_tmp_dir);
+ if (result == 0) return 0; /* Success */
+ unlink(kde_tmp_dir);
+ strncat(user_tmp_dir, "XXXXXX", PATH_MAX - strlen(user_tmp_dir));
+ mktemp(user_tmp_dir); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
+ return create_link(kde_tmp_dir, user_tmp_dir);
+ }
+ if ((result == -1) || (!S_ISLNK(stat_buf.st_mode)))
+ {
+ fprintf(stderr, "Error: \"%s\" is not a link or a directory.\n", kde_tmp_dir);
+ return 1;
+ }
+ /* kde_tmp_dir is a link. Check whether it points to a valid directory. */
+ result = readlink(kde_tmp_dir, tmp_buf, PATH_MAX);
+ if (result == -1)
+ {
+ fprintf(stderr, "Error: \"%s\" could not be read.\n", kde_tmp_dir);
+ return 1;
+ }
+ tmp_buf[result] = '\0';
+ /*printf("Link points to \"%s\"\n", tmp_buf);*/
+ if (strncmp(tmp_buf, user_tmp_dir, strlen(user_tmp_dir)) != 0)
+ {
+ fprintf(stderr, "Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir, tmp_buf, user_tmp_dir);
+ unlink(kde_tmp_dir);
+ /*printf("Creating link %s.\n", kde_tmp_dir);*/
+ result = create_link(kde_tmp_dir, user_tmp_dir);
+ if (result == 0) return 0; /* Success */
+ unlink(kde_tmp_dir);
+ strncat(user_tmp_dir, "XXXXXX", PATH_MAX - strlen(user_tmp_dir));
+ mktemp(user_tmp_dir); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
+ return create_link(kde_tmp_dir, user_tmp_dir);
+ }
+ result = check_tmp_dir(tmp_buf);
+ if (result == 0) return 0; /* Success */
+ unlink(kde_tmp_dir);
+ strncat(user_tmp_dir, "XXXXXX", PATH_MAX - strlen(user_tmp_dir));
+ mktemp(user_tmp_dir); /* We want a directory, not a file, so using mkstemp makes no sense and is wrong */
+ return create_link(kde_tmp_dir, user_tmp_dir);
+}
+
+int main(int argc, char **argv)
+{
+ const char *tmp = 0;
+ char *tmp_prefix = 0;
+ const char *kde_prefix = 0;
+ int res = 0;
+
+ if ((argc != 2) ||
+ ((strcmp(argv[1], "tmp")!=0) &&
+ (strcmp(argv[1], "socket")!=0) &&
+ (strcmp(argv[1], "cache")!=0)))
+ {
+ fprintf(stderr, "Usage: lnusertemp tmp|socket|cache\n");
+ return 1;
+ }
+
+ tmp = getenv("KDETMP");
+ if (!tmp || !tmp[0])
+ tmp = getenv("TMPDIR");
+ if (!tmp || !tmp[0])
+ tmp = "/tmp";
+
+ if (strcmp(argv[1], "tmp") == 0)
+ {
+ tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/kde-")+1);
+ strcpy(tmp_prefix, tmp);
+ strcat(tmp_prefix, "/kde-");
+
+ kde_prefix = "/tmp-";
+ }
+ else if (strcmp(argv[1], "socket") == 0)
+ {
+ tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/ksocket-")+1);
+ strcpy(tmp_prefix, tmp );
+ strcat(tmp_prefix, "/ksocket-" );
+
+ kde_prefix = "/socket-";
+ }
+ else if (strcmp(argv[1], "cache") == 0)
+ {
+ tmp = getenv("KDEVARTMP");
+ if (!tmp || !tmp[0])
+ tmp = "/var/tmp";
+
+ tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/kdecache-")+1);
+ strcpy(tmp_prefix, tmp );
+ strcat(tmp_prefix, "/kdecache-" );
+
+ kde_prefix = "/cache-";
+ }
+
+ res = build_link(tmp_prefix, kde_prefix);
+
+ free(tmp_prefix);
+
+ return res;
+}
diff --git a/kinit/setproctitle.cpp b/kinit/setproctitle.cpp
new file mode 100644
index 000000000..a326ef291
--- /dev/null
+++ b/kinit/setproctitle.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * A copy of the above mentioned LICENSE file can be found in
+ * LICENSE.setproctitle.
+ *
+ * Ported for use with KDE by Waldo Bastian <bastian@kde.org>
+ */
+
+#include "setproctitle.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* _GNU_SOURCE might already be defined in <config.h> */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* _PATH_KMEM should be defined in <paths.h> */
+#ifndef _PATH_KMEM
+# define _PATH_KMEM "/dev/kmem"
+#endif
+
+#define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
+
+
+/*
+** SETPROCTITLE -- set process title for ps
+**
+** Parameters:
+** fmt -- a printf style format string.
+** a, b, c -- possible parameters to fmt.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Clobbers argv of our main procedure so ps(1) will
+** display the title.
+*/
+
+#define SPT_NONE 0 /* don't use it at all */
+#define SPT_REUSEARGV 1 /* cover argv with title information */
+#define SPT_BUILTIN 2 /* use libc builtin */
+#define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
+#define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
+#define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
+#define SPT_SCO 6 /* write kernel u. area */
+#define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
+
+#ifndef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+#endif
+
+#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
+
+# if SPT_TYPE == SPT_PSTAT
+# include <sys/pstat.h>
+# endif
+# if SPT_TYPE == SPT_PSSTRINGS
+# include <machine/vmparam.h>
+# ifdef HAVE_SYS_EXEC_H
+# include <sys/exec.h>
+# endif
+# ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
+# undef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+# else
+# ifndef NKPDE /* FreeBSD 2.0 */
+# define NKPDE 63
+typedef unsigned int *pt_entry_t;
+# endif
+# endif
+# endif
+
+# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
+# define SETPROC_STATIC static
+# else
+# define SETPROC_STATIC
+# endif
+
+# if SPT_TYPE == SPT_SYSMIPS
+# include <sys/sysmips.h>
+# include <sys/sysnews.h>
+# endif
+
+# if SPT_TYPE == SPT_SCO
+# include <sys/immu.h>
+# include <sys/dir.h>
+# include <sys/user.h>
+# include <sys/fs/s5param.h>
+# if PSARGSZ > MAXLINE
+# define SPT_BUFSIZE PSARGSZ
+# endif
+# endif
+
+# ifndef SPT_PADCHAR
+# ifdef _AIX
+# define SPT_PADCHAR '\0'
+# else
+# define SPT_PADCHAR ' '
+# endif
+# endif
+
+#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
+
+# ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE 2048
+# endif
+
+/*
+** Pointers for setproctitle.
+** This allows "ps" listings to give more useful information.
+*/
+
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+
+void
+kdeinit_initsetproctitle(int argc, char **argv, char **envp)
+{
+ register int i, envpsize = 0;
+#if !defined(HAVE_NSGETENVIRON) || !defined(HAVE_CRT_EXTERNS_H)
+ extern char **environ;
+#endif
+
+ /*
+ ** Move the environment so setproctitle can use the space at
+ ** the top of memory.
+ */
+
+ for (i = 0; envp[i] != NULL; i++)
+ envpsize += strlen(envp[i]) + 1;
+ environ = (char **) malloc(sizeof (char *) * (i + 1));
+ if (environ == NULL)
+ return;
+
+ for (i = 0; envp[i] != NULL; i++)
+ environ[i] = strdup(envp[i]);
+ environ[i] = NULL;
+
+ /*
+ ** Save start and extent of argv for setproctitle.
+ */
+
+ Argv = argv;
+
+ /*
+ ** Determine how much space we can use for setproctitle.
+ ** Use all contiguous argv and envp pointers starting at argv[0]
+ */
+ for (i = 0; i < argc; i++)
+ {
+ if (i==0 || LastArgv + 1 == argv[i])
+ LastArgv = argv[i] + strlen(argv[i]);
+ else
+ continue;
+ }
+
+ /*
+ * On linux, we don't want to reuse the memory allocated for
+ * the environment, as there are tools that try to read our environment
+ * variables while we're running (ConsoleKit does that).
+ * There is no way to move or resize it, so just not touchint it
+ * seems to be the only option
+ */
+#ifndef __linux__
+ for (i=0; envp[i] != NULL; i++)
+ {
+ if (LastArgv + 1 == envp[i])
+ LastArgv = envp[i] + strlen(envp[i]);
+ else
+ continue;
+ }
+#endif
+}
+
+#if SPT_TYPE != SPT_BUILTIN
+
+/*VARARGS1*/
+static void
+setproctitle(const char *fmt, ...)
+{
+# if SPT_TYPE != SPT_NONE
+ register char *p;
+ register int i;
+ SETPROC_STATIC char buf[SPT_BUFSIZE];
+ va_list ap;
+# if SPT_TYPE == SPT_PSTAT
+ union pstun pst;
+# endif
+# if SPT_TYPE == SPT_SCO
+ off_t seek_off;
+ static int kmem = -1;
+#warning (rikkus) kmempid is declared as int, should be long
+ static int kmempid = -1;
+ struct user u;
+# endif
+
+ p = buf;
+
+ /* print the argument string */
+ va_start(ap, fmt);
+ (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
+ va_end(ap);
+
+ i = strlen(buf);
+
+# if SPT_TYPE == SPT_PSTAT
+ pst.pst_command = buf;
+ pstat(PSTAT_SETCMD, pst, i, 0, 0);
+# endif
+# if SPT_TYPE == SPT_PSSTRINGS
+ PS_STRINGS->ps_nargvstr = 1;
+ PS_STRINGS->ps_argvstr = buf;
+# endif
+# if SPT_TYPE == SPT_SYSMIPS
+ sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
+# endif
+# if SPT_TYPE == SPT_SCO
+ if (kmem < 0 || kmempid != getpid())
+ {
+ if (kmem >= 0)
+ close(kmem);
+ kmem = open(_PATH_KMEM, O_RDWR, 0);
+ if (kmem < 0)
+ return;
+ (void) fcntl(kmem, F_SETFD, 1);
+ kmempid = getpid();
+ }
+ buf[PSARGSZ - 1] = '\0';
+ seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
+ if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
+ (void) write(kmem, buf, PSARGSZ);
+# endif
+# if SPT_TYPE == SPT_REUSEARGV
+ if (i > LastArgv - Argv[0] - 2)
+ {
+ i = LastArgv - Argv[0] - 2;
+ buf[i] = '\0';
+ }
+ (void) strcpy(Argv[0], buf);
+ p = &Argv[0][i];
+ while (p < LastArgv)
+ *p++ = SPT_PADCHAR;
+ Argv[1] = NULL;
+# endif
+# if SPT_TYPE == SPT_CHANGEARGV
+ Argv[0] = buf;
+ Argv[1] = 0;
+# endif
+# endif /* SPT_TYPE != SPT_NONE */
+}
+
+#endif /* SPT_TYPE != SPT_BUILTIN */
+ /*
+** SM_SETPROCTITLE -- set process task and set process title for ps
+**
+** Possibly set process status and call setproctitle() to
+** change the ps display.
+**
+** Parameters:
+** status -- whether or not to store as process status
+** fmt -- a printf style format string.
+** a, b, c -- possible parameters to fmt.
+**
+** Returns:
+** none.
+*/
+
+/*VARARGS2*/
+void
+kdeinit_setproctitle(const char *fmt, ...)
+{
+ char buf[SPT_BUFSIZE];
+
+ va_list ap;
+ /* print the argument string */
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, SPT_BUFSIZE, fmt, ap);
+ va_end(ap);
+
+ setproctitle("%s", buf);
+}
+
diff --git a/kinit/setproctitle.h b/kinit/setproctitle.h
new file mode 100644
index 000000000..df400f4cb
--- /dev/null
+++ b/kinit/setproctitle.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * A copy of the above mentioned LICENSE file can be found in
+ * LICENSE.setproctitle.
+ *
+ * Ported for use with KDE by Waldo Bastian <bastian@kde.org>
+ */
+
+#ifndef _SETPROCTITLE_H_
+#define _SETPROCTITLE_H_
+
+#ifdef __hpux
+# define SPT_TYPE SPT_PSTAT
+#endif
+
+#ifdef _AIX3
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#ifdef _AIX4
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#ifdef AIX /* AIX/RT compiler pre-defines this */
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#ifdef DGUX_5_4_2
+# define DGUX 1
+#endif
+
+#ifdef DGUX
+# define SPT_TYPE SPT_NONE /* don't use setproctitle */
+#endif
+
+/*
+** Apple Rhapsody
+** Contributed by Wilfredo Sanchez <wsanchez@apple.com>
+*/
+
+#ifdef __APPLE__
+# define SPT_TYPE SPT_PSSTRINGS
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#if defined(BSD4_4) && !defined(__bsdi__) && !defined(__GNU__)
+# define SPT_TYPE SPT_PSSTRINGS /* use PS_STRINGS pointer */
+#endif
+
+#ifdef __bsdi__
+# if defined(_BSDI_VERSION) && _BSDI_VERSION >= 199312
+/* version 1.1 or later */
+# undef SPT_TYPE
+# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */
+# else
+/* version 1.0 or earlier */
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# endif
+#endif
+
+#if defined(__QNX__)
+# define SPT_TYPE SPT_REUSEARGV
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+# if defined(__NetBSD__) || defined(__DragonFly__)
+# undef SPT_TYPE
+# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */
+# endif
+# if defined(__FreeBSD__)
+# undef SPT_TYPE
+# if __FreeBSD__ >= 2
+# include <osreldate.h>
+# if __FreeBSD_version >= 199512 /* 2.2-current when it appeared */
+# include <sys/types.h>
+# include <libutil.h>
+# define SPT_TYPE SPT_BUILTIN
+# endif
+# endif
+# ifndef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+# endif
+# endif
+# if defined(__OpenBSD__)
+# undef SPT_TYPE
+# define SPT_TYPE SPT_BUILTIN /* setproctitle is in libc */
+# endif
+#endif
+
+#ifdef __GNU_HURD__
+# define SPT_TYPE SPT_CHANGEARGV
+#endif
+
+/* SCO UNIX 3.2v4.2/Open Desktop 3.0 */
+#ifdef _SCO_unix_4_2
+# define _SCO_unix_
+#endif
+
+/* SCO UNIX 3.2v4.0 Open Desktop 2.0 and earlier */
+#ifdef _SCO_unix_
+# define SPT_TYPE SPT_SCO /* write kernel u. area */
+#endif
+
+
+#ifdef __linux__
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#ifdef _SEQUENT_
+# define SPT_TYPE SPT_NONE /* don't use setproctitle */
+#endif
+
+#ifdef apollo
+# define SPT_TYPE SPT_NONE /* don't use setproctitle */
+#endif
+
+#ifdef __svr5__
+# define SPT_PADCHAR '\0' /* pad process title with nulls */
+#endif
+
+#ifdef NCR_MP_RAS2
+# define SPT_TYPE SPT_NONE
+#endif
+
+#ifdef NCR_MP_RAS3
+# define SPT_TYPE SPT_NONE
+#endif
+
+#ifdef sony_news
+# ifndef __svr4
+# ifndef SPT_TYPE
+# define SPT_TYPE SPT_SYSMIPS /* use sysmips() (OS 6.0.2 or later) */
+# endif
+# endif
+#endif
+
+
+extern void kdeinit_initsetproctitle(int, char **, char **);
+extern void kdeinit_setproctitle(const char *, ...)
+#ifdef __GNUC__
+ __attribute__ (( format ( printf, 1, 2 ) ) )
+#endif
+;
+
+#endif
+
diff --git a/kinit/shell.c b/kinit/shell.c
new file mode 100644
index 000000000..62bcb3cc7
--- /dev/null
+++ b/kinit/shell.c
@@ -0,0 +1,25 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 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.
+*/
+
+/* this should just save me from duplicating the code */
+
+#define WE_ARE_KSHELL
+#include "wrapper.c"
diff --git a/kinit/start_kdeinit.c b/kinit/start_kdeinit.c
new file mode 100644
index 000000000..722f3e97f
--- /dev/null
+++ b/kinit/start_kdeinit.c
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2006 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.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef KDEINIT_OOM_PROTECT
+
+/*
+ Prevent getting killed by bad heuristic in Linux OOM-killer.
+ This wrapper decreases the chance OOM killer kills it (or its children,
+ namely kdeinit), opens a pipe and forks. Child drops privileges
+ and launches kdeinit. Since processes started by kdeinit should
+ not have this protection, kdeinit will after forking send the new
+ PID using the pipe and wait for a signal. This parent will reset the protection
+ and SIGUSR1 the process to continue.
+ returns 1 if pid is valid
+*/
+
+static int set_protection( pid_t pid, int enable )
+{
+ char buf[ 1024 ];
+ int procfile;
+ sprintf( buf, "/proc/%d/stat", pid );
+ if( !enable ) {
+ /* Be paranoid and check that the pid we got from the pipe
+ belongs to this user. */
+ struct stat st;
+ if( lstat( buf, &st ) < 0 || st.st_uid != getuid())
+ return 0;
+ }
+ sprintf( buf, "/proc/%d/oom_adj", pid );
+ procfile = open( buf, O_WRONLY );
+ if( procfile >= 0 ) {
+ if( enable )
+ write( procfile, "-5", sizeof( "-5" ));
+ else
+ write( procfile, "0", sizeof( "0" ));
+ close( procfile );
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int pipes[ 2 ];
+ int new_argc;
+ const char** new_argv;
+ char helper_num[ 1024 ];
+ unsigned i;
+ char** orig_environ = NULL;
+ char header[ 7 ];
+ if( pipe( pipes ) < 0 ) {
+ perror( "pipe()" );
+ return 1;
+ }
+ if( argc < 0 || argc > 1000 )
+ abort(); /* paranoid */
+ set_protection( getpid(), 1 );
+ switch( fork()) {
+ case -1:
+ perror( "fork()" );
+ return 1;
+ default: /* parent, drop privileges and exec */
+ if (setgid(getgid())) {
+ perror("setgid()");
+ return 1;
+ }
+ if (setuid(getuid()) || geteuid() != getuid()) {
+ perror("setuid()");
+ return 1;
+ }
+ close( pipes[ 0 ] );
+ /* read original environment passed by start_kdeinit_wrapper */
+ if( read( 0, header, 7 ) == 7 && strncmp( header, "environ", 7 ) == 0 ) {
+ unsigned count;
+ if( read( 0, &count, sizeof( unsigned )) == sizeof( unsigned )
+ && count && count < (1<<16)) {
+ char** env = malloc(( count + 1 ) * sizeof( char* ));
+ int ok = 1;
+ for( i = 0;
+ i < count && ok;
+ ++i ) {
+ unsigned len;
+ if( read( 0, &len, sizeof( unsigned )) == sizeof( unsigned )
+ && len && len < (1<<12)) {
+ env[ i ] = malloc( len + 1 );
+ if( (unsigned) read( 0, env[ i ], len ) == len ) {
+ env[ i ][ len ] = '\0';
+ } else {
+ ok = 0;
+ }
+ }
+ }
+ if( ok ) {
+ env[ i ] = NULL;
+ orig_environ = env;
+ }
+ }
+ }
+ if(argc == 0)
+ return 1;
+ new_argc = argc + 2;
+ new_argv = malloc( sizeof( char* ) * ( new_argc + 1 ));
+ if( new_argv == NULL )
+ return 1;
+ new_argv[ 0 ] = EXECUTE;
+ new_argv[ 1 ] = "--oom-pipe";
+ sprintf( helper_num, "%d", pipes[ 1 ] );
+ new_argv[ 2 ] = helper_num;
+ for( i = 1;
+ i <= (unsigned) argc;
+ ++i )
+ new_argv[ i + 2 ] = argv[ i ];
+ if( orig_environ )
+ execve(EXECUTE, (char**)new_argv, orig_environ);
+ else
+ execv(EXECUTE, (char**)new_argv);
+ perror(EXECUTE);
+ return 1;
+ case 0: /* child, keep privileges and do the privileged work */
+ close( pipes[ 1 ] );
+ for(;;) {
+ pid_t pid = 0;
+ int ret = read( pipes[ 0 ], &pid, sizeof( pid_t ));
+ if( ret < 0 && errno == EINTR )
+ continue;
+ if( ret <= 0 ) /* pipe closed or error, exit */
+ _exit(0);
+ if( pid != 0 ) {
+ if (set_protection( pid, 0 ))
+ kill( pid, SIGUSR1 );
+ }
+ }
+ }
+}
+
+#else /* not Linux, the simple non-setuid case */
+
+int main(int argc, char **argv)
+{
+ if(argc == 0)
+ return 1;
+ argv[0] = (char*)EXECUTE;
+ execv(EXECUTE,argv);
+ perror(EXECUTE);
+ return 1;
+}
+#endif
diff --git a/kinit/start_kdeinit_wrapper.c b/kinit/start_kdeinit_wrapper.c
new file mode 100644
index 000000000..bc6d0e31f
--- /dev/null
+++ b/kinit/start_kdeinit_wrapper.c
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2007 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef KDEINIT_OOM_PROTECT
+
+/*
+ The start_kdeinit wrapper is setuid, which means some shell variables like LD_LIBRARY_PATH
+ get unset before it's launched. However kdeinit is used to launch most of KDE, so such variables
+ should not be dropped. Therefore this wrapper for the setuid wrapper read the environment
+ and writes it to start_kdeinit's stdin, which after dropping priviledges reads it and uses it
+ for launching the real kdeinit.
+*/
+int main(int argc, char **argv)
+{
+ int pipes[ 2 ];
+ if(argc == 0)
+ return 1;
+ if( pipe( pipes ) < 0 ) {
+ perror( "pipe()" );
+ return 1;
+ }
+ switch( fork()) {
+ case -1:
+ perror( "fork()" );
+ return 1;
+ default: /* parent, exec */
+ close( pipes[ 1 ] );
+ close( 0 ); /* stdin */
+ dup2( pipes[ 0 ], 0 );
+ close( pipes[ 0 ] );
+ argv[ 0 ] = (char*)"start_kdeinit";
+ execvp("start_kdeinit", argv);
+ perror("start_kdeinit");
+ return 1;
+ case 0: { /* child, pass env and exit */
+ extern char** environ;
+ int i;
+ close( pipes[ 0 ] );
+ write( pipes[ 1 ], "environ", 7 ); /* header, just in case */
+ for( i = 0;
+ environ[ i ] != NULL;
+ ++i )
+ {}
+ write( pipes[ 1 ], &i, sizeof( int )); /* write count */
+ for( i = 0;
+ environ[ i ] != NULL;
+ ++i )
+ {
+ int len = strlen( environ[ i ] );
+ write( pipes[ 1 ], &len, sizeof( int )); /* write length */
+ write( pipes[ 1 ], environ[ i ], strlen( environ[ i ] ));
+ }
+ close( pipes[ 1 ] );
+ }
+ }
+ return 0;
+}
+
+#else /* not Linux, the simple non-setuid case */
+
+int main(int argc, char **argv)
+{
+ if(argc == 0)
+ return 1;
+ argv[0] = "start_kdeinit";
+ execvp("start_kdeinit",argv);
+ perror("start_kdeinit");
+ return 1;
+}
+#endif
diff --git a/kinit/tests/Makefile.am b/kinit/tests/Makefile.am
new file mode 100644
index 000000000..909e7c14b
--- /dev/null
+++ b/kinit/tests/Makefile.am
@@ -0,0 +1,10 @@
+# $Id$
+
+INCLUDES= -I$(srcdir)/../.. $(all_includes)
+####### Files
+
+check_PROGRAMS = klaunchertest
+
+klaunchertest_SOURCES = klaunchertest.cpp
+klaunchertest_LDADD = $(LIB_KIO)
+
diff --git a/kinit/tests/klaunchertest.cpp b/kinit/tests/klaunchertest.cpp
new file mode 100644
index 000000000..56cc93c24
--- /dev/null
+++ b/kinit/tests/klaunchertest.cpp
@@ -0,0 +1,54 @@
+/*
+ This file is part of KDE
+
+ Copyright (C) 1998 Waldo Bastian (bastian@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ This software 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qstring.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <stdio.h>
+#include <qvaluelist.h>
+#include <kservice.h>
+
+
+int main(int argc, char *argv[])
+{
+ KApplication::kdeinitExec("konsole");
+
+ KApplication k(argc, argv, "klaunchertest");
+
+ kapp->dcopClient()->registerAs( kapp->name()) ;
+
+#if 0
+ QString error;
+ QCString dcopService;
+ int pid;
+ int result = KApplication::startServiceByDesktopName(
+ QString::fromLatin1("konsole"), QString::null, &error, &dcopService, &pid );
+
+ printf("Result = %d, error = \"%s\", dcopService = \"%s\", pid = %d\n",
+ result, error.ascii(), dcopService.data(), pid);
+
+ result = KApplication::startServiceByDesktopName(
+ QString::fromLatin1("konqueror"), QString::null, &error, &dcopService, &pid );
+
+ printf("Result = %d, error = \"%s\", dcopService = \"%s\", pid = %d\n",
+ result, error.ascii(), dcopService.data(), pid);
+#endif
+}
+
diff --git a/kinit/wrapper.c b/kinit/wrapper.c
new file mode 100644
index 000000000..0248e4789
--- /dev/null
+++ b/kinit/wrapper.c
@@ -0,0 +1,547 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (c) 1999 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.
+*/
+
+#include <config.h>
+
+#include "klauncher_cmds.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <signal.h>
+
+extern char **environ;
+
+static char *getDisplay()
+{
+ const char *display;
+ char *result;
+ char *screen;
+ char *colon;
+ char *i;
+/*
+ don't test for a value from qglobal.h but instead distinguish
+ Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS
+ on the commandline (which in qglobal.h however triggers Q_WS_QWS,
+ but we don't want to include that here) (Simon)
+#ifdef Q_WS_X11
+ */
+#if !defined(QWS)
+ display = getenv("DISPLAY");
+#else
+ display = getenv("QWS_DISPLAY");
+#endif
+ if (!display || !*display)
+ {
+ display = ":0";
+ }
+ result = malloc(strlen(display)+1);
+ if (result == NULL)
+ return NULL;
+
+ strcpy(result, display);
+ screen = strrchr(result, '.');
+ colon = strrchr(result, ':');
+ if (screen && (screen > colon))
+ *screen = '\0';
+ while((i = strchr(result, ':')))
+ *i = '_';
+ return result;
+}
+
+/*
+ * Write 'len' bytes from 'buffer' into 'sock'.
+ * returns 0 on success, -1 on failure.
+ */
+static int write_socket(int sock, char *buffer, int len)
+{
+ ssize_t result;
+ int bytes_left = len;
+ while ( bytes_left > 0)
+ {
+ result = write(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;
+}
+
+/*
+ * 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 int openSocket()
+{
+ kde_socklen_t socklen;
+ int s;
+ struct sockaddr_un server;
+#define MAX_SOCK_FILE 255
+ char sock_file[MAX_SOCK_FILE + 1];
+ const char *home_dir = getenv("HOME");
+ const char *kde_home = getenv("KDEHOME");
+ char *display;
+
+ sock_file[0] = sock_file[MAX_SOCK_FILE] = 0;
+
+ if (!kde_home || !kde_home[0])
+ {
+ kde_home = "~/.kde/";
+ }
+
+ if (kde_home[0] == '~')
+ {
+ if (!home_dir || !home_dir[0])
+ {
+ fprintf(stderr, "Warning: $HOME not set!\n");
+ return -1;
+ }
+ if (strlen(home_dir) > (MAX_SOCK_FILE-100))
+ {
+ fprintf(stderr, "Warning: Home directory path too long!\n");
+ return -1;
+ }
+ kde_home++;
+ strncpy(sock_file, home_dir, MAX_SOCK_FILE);
+ }
+ strncat(sock_file, kde_home, MAX_SOCK_FILE - strlen(sock_file));
+
+ /** Strip trailing '/' **/
+ if ( sock_file[strlen(sock_file)-1] == '/')
+ sock_file[strlen(sock_file)-1] = 0;
+
+ strncat(sock_file, "/socket-", MAX_SOCK_FILE - strlen(sock_file));
+ if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0)
+ {
+ perror("Warning: Could not determine hostname: ");
+ return -1;
+ }
+ sock_file[sizeof(sock_file)-1] = '\0';
+
+ /* append $DISPLAY */
+ display = getDisplay();
+ if (display == NULL)
+ {
+ fprintf(stderr, "Error: Could not determine display.\n");
+ return -1;
+ }
+
+ if (strlen(sock_file)+strlen(display)+strlen("/kdeinit_")+2 > MAX_SOCK_FILE)
+ {
+ fprintf(stderr, "Warning: Socket name will be too long.\n");
+ free (display);
+ return -1;
+ }
+ strcat(sock_file, "/kdeinit_");
+ strcat(sock_file, display);
+ free(display);
+
+ if (strlen(sock_file) >= sizeof(server.sun_path))
+ {
+ fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n");
+ return -1;
+ }
+
+ /*
+ * create the socket stream
+ */
+ s = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (s < 0)
+ {
+ perror("Warning: socket() failed: ");
+ return -1;
+ }
+
+ server.sun_family = AF_UNIX;
+ strcpy(server.sun_path, sock_file);
+ socklen = sizeof(server);
+ if(connect(s, (struct sockaddr *)&server, socklen) == -1)
+ {
+ perror("Warning: connect() failed: ");
+ close(s);
+ return -1;
+ }
+ return s;
+}
+
+static pid_t kwrapper_pid;
+static volatile pid_t got_sig; /* = 0 */
+static pid_t last_sig; /* = 0 */
+
+static void sig_pass_handler( int signo );
+static void setup_signals( void );
+
+static void setup_signal_handler( int signo, int clean )
+{
+ struct sigaction sa;
+ if( clean )
+ sa.sa_handler = SIG_DFL;
+ else
+ sa.sa_handler = sig_pass_handler;
+ sigemptyset( &sa.sa_mask );
+ sigaddset( &sa.sa_mask, signo );
+ sa.sa_flags = 0; /* don't use SA_RESTART */
+ sigaction( signo, &sa, 0 );
+}
+
+static void sig_pass_handler( int signo )
+{
+ int save_errno = errno;
+ if( signo == SIGTSTP )
+ kill( kwrapper_pid, SIGSTOP ); /* pass the signal to the real process */
+ else /* SIGTSTP wouldn't work ... I don't think is much */
+ kill( kwrapper_pid, signo ); /* of a problem */
+ got_sig = signo;
+ if( got_sig == SIGCONT )
+ setup_signals(); /* restore signals */
+ errno = save_errno;
+}
+
+static void setup_signals()
+{
+ setup_signal_handler( SIGHUP, 0 );
+ setup_signal_handler( SIGINT, 0 );
+ setup_signal_handler( SIGQUIT, 0 );
+ setup_signal_handler( SIGILL, 0 ); /* e.g. this one is probably doesn't make sense to pass */
+ setup_signal_handler( SIGABRT, 0 ); /* but anyway ... */
+ setup_signal_handler( SIGFPE, 0 );
+ /* SIGKILL can't be handled :( */
+ setup_signal_handler( SIGSEGV, 0 );
+ setup_signal_handler( SIGPIPE, 0 );
+ setup_signal_handler( SIGALRM, 0 );
+ setup_signal_handler( SIGTERM, 0 );
+ setup_signal_handler( SIGUSR1, 0 );
+ setup_signal_handler( SIGUSR2, 0 );
+ setup_signal_handler( SIGCHLD, 0 ); /* is this a good idea ??? */
+ setup_signal_handler( SIGCONT, 0 ); /* SIGSTOP can't be handled, but SIGTSTP and SIGCONT can */
+ /* SIGSTOP */ /* which should be enough */
+ setup_signal_handler( SIGTSTP, 0 );
+ setup_signal_handler( SIGTTIN, 0 ); /* is this a good idea ??? */
+ setup_signal_handler( SIGTTOU, 0 ); /* is this a good idea ??? */
+ /* some more ? */
+}
+
+static void kwrapper_run( pid_t pid )
+{
+ kwrapper_pid = pid;
+ setup_signals();
+ for(;;)
+ {
+ sleep( 1 ); /* poll and see if the real process is still alive */
+ if( got_sig != last_sig ) /* did we get a signal ? */
+ {
+ last_sig = got_sig;
+ if( got_sig == SIGCHLD )
+ ; /* nothing, ignore */
+ else if( got_sig == SIGCONT )
+ ; /* done in signal handler */
+ else /* do the default action ( most of them quit the app ) */
+ {
+ setup_signal_handler( got_sig, 1 );
+ raise( got_sig ); /* handle the signal again */
+ }
+ }
+ if( kill( pid, 0 ) < 0 )
+ break;
+ }
+ /* I'm afraid it won't be that easy to get the return value ... */
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ int wrapper = 0;
+ int ext_wrapper = 0;
+ int kwrapper = 0;
+ long arg_count;
+ long env_count;
+ klauncher_header header;
+ char *start, *p, *buffer;
+ char cwd[8192];
+ const char *tty = NULL;
+ long avoid_loops = 0;
+ const char* startup_id = NULL;
+ int sock;
+
+ long size = 0;
+
+ start = argv[0];
+ p = start + strlen(argv[0]);
+ while (--p > start)
+ {
+ if (*p == '/') break;
+ }
+ if ( p > start)
+ p++;
+ start = p;
+
+ if (strcmp(start, "kdeinit_wrapper") == 0)
+ wrapper = 1;
+ else if (strcmp(start, "kshell") == 0)
+ ext_wrapper = 1;
+ else if (strcmp(start, "kwrapper") == 0)
+ kwrapper = 1;
+ else if (strcmp(start, "kdeinit_shutdown") == 0)
+ {
+ if( argc > 1)
+ {
+ fprintf(stderr, "Usage: %s\n\n", start);
+ fprintf(stderr, "Shuts down kdeinit master process and terminates all processes spawned from it.\n");
+ exit( 255 );
+ }
+ sock = openSocket();
+ if( sock < 0 )
+ {
+ fprintf( stderr, "Error: Can't contact kdeinit!\n" );
+ exit( 255 );
+ }
+ header.cmd = LAUNCHER_TERMINATE_KDE;
+ header.arg_length = 0;
+ write_socket(sock, (char *) &header, sizeof(header));
+ read_socket(sock, (char *) &header, 1); /* wait for the socket to close */
+ return 0;
+ }
+
+ if (wrapper || ext_wrapper || kwrapper)
+ {
+ argv++;
+ argc--;
+ if (argc < 1)
+ {
+ fprintf(stderr, "Usage: %s <application> [<args>]\n", start);
+ exit(255); /* usage should be documented somewhere ... */
+ }
+ start = argv[0];
+ }
+
+ sock = openSocket();
+ if( sock < 0 ) /* couldn't contact kdeinit, start argv[ 0 ] directly */
+ {
+ execvp( argv[ 0 ], argv );
+ fprintf( stderr, "Error: Can't run %s !\n", argv[ 0 ] );
+ exit( 255 );
+ }
+
+ if( !wrapper && !ext_wrapper && !kwrapper )
+ { /* was called as a symlink */
+ avoid_loops = 1;
+#if defined(WE_ARE_KWRAPPER)
+ kwrapper = 1;
+#elif defined(WE_ARE_KSHELL)
+ ext_wrapper = 1;
+#else
+ wrapper = 1;
+#endif
+ }
+
+ arg_count = argc;
+
+ size += sizeof(long); /* Number of arguments*/
+
+ size += strlen(start)+1; /* Size of first argument. */
+
+ for(i = 1; i < argc; i++)
+ {
+ size += strlen(argv[i])+1;
+ }
+ if( wrapper )
+ {
+ size += sizeof(long); /* empty envs */
+ }
+ if (ext_wrapper || kwrapper)
+ {
+ if (!getcwd(cwd, 8192))
+ cwd[0] = '\0';
+ size += strlen(cwd)+1;
+
+ env_count = 0;
+ size += sizeof(long); /* Number of env.vars. */
+
+ for(; environ[env_count] ; env_count++)
+ {
+ int l = strlen(environ[env_count])+1;
+ size += l;
+ }
+
+ if( kwrapper )
+ {
+ tty = ttyname(1);
+ if (!tty || !isatty(2))
+ tty = "";
+ size += strlen(tty)+1;
+ }
+ }
+
+ size += sizeof( avoid_loops );
+
+ if( !wrapper )
+ {
+ startup_id = getenv( "DESKTOP_STARTUP_ID" );
+ if( startup_id == NULL )
+ startup_id = "";
+ size += strlen( startup_id ) + 1;
+ }
+
+ if (wrapper)
+ header.cmd = LAUNCHER_EXEC_NEW;
+ else if (kwrapper)
+ header.cmd = LAUNCHER_KWRAPPER;
+ else
+ header.cmd = LAUNCHER_SHELL;
+ header.arg_length = size;
+ write_socket(sock, (char *) &header, sizeof(header));
+
+ buffer = (char *) malloc(size);
+ if (buffer == NULL)
+ {
+ fprintf(stderr, "Error: malloc() failed.");
+ exit(255);
+ }
+ p = buffer;
+
+ memcpy(p, &arg_count, sizeof(arg_count));
+ p += sizeof(arg_count);
+
+ memcpy(p, start, strlen(start)+1);
+ p += strlen(start)+1;
+
+ for(i = 1; i < argc; i++)
+ {
+ memcpy(p, argv[i], strlen(argv[i])+1);
+ p += strlen(argv[i])+1;
+ }
+
+ if( wrapper )
+ {
+ long dummy = 0;
+ memcpy(p, &dummy, sizeof(dummy)); /* empty envc */
+ p+= sizeof( dummy );
+ }
+ if (ext_wrapper || kwrapper)
+ {
+ memcpy(p, cwd, strlen(cwd)+1);
+ p+= strlen(cwd)+1;
+
+ memcpy(p, &env_count, sizeof(env_count));
+ p+= sizeof(env_count);
+
+ for(i = 0; i < env_count; i++)
+ {
+ int l = strlen(environ[i])+1;
+ memcpy(p, environ[i], l);
+ p += l;
+ }
+
+ if( kwrapper )
+ {
+ memcpy(p, tty, strlen(tty)+1);
+ p+=strlen(tty)+1;
+ }
+ }
+
+ memcpy( p, &avoid_loops, sizeof( avoid_loops ));
+ p += sizeof( avoid_loops );
+
+ if( !wrapper )
+ {
+ memcpy(p, startup_id, strlen(startup_id)+1);
+ p+= strlen(startup_id)+1;
+ }
+
+ if( p - buffer != size ) /* should fail only if you change this source and do */
+ /* a stupid mistake, it should be assert() actually */
+ {
+ fprintf(stderr, "Oops. Invalid format.\n");
+ exit(255);
+ }
+
+ write_socket(sock, buffer, size);
+ free( buffer );
+
+ if (read_socket(sock, (char *) &header, sizeof(header))==-1)
+ {
+ fprintf(stderr, "Communication error with KInit.\n");
+ exit(255);
+ }
+
+ if (header.cmd == LAUNCHER_OK)
+ {
+ long pid;
+ buffer = (char *) malloc(header.arg_length);
+ if (buffer == NULL)
+ {
+ fprintf(stderr, "Error: malloc() failed\n");
+ exit(255);
+ }
+ read_socket(sock, buffer, header.arg_length);
+ pid = *((long *) buffer);
+ if( !kwrapper ) /* kwrapper shouldn't print any output */
+ printf("Launched ok, pid = %ld\n", pid);
+ else
+ kwrapper_run( pid );
+ }
+ else if (header.cmd == LAUNCHER_ERROR)
+ {
+ fprintf(stderr, "KInit could not launch '%s'.\n", start);
+ exit(255);
+ }
+ else
+ {
+ fprintf(stderr, "Unexpected response from KInit (response = %ld).\n", header.cmd);
+ exit(255);
+ }
+ exit(0);
+}