diff options
Diffstat (limited to 'tdeinit')
29 files changed, 7003 insertions, 0 deletions
diff --git a/tdeinit/CMakeLists.txt b/tdeinit/CMakeLists.txt new file mode 100644 index 000000000..be78311eb --- /dev/null +++ b/tdeinit/CMakeLists.txt @@ -0,0 +1,167 @@ +################################################# +# +# (C) 2010 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${TQT_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR}/tdecore + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/dcop + ${CMAKE_SOURCE_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/tdeui + ${CMAKE_SOURCE_DIR}/tdeio + ${CMAKE_SOURCE_DIR}/tdeio/tdeio + ${FREETYPE_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +#### tdeinit #################################### + +set( target tdeinit ) + +set( ${target}_SRCS + tdeinit.cpp setproctitle.cpp +) + +tde_add_executable( ${target} + SOURCES ${${target}_SRCS} + LINK ltdlc-static tdeparts-shared + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### tdeinit_wrapper ############################ + +set( target tdeinit_wrapper ) + +tde_add_executable( ${target} + SOURCES wrapper.c + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### kshell ##################################### + +set( target kshell ) + +tde_add_executable( ${target} + SOURCES shell.c + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### tdeinit_shutdown ########################### + +set( target tdeinit_shutdown ) + +tde_add_executable( ${target} + SOURCES wrapper.c + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### lnusertemp ################################# + +set( target lnusertemp ) + +tde_add_executable( ${target} + SOURCES lnusertemp.c + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### kwrapper ################################### + +set( target kwrapper ) + +tde_add_executable( ${target} + SOURCES kwrapper.c + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### tdeioslave ################################### + +set( target tdeioslave ) + +tde_add_executable( ${target} AUTOMOC + SOURCES tdeioslave.cpp + LINK ltdlc-static tdeio-shared + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### tdestartupconfig ############################# + +set( target tdestartupconfig ) + +tde_add_executable( ${target} AUTOMOC + SOURCES tdestartupconfig.cpp + LINK tdefakes-shared ${TQT_LIBRARIES} + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### tdedostartupconfig ########################### + +set( target tdedostartupconfig ) + +tde_add_executable( ${target} AUTOMOC + SOURCES tdedostartupconfig.cpp + LINK tdecore-shared + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### start_tdeinit ############################## + +set( target start_tdeinit ) + +if( TDEINIT_SETUID ) + set( _setuid SETUID ) +endif( TDEINIT_SETUID ) + +tde_add_executable( ${target} ${_setuid} + SOURCES start_tdeinit.c + DESTINATION ${BIN_INSTALL_DIR} +) + +set_target_properties( ${target} PROPERTIES COMPILE_FLAGS -DEXECUTE=\\"${BIN_INSTALL_DIR}/tdeinit\\" ) + + +#### start_tdeinit_wrapper ###################### + +set( target start_tdeinit_wrapper ) + +tde_add_executable( ${target} + SOURCES start_tdeinit_wrapper.c + DESTINATION ${BIN_INSTALL_DIR} +) + + +#### tdelauncher ################################## + +set( target tdelauncher ) + +set( ${target}_SRCS + tdelauncher.cpp tdelauncher_main.cpp autostart.cpp +) + +tde_add_tdeinit_executable( ${target} AUTOMOC + SOURCES ${${target}_SRCS} + LINK tdeio-shared +) diff --git a/tdeinit/LICENSE.setproctitle b/tdeinit/LICENSE.setproctitle new file mode 100644 index 000000000..d0ff63a9c --- /dev/null +++ b/tdeinit/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/tdeinit/Mainpage.dox b/tdeinit/Mainpage.dox new file mode 100644 index 000000000..06a34909b --- /dev/null +++ b/tdeinit/Mainpage.dox @@ -0,0 +1,29 @@ +/** @mainpage Trinity Initialization Routines + +tdeinit 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 Trinity applications. + +tdeinit_wrapper, kshell and kwrapper provide methods +of starting programs via tdeinit. + +@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 = tdecore +// DOXYGEN_SET_PROJECT_NAME = KInit +// vim:ts=4:sw=4:expandtab:filetype=doxygen diff --git a/tdeinit/Makefile.am b/tdeinit/Makefile.am new file mode 100644 index 000000000..85381ccd4 --- /dev/null +++ b/tdeinit/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) $(TDEINIT_XFT_INCLUDES) + +SUBDIRS = . tests + +bin_PROGRAMS = tdeinit tdeinit_wrapper kshell tdeinit_shutdown lnusertemp kwrapper tdeioslave \ + tdestartupconfig tdedostartupconfig start_tdeinit start_tdeinit_wrapper + +lib_LTLIBRARIES = +tdeinit_LTLIBRARIES = tdelauncher.la +tdeinit_SOURCES = tdeinit.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) + +tdeinit_LDFLAGS = $(KDE_MT_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) $(USER_LDFLAGS) \ + $(KDE_RPATH) +tdeinit_LDADD = $(LIB_KPARTS) -lXft $(LIB_QT) + +tdeioslave_SOURCES = tdeioslave.cpp +tdeioslave_LDFLAGS = $(KDE_MT_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) $(USER_LDFLAGS) \ + $(KDE_RPATH) +tdeioslave_LDADD = $(LIB_KIO) + +tdeinit_wrapper_SOURCES = wrapper.c +tdeinit_wrapper_LDADD = $(LIBSOCKET) + +kshell_SOURCES = shell.c +kshell_LDADD = $(LIBSOCKET) + +tdeinit_shutdown_SOURCES = wrapper.c +tdeinit_shutdown_LDADD = $(LIBSOCKET) + +lnusertemp_SOURCES = lnusertemp.c +lnusertemp_LDFLAGS = $(KDE_RPATH) + +kwrapper_SOURCES = kwrapper.c +kwrapper_LDADD = $(LIBSOCKET) + +tdelauncher_la_LDFLAGS = $(all_libraries) -module -avoid-version +tdelauncher_la_LIBADD = $(LIB_KIO) $(LIB_QT) $(LIB_TDECORE) $(top_builddir)/dcop/libDCOP.la $(LIB_X11) +tdelauncher_la_SOURCES = tdelauncher.cpp tdelauncher_main.cpp autostart.cpp + +tdestartupconfig_SOURCES = tdestartupconfig.cpp +tdestartupconfig_LDADD = $(top_builddir)/tdecore/libtdefakes.la $(LIB_QT) +tdedostartupconfig_SOURCES = tdedostartupconfig.cpp +tdedostartupconfig_LDFLAGS = $(KDE_MT_LDFLAGS) $(QT_LDFLAGS) $(X_LDFLAGS) $(USER_LDFLAGS) \ + $(KDE_RPATH) +tdedostartupconfig_LDADD = $(LIB_TDECORE) + +start_tdeinit_SOURCES = start_tdeinit.c +tdeinitpath = $(bindir)/tdeinit +start_tdeinit_CFLAGS = $(KDE_USE_FPIE) -DEXECUTE=\"$(tdeinitpath)\" +start_tdeinit_LDFLAGS = $(KDE_USE_PIE) + +start_tdeinit_wrapper_SOURCES = start_tdeinit_wrapper.c + +METASOURCES = AUTO + +noinst_HEADERS = tdelauncher.h autostart.h tdelauncher_cmds.h setproctitle.h + +kwrapper.o: wrapper.c + +shell.o: wrapper.c + +dummy.cpp: + echo > dummy.cpp + +install-exec-hook: + @if test \${TDEINIT_SETUID} != 0; then \ + (chown 0 $(DESTDIR)$(bindir)/start_tdeinit && chmod 4755 $(DESTDIR)$(bindir)/start_tdeinit) || echo "Please make start_tdeinit setuid root" ; \ + fi + +include $(top_srcdir)/admin/Doxyfile.am diff --git a/tdeinit/README b/tdeinit/README new file mode 100644 index 000000000..644efbaf6 --- /dev/null +++ b/tdeinit/README @@ -0,0 +1,84 @@ +README + +tdeinit 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 tdeinit 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 +============ + +tdeinit 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, libtdecore and libtdeui +in the conventional way takes approx. 150ms on a Pentium III - 500Mhz. +Starting the same application via tdeinit takes less than 10ms. + +(application without TDEApplication constructor, the TDEApplication +constructor requires an extra 100ms in both cases) + +Memory Usage +============ + +An application linked against libqt, libtdecore and libtdeui started +in the conventional way requires about 498Kb memory. +(average of 10 instances) If the same application is started via +tdeinit it requires about 142Kb. A difference of 356Kb (application +without TDEApplication constructor) + +If we take the TDEApplication constructor into account, an application +started in the conventional way takes about 679Kb memory while the same +application started via tdeinit 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 tdeinit is +started with this option on, tdeinit is back to its full efficiency, an +application with a TDEApplication constructor now uses 338Kb of memory. +A difference of 341Kb with the normal case. + +Adapting programs to use tdeinit. +=============================== + +The sourcecode of a program does not require any change to take advantage +of tdeinit. 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 tdeinit: + +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 tdeinit is "tdeinit". This problem +can be corrected to a degree by changing the application name as shown +by 'ps'. However, applications like "killall" will only see "tdeinit" as +process name. To workaround this, use "kdekillall", from tdesdk/scripts, +for applications started via tdeinit. + diff --git a/tdeinit/README.DCOP b/tdeinit/README.DCOP new file mode 100644 index 000000000..cd5ba983e --- /dev/null +++ b/tdeinit/README.DCOP @@ -0,0 +1,83 @@ +"TDELauncher" 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 $TDEDIRS/applnk + * and/or $TDEDIRS/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 $TDEDIR/applnk and/or + * $TDEDIR/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/tdeinit/README.autostart b/tdeinit/README.autostart new file mode 100644 index 000000000..ab8132915 --- /dev/null +++ b/tdeinit/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 $TDEDIR/share/autostart or $TDEHOME/share/autostart. The .desktop file can +contain the following optional entries to control its autostart: + + X-TDE-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-TDE-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-TDE-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-TDE-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/tdeinit/README.wrapper b/tdeinit/README.wrapper new file mode 100644 index 000000000..11483d2bc --- /dev/null +++ b/tdeinit/README.wrapper @@ -0,0 +1,38 @@ +README + +tdeinit_wrapper, kshell and kwrapper are a programs that +start programs via tdeinit. + +E.g. You can make a symbolic link from $TDEDIR/bin/konsole to +$TDEDIR/bin/tdeinit_wrapper. Typing 'konsole' on the command line +will then start 'konsole.la' through tdeinit instead. + +tdeinit_wrapper is the simplest for, it only passes the program +and arguments to tdeinit, nothing else + +kshell is usually the best choice, it passes the program, +arguments, complete environment ( $PATH, etc. ) and current +working directory to tdeinit + +kwrapper tries to make the program look like it was actually +really started directly and not via tdeinit. 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 tdeinit 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 tdeinit. - tdeinit 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 + tdeinit was started. - done, I hope it's ok diff --git a/tdeinit/autostart.cpp b/tdeinit/autostart.cpp new file mode 100644 index 000000000..3f49dfc1d --- /dev/null +++ b/tdeinit/autostart.cpp @@ -0,0 +1,310 @@ +/* + * + * 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 <tdeconfig.h> +#include <kdesktopfile.h> +#include <tdeglobal.h> +#include <kstandarddirs.h> + +#include <stdlib.h> + +class AutoStartItem +{ +public: + TQString name; + TQString service; + TQString startAfter; + int phase; +}; + +class AutoStartList: public TQPtrList<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); + TDEGlobal::dirs()->addResourceType("autostart", "share/autostart"); + TQString xdgdirs = getenv("XDG_CONFIG_DIRS"); + if (xdgdirs.isEmpty()) + xdgdirs = "/etc/xdg"; + + TQStringList xdgdirslist = TQStringList::split( ':', xdgdirs ); + for ( TQStringList::Iterator itr = xdgdirslist.begin(); itr != xdgdirslist.end(); ++itr ) { + TDEGlobal::dirs()->addResourceDir("autostart", (*itr) +"/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 TQString extractName(TQString 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 TQString &condition) +{ + if (condition.isEmpty()) + return true; + + TQStringList list = TQStringList::split(':', condition, true); + if (list.count() < 4) + return true; + if (list[0].isEmpty() || list[2].isEmpty()) + return true; + + TDEConfig 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() +{ + TQStringList files = TDEGlobal::dirs()->findAllResources("xdgconf-autostart", "*.desktop", false, true); + TQStringList kdefiles = TDEGlobal::dirs()->findAllResources("autostart", "*.desktop", false, true); + files += kdefiles; + + for(TQStringList::ConstIterator it = files.begin(); + it != files.end(); + ++it) + { + KDesktopFile config(*it, true); + if (config.hasKey("X-TDE-autostart-condition")) { + if (!startCondition(config.readEntry("X-TDE-autostart-condition"))) + continue; + } + else { + if (!startCondition(config.readEntry("X-TDE-autostart-condition"))) + continue; + } + if (!config.tryExec()) + continue; + if (config.readBoolEntry("Hidden", false)) + continue; + + // Check to see if the most important ( usually ~/.config/autostart or ~/.trinity/Autostart) XDG directory + // has overridden the Hidden directive and honor it if set to True + bool autostartOverriddenAndDisabled = false; + for(TQStringList::ConstIterator localit = files.begin(); + localit != files.end(); + ++localit) + { + if (((*localit).startsWith(TDEGlobal::dirs()->localxdgconfdir()) == true) || ((*localit).startsWith(TDEGlobal::dirs()->localtdedir()) == true)) { + // Same local file name? + TQString localOuter; + TQString localInner; + int slashPos = (*it).findRev( '/', -1, TRUE ); + if (slashPos == -1) { + localOuter = (*it); + } + else { + localOuter = (*it).mid(slashPos+1); + } + slashPos = (*localit).findRev( '/', -1, TRUE ); + if (slashPos == -1) { + localInner = (*localit); + } + else { + localInner = (*localit).mid(slashPos+1); + } + if (localOuter == localInner) { + // Overridden! + // But is Hidden == True? + KDesktopFile innerConfig(*localit, true); + if (innerConfig.readBoolEntry("Hidden", false)) { + // Override confirmed; exit speedily without autostarting + autostartOverriddenAndDisabled = true; + } + } + } + } + + if (autostartOverriddenAndDisabled == true) + continue; + + if (config.hasKey("OnlyShowIn")) + { +#ifdef WITH_OLD_XDG_STD + if ((!config.readListEntry("OnlyShowIn", ';').contains("TDE")) && (!config.readListEntry("OnlyShowIn", ';').contains("KDE"))) + continue; +#else + if (!config.readListEntry("OnlyShowIn", ';').contains("TDE")) + continue; +#endif + } + if (config.hasKey("NotShowIn")) + { +#ifdef WITH_OLD_XDG_STD + if ((config.readListEntry("NotShowIn", ';').contains("TDE")) || (config.readListEntry("NotShowIn", ';').contains("KDE"))) + continue; +#else + if (config.readListEntry("NotShowIn", ';').contains("TDE")) + continue; +#endif + } + + AutoStartItem *item = new AutoStartItem; + item->name = extractName(*it); + item->service = *it; + if (config.hasKey("X-TDE-autostart-after")) + item->startAfter = config.readEntry("X-TDE-autostart-after"); + else + item->startAfter = config.readEntry("X-TDE-autostart-after"); + if( m_newStartup ) + { + if (config.hasKey("X-TDE-autostart-phase")) + item->phase = config.readNumEntry("X-TDE-autostart-phase", 2); + else + item->phase = config.readNumEntry("X-TDE-autostart-phase", 2); + if (item->phase < 0) + item->phase = 0; + } + else + { + if (config.hasKey("X-TDE-autostart-phase")) + item->phase = config.readNumEntry("X-TDE-autostart-phase", 1); + else + item->phase = config.readNumEntry("X-TDE-autostart-phase", 1); + if (item->phase < 1) + item->phase = 1; + } + m_startList->append(item); + } + + // Check for duplicate entries and remove if found + TQPtrListIterator<AutoStartItem> it1(*m_startList); + TQPtrListIterator<AutoStartItem> it2(*m_startList); + AutoStartItem *item1; + AutoStartItem *item2; + while ((item1 = it1.current()) != 0) { + bool dupfound1 = false; + it2.toFirst(); + while ((item2 = it2.current()) != 0) { + bool dupfound2 = false; + if (item2 != item1) { + if (item1->service == item2->service) { + m_startList->removeRef(item2); + dupfound1 = true; + dupfound2 = true; + } + } + if (!dupfound2) { + ++it2; + } + } + if (!dupfound1) { + ++it1; + } + } +} + +TQString +AutoStart::startService() +{ + if (m_startList->isEmpty()) + return 0; + + while(!m_started.isEmpty()) + { + + // Check for items that depend on previously started items + TQString 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); + TQString 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); + TQString 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); + TQString service = item->service; + m_startList->remove(); + return service; + } + } + + return 0; +} diff --git a/tdeinit/autostart.h b/tdeinit/autostart.h new file mode 100644 index 000000000..ce2efe2da --- /dev/null +++ b/tdeinit/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 <tqstringlist.h> + +class AutoStartList; + +class AutoStart +{ +public: + AutoStart( bool new_startup ); + ~AutoStart(); + + void loadAutoStartList(); + TQString 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; + TQStringList m_started; + int m_phase; + bool m_phasedone; +}; + +#endif diff --git a/tdeinit/configure.in.in b/tdeinit/configure.in.in new file mode 100644 index 000000000..1d14ee592 --- /dev/null +++ b/tdeinit/configure.in.in @@ -0,0 +1,59 @@ +TDEINIT_USE_XFT=1 +dnl check if Qt is linked against Xft +KDE_CHECK_LIB(qt-mt,XftInit,[],[TDEINIT_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`" + TDEINIT_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`" + TDEINIT_FONTCONFIG=1 + fi +fi + +KDE_FIND_PATH(freetype-config, TDEINIT_FREETYPE_CONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/bin /usr/local/bin /opt/local/bin]) +if test -n "$TDEINIT_FREETYPE_CONFIG"; then + TDEINIT_XFT_INCLUDES="`$TDEINIT_FREETYPE_CONFIG --cflags` $FONTCONFIG_CFLAGS" + tdeinit_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $TDEINIT_XFT_INCLUDES $FONTCONFIG_CFLAGS" + KDE_CHECK_HEADER(X11/Xft/Xft.h,[],[TDEINIT_USE_XFT=]) + CPPFLAGS=$tdeinit_cppflags +else + TDEINIT_USE_XFT= +fi + +if test -n "$TDEINIT_USE_XFT"; then + AC_DEFINE(TDEINIT_USE_XFT,1,[Use Xft preinitialization in tdeinit]) +fi +if test -n "$TDEINIT_FONTCONFIG"; then + AC_DEFINE(TDEINIT_USE_FONTCONFIG,1,[Use FontConfig in tdeinit]) +fi +AC_SUBST(TDEINIT_XFT_INCLUDES) + +AC_MSG_CHECKING(whether to make tdeinit setuid root in order to protect it from bad Linux OOM-killer) +tdeinit_setuid= +case $target_os in + linux*) + AC_MSG_RESULT(yes) + TDEINIT_SETUID=1 + AC_DEFINE(TDEINIT_OOM_PROTECT,1,[Enable prevention against poor Linux OOM-killer]) + ;; + *) AC_MSG_RESULT(no) + TDEINIT_SETUID=0 + ;; +esac +AC_SUBST(TDEINIT_SETUID) diff --git a/tdeinit/kwrapper.c b/tdeinit/kwrapper.c new file mode 100644 index 000000000..68f27a57a --- /dev/null +++ b/tdeinit/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/tdeinit/lnusertemp.c b/tdeinit/lnusertemp.c new file mode 100644 index 000000000..171107041 --- /dev/null +++ b/tdeinit/lnusertemp.c @@ -0,0 +1,294 @@ +/* + 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 kdehostname); + +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, "[lnusertemp] 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, "[lnusertemp] Error: \"%s\" is not a directory.\n", tmp_dir); + return 1; + } + + if (stat_buf.st_uid != getuid()) + { + fprintf(stderr, "[lnusertemp] 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, "[lnusertemp] Error: Can not create link from \"%s\" to \"%s\"\n", file, tmp_dir); + return 1; + } + /*printf("[lnusertemp] Created link from \"%s\" to \"%s\"\n", file, tmp_dir);*/ + return 0; +} + + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif +int build_link(const char *tmp_prefix, const char *kde_prefix, int kdehostname) +{ + 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("TDEHOME") : getenv("TDEROOTHOME"); + int result; + struct stat stat_buf; + + kde_tmp_dir[0] = 0; + + pw_ent = getpwuid(uid); + if (!pw_ent) + { + fprintf(stderr, "[lnusertemp] 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 = "~/.trinity/"; + } + + 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, "[lnusertemp] Aborting. $HOME not set!\n"); + return 1; + } + if (strlen(home_dir) > (PATH_MAX-100)) + { + fprintf(stderr, "[lnusertemp] 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("[lnusertemp] mkdir failed: "); + return 1; + } + + strncat(kde_tmp_dir, kde_prefix, PATH_MAX - strlen(kde_tmp_dir)); + + if( kdehostname ) + { + if( getenv("XAUTHLOCALHOSTNAME")) + strncat(kde_tmp_dir+strlen(kde_tmp_dir), getenv("XAUTHLOCALHOSTNAME"), PATH_MAX - strlen(kde_tmp_dir) - 1); + else + return 0; + } + else + { + if (gethostname(kde_tmp_dir+strlen(kde_tmp_dir), PATH_MAX - strlen(kde_tmp_dir) - 1) != 0) + { + perror("[lnusertemp] Could not determine hostname: "); + return 1; + } + } + 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))) + { + /* $TDEHOME/tmp is a normal directory. Do nothing. */ + /*printf("[lnusertemp] Directory \"%s\" already exists.\n", kde_tmp_dir);*/ + return 0; + } + if ((result == -1) && (errno == ENOENT)) + { + /*printf("[lnusertemp] 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, "[lnusertemp] 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, "[lnusertemp] Error: \"%s\" could not be read.\n", kde_tmp_dir); + return 1; + } + tmp_buf[result] = '\0'; + /*printf("[lnusertemp] Link \"%s\" points to \"%s\"\n", kde_tmp_dir, tmp_buf);*/ + if (strncmp(tmp_buf, user_tmp_dir, strlen(user_tmp_dir)) != 0) + { + fprintf(stderr, "[lnusertemp] Error: \"%s\" points to \"%s\" instead of \"%s\".\n", kde_tmp_dir, tmp_buf, user_tmp_dir); + unlink(kde_tmp_dir); + /*printf("[lnusertemp] 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, "[lnusertemp] Usage: lnusertemp tmp|socket|cache\n"); + return 1; + } + + tmp = getenv("TDETMP"); + if (!tmp || !tmp[0]) + tmp = getenv("TMPDIR"); + if (!tmp || !tmp[0]) + tmp = getenv("TEMP"); + if (!tmp || !tmp[0]) + tmp = getenv("TMP"); + if (!tmp || !tmp[0]) + tmp = "/tmp"; + + if (strcmp(argv[1], "tmp") == 0) + { + tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/tde-")+1); + strcpy(tmp_prefix, tmp); + strcat(tmp_prefix, "/tde-"); + + kde_prefix = "/tmp-"; + } + else if (strcmp(argv[1], "socket") == 0) + { + tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/tdesocket-")+1); + strcpy(tmp_prefix, tmp ); + strcat(tmp_prefix, "/tdesocket-" ); + + kde_prefix = "/socket-"; + } + else if (strcmp(argv[1], "cache") == 0) + { + tmp = getenv("TDEVARTMP"); + if (!tmp || !tmp[0]) + tmp = "/var/tmp"; + + tmp_prefix = (char *)malloc(strlen(tmp)+strlen("/tdecache-")+1); + strcpy(tmp_prefix, tmp ); + strcat(tmp_prefix, "/tdecache-" ); + + kde_prefix = "/cache-"; + } + + res = build_link(tmp_prefix, kde_prefix, 1); + if( build_link(tmp_prefix, kde_prefix, 0)) + res = 1; + + free(tmp_prefix); + + return res; +} diff --git a/tdeinit/setproctitle.cpp b/tdeinit/setproctitle.cpp new file mode 100644 index 000000000..e6c85561c --- /dev/null +++ b/tdeinit/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 +tdeinit_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 +tdeinit_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/tdeinit/setproctitle.h b/tdeinit/setproctitle.h new file mode 100644 index 000000000..7a134e73e --- /dev/null +++ b/tdeinit/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 tdeinit_initsetproctitle(int, char **, char **); +extern void tdeinit_setproctitle(const char *, ...) +#ifdef __GNUC__ + __attribute__ (( format ( printf, 1, 2 ) ) ) +#endif +; + +#endif + diff --git a/tdeinit/shell.c b/tdeinit/shell.c new file mode 100644 index 000000000..62bcb3cc7 --- /dev/null +++ b/tdeinit/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/tdeinit/start_tdeinit.c b/tdeinit/start_tdeinit.c new file mode 100644 index 000000000..e46aaa49f --- /dev/null +++ b/tdeinit/start_tdeinit.c @@ -0,0 +1,191 @@ +/* + * 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 TDEINIT_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 tdeinit), opens a pipe and forks. Child drops privileges + and launches tdeinit. Since processes started by tdeinit should + not have this protection, tdeinit 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; + struct stat st; + + /* Newer kernels (noticed in 2.6.36) */ + sprintf( buf, "/proc/%d/oom_score_adj", pid ); + if ( lstat (buf, &st) == 0) { + if( !enable ) { + /* Be paranoid and check that the pid we got from the pipe + belongs to this user. */ + if( st.st_uid != getuid()) + return 0; + } + procfile = open(buf, O_WRONLY); + if( enable ) + write( procfile, "-300", sizeof( "-300" )); + else + write( procfile, "0", sizeof( "0" )); + close( procfile ); + return 1; + } + + sprintf( buf, "/proc/%d/stat", pid ); + if( !enable ) { + /* Be paranoid and check that the pid we got from the pipe + belongs to this user. */ + 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_tdeinit_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/tdeinit/start_tdeinit_wrapper.c b/tdeinit/start_tdeinit_wrapper.c new file mode 100644 index 000000000..0328f3e43 --- /dev/null +++ b/tdeinit/start_tdeinit_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 TDEINIT_OOM_PROTECT + +/* + The start_tdeinit wrapper is setuid, which means some shell variables like LD_LIBRARY_PATH + get unset before it's launched. However tdeinit 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_tdeinit's stdin, which after dropping priviledges reads it and uses it + for launching the real tdeinit. +*/ +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_tdeinit"; + execvp("start_tdeinit", argv); + perror("start_tdeinit"); + 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_tdeinit"; + execvp("start_tdeinit",argv); + perror("start_tdeinit"); + return 1; +} +#endif diff --git a/tdeinit/tdedostartupconfig.cpp b/tdeinit/tdedostartupconfig.cpp new file mode 100644 index 000000000..bbfdf88a4 --- /dev/null +++ b/tdeinit/tdedostartupconfig.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 tdestartupconfig.cpp . + +#include <tqfile.h> +#include <tqtextstream.h> +#include <kinstance.h> +#include <kstandarddirs.h> +#include <tdeconfig.h> +#include <kdebug.h> + +TQString get_entry( TQString* ll ) + { + TQString& l = *ll; + l = l.stripWhiteSpace(); + if( l.isEmpty()) + return TQString::null; + TQString ret; + if( l[ 0 ] == '\'' ) + { + unsigned int pos = 1; + while( pos < l.length() && l[ pos ] != '\'' ) + ret += l[ pos++ ]; + if( pos >= l.length()) + { + *ll = TQString::null; + return TQString::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() + { + TDEInstance inst( "tdedostartupconfig" ); + kdDebug() << "Running tdedostartupconfig." << endl; + TQString keysname = locateLocal( "config", "startupconfigkeys" ); + TQFile keys( keysname ); + if( !keys.open( IO_ReadOnly )) + return 3; + TQFile f1( locateLocal( "config", "startupconfig" )); + if( !f1.open( IO_WriteOnly )) + return 4; + TQFile f2( locateLocal( "config", "startupconfigfiles" )); + if( !f2.open( IO_WriteOnly )) + return 5; + TQTextStream startupconfig( &f1 ); + TQTextStream startupconfigfiles( &f2 ); + startupconfig << "#! /bin/sh\n"; + for(;;) + { + TQString line; + if( keys.readLine( line, 1024 ) < 0 ) + break; + line = line.stripWhiteSpace(); + if( line.isEmpty()) + break; + TQString tmp = line; + TQString 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 + TDEConfig cfg( file ); + group = group.mid( 1, group.length() - 2 ); + TQMap< TQString, TQString > entries = cfg.entryMap( group ); + startupconfig << "# " << line << "\n"; + for( TQMap< TQString, TQString >::ConstIterator it = entries.begin(); + it != entries.end(); + ++it ) + { + TQString key = it.key(); + TQString value = *it; + startupconfig << TQString(file.replace( ' ', '_' )).lower() + << "_" << TQString(group.replace( ' ', '_' )).lower() + << "_" << TQString(key.replace( ' ', '_' )).lower() + << "=\"" << value.replace( "\"", "\\\"" ) << "\"\n"; + } + } + else + { // a single key + if( key.isEmpty()) + return 7; + TDEConfig cfg( file ); + cfg.setGroup( group ); + TQString value = cfg.readEntry( key, def ); + startupconfig << "# " << line << "\n"; + startupconfig << TQString(file.replace( ' ', '_' )).lower() + << "_" << TQString(group.replace( ' ', '_' )).lower() + << "_" <<TQString( key.replace( ' ', '_' )).lower() + << "=\"" << value.replace( "\"", "\\\"" ) << "\"\n"; + } + startupconfigfiles << line << endl; + // use even currently non-existing paths in $TDEDIRS + TQStringList dirs = TQStringList::split( KPATH_SEPARATOR, TDEGlobal::dirs()->kfsstnd_prefixes()); + for( TQStringList::ConstIterator it = dirs.begin(); + it != dirs.end(); + ++it ) + { + TQString cfg = *it + "share/config/" + file; + if( TDEStandardDirs::exists( cfg )) + startupconfigfiles << cfg << "\n"; + else + startupconfigfiles << "!" << cfg << "\n"; + } + startupconfigfiles << "*\n"; + } + return 0; + } diff --git a/tdeinit/tdeinit.cpp b/tdeinit/tdeinit.cpp new file mode 100644 index 000000000..ca0d1dcaf --- /dev/null +++ b/tdeinit/tdeinit.cpp @@ -0,0 +1,1913 @@ +/* + * This file is part of the KDE libraries + * Copyright (c) 1999-2000 Waldo Bastian <bastian@kde.org> + * (c) 1999 Mario Weilguni <mweilguni@sime.com> + * (c) 2001 Lubos Lunak <l.lunak@kde.org> + * + * $Id$ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include <config.h> + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/wait.h> +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> // Needed on some systems. +#endif + +#include <errno.h> +#include <fcntl.h> +#include <setproctitle.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <locale.h> + +#include <tqstring.h> +#include <tqfile.h> +#include <tqdatetime.h> +#include <tqfileinfo.h> +#include <tqtextstream.h> +#include <tqregexp.h> +#include <tqfont.h> +#include <kinstance.h> +#include <kstandarddirs.h> +#include <tdeglobal.h> +#include <tdeconfig.h> +#include <klibloader.h> +#include <tdeapplication.h> +#include <tdelocale.h> + +#ifdef HAVE_SYS_PRCTL_H +#include <sys/prctl.h> +#ifndef PR_SET_NAME +#define PR_SET_NAME 15 +#endif +#endif + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <tdestartupinfo.h> // schroder +#endif + +#include <tdeversion.h> + +#include "ltdl.h" +#include "tdelauncher_cmds.h" + +//#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#ifdef Q_WS_X11 +//#undef K_WS_QTONLY +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#endif + +#ifdef HAVE_DLFCN_H +# include <dlfcn.h> +#endif + +#ifdef RTLD_GLOBAL +# define LTDL_GLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LTDL_GLOBAL DL_GLOBAL +# else +# define LTDL_GLOBAL 0 +# endif +#endif + +#if defined(TDEINIT_USE_XFT) && defined(TDEINIT_USE_FONTCONFIG) +#include <X11/Xft/Xft.h> +extern "C" FcBool XftInitFtLibrary (void); +#include <fontconfig/fontconfig.h> +#endif + +extern char **environ; + +extern int lt_dlopen_flag; +//#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#ifdef Q_WS_X11 +static int X11fd = -1; +static Display *X11display = 0; +static int X11_startup_notify_fd = -1; +static Display *X11_startup_notify_display = 0; +#endif +static const TDEInstance *s_instance = 0; +#define MAX_SOCK_FILE 255 +static char sock_file[MAX_SOCK_FILE]; +static char sock_file_old[MAX_SOCK_FILE]; + +//#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#ifdef Q_WS_X11 +#define DISPLAY "DISPLAY" +#elif defined(Q_WS_QWS) +#define DISPLAY "QWS_DISPLAY" +#elif defined(Q_WS_MACX) +#define DISPLAY "MAC_DISPLAY" +#elif defined(K_WS_QTONLY) +#define DISPLAY "QT_DISPLAY" +#else +#error Use QT/X11 or QT/Embedded +#endif + +/* Group data */ +static struct { + int maxname; + int fd[2]; + int launcher[2]; /* socket pair for launcher communication */ + int deadpipe[2]; /* pipe used to detect dead children */ + int initpipe[2]; + int wrapper; /* socket for wrapper communication */ + int wrapper_old; /* old socket for wrapper communication */ + char result; + int exit_status; + pid_t fork; + pid_t launcher_pid; + pid_t my_pid; + int n; + lt_dlhandle handle; + lt_ptr sym; + char **argv; + int (*func)(int, char *[]); + int (*launcher_func)(int); + bool debug_wait; + int lt_dlopen_flag; + TQCString errorMsg; + bool launcher_ok; + bool suicide; +} d; + +//#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#ifdef Q_WS_X11 +extern "C" { +int tdeinit_xio_errhandler( Display * ); +int tdeinit_x_errhandler( Display *, XErrorEvent *err ); +} +#endif + +/* These are to link libtdeparts even if 'smart' linker is used */ +#include <tdeparts/plugin.h> +extern "C" KParts::Plugin* _tdeinit_init_tdeparts() { return new KParts::Plugin(); } +/* These are to link libtdeio even if 'smart' linker is used */ +#include <tdeio/authinfo.h> +extern "C" TDEIO::AuthInfo* _tdeioslave_init_kio() { return new TDEIO::AuthInfo(); } + +/* + * Close fd's which are only useful for the parent process. + * Restore default signal handlers. + */ +static void close_fds() +{ + if (d.deadpipe[0] != -1) + { + close(d.deadpipe[0]); + d.deadpipe[0] = -1; + } + + if (d.deadpipe[1] != -1) + { + close(d.deadpipe[1]); + d.deadpipe[1] = -1; + } + + if (d.initpipe[0] != -1) + { + close(d.initpipe[0]); + d.initpipe[0] = -1; + } + + if (d.initpipe[1] != -1) + { + close(d.initpipe[1]); + d.initpipe[1] = -1; + } + + if (d.launcher_pid) + { + close(d.launcher[0]); + d.launcher_pid = 0; + } + if (d.wrapper) + { + close(d.wrapper); + d.wrapper = 0; + } + if (d.wrapper_old) + { + close(d.wrapper_old); + d.wrapper_old = 0; + } +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + if (X11fd >= 0) + { + close(X11fd); + X11fd = -1; + } + if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd ) + { + close(X11_startup_notify_fd); + X11_startup_notify_fd = -1; + } +#endif + + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +} + +static void exitWithErrorMsg(const TQString &errorMsg) +{ + fprintf( stderr, "[tdeinit] %s\n", errorMsg.local8Bit().data() ); + TQCString utf8ErrorMsg = errorMsg.utf8(); + d.result = 3; // Error with msg + write(d.fd[1], &d.result, 1); + int l = utf8ErrorMsg.length(); + write(d.fd[1], &l, sizeof(int)); + write(d.fd[1], utf8ErrorMsg.data(), l); + close(d.fd[1]); + exit(255); +} + +static void setup_tty( const char* tty ) +{ + if( tty == NULL || *tty == '\0' ) + return; + int fd = open( tty, O_WRONLY ); + if( fd < 0 ) + { + fprintf(stderr, "[tdeinit] couldn't open() %s: %s\n", tty, strerror (errno) ); + return; + } + if( dup2( fd, STDOUT_FILENO ) < 0 ) + { + fprintf(stderr, "[tdeinit] couldn't dup2() %s: %s\n", tty, strerror (errno) ); + close( fd ); + return; + } + if( dup2( fd, STDERR_FILENO ) < 0 ) + { + fprintf(stderr, "[tdeinit] couldn't dup2() %s: %s\n", tty, strerror (errno) ); + close( fd ); + return; + } + close( fd ); +} + +// from tdecore/netwm.cpp +static int get_current_desktop( Display* disp ) +{ + int desktop = 0; // no desktop by default +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 // Only X11 supports multiple desktops + Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP", False ); + Atom type_ret; + int format_ret; + unsigned char *data_ret; + unsigned long nitems_ret, unused; + if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop, + 0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret ) + == Success) + { + if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) + desktop = *((long *) data_ret) + 1; + if (data_ret) + XFree ((char*) data_ret); + } +#endif + return desktop; +} + +// var has to be e.g. "DISPLAY=", i.e. with = +const char* get_env_var( const char* var, int envc, const char* envs ) +{ + if( envc > 0 ) + { // get the var from envs + const char* env_l = envs; + int ln = strlen( var ); + for (int i = 0; i < envc; i++) + { + if( strncmp( env_l, var, ln ) == 0 ) + return env_l + ln; + while(*env_l != 0) env_l++; + env_l++; + } + } + return NULL; +} + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 // FIXME(E): Implement for Qt/Embedded +static void init_startup_info( TDEStartupInfoId& 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 ); + TDEStartupInfoData data; + int desktop = get_current_desktop( X11_startup_notify_display ); + data.setDesktop( desktop ); + data.setBin( bin ); + TDEStartupInfo::sendChangeX( X11_startup_notify_display, id, data ); + XFlush( X11_startup_notify_display ); +} + +static void complete_startup_info( TDEStartupInfoId& id, pid_t pid ) +{ + if( X11_startup_notify_display == NULL ) + return; + if( pid == 0 ) // failure + TDEStartupInfo::sendFinishX( X11_startup_notify_display, id ); + else + { + TDEStartupInfoData data; + data.addPid( pid ); + data.setHostname(); + TDEStartupInfo::sendChangeX( X11_startup_notify_display, id, data ); + } + XCloseDisplay( X11_startup_notify_display ); + X11_startup_notify_display = NULL; + X11_startup_notify_fd = -1; +} +#endif + +TQCString execpath_avoid_loops( const TQCString& exec, int envc, const char* envs, bool avoid_loops ) +{ + TQStringList paths; + if( envc > 0 ) /* use the passed environment */ + { + const char* path = get_env_var( "PATH=", envc, envs ); + if( path != NULL ) + paths = TQStringList::split( TQRegExp( "[:\b]" ), path, true ); + } + else + paths = TQStringList::split( TQRegExp( "[:\b]" ), getenv( "PATH" ), true ); + TQCString execpath = TQFile::encodeName( + s_instance->dirs()->findExe( exec, paths.join( TQString( ":" )))); + if( avoid_loops && !execpath.isEmpty()) + { + int pos = execpath.findRev( '/' ); + TQString bin_path = execpath.left( pos ); + for( TQStringList::Iterator it = paths.begin(); + it != paths.end(); + ++it ) + if( ( *it ) == bin_path || ( *it ) == bin_path + '/' ) + { + paths.remove( it ); + break; // --> + } + execpath = TQFile::encodeName( + s_instance->dirs()->findExe( exec, paths.join( TQString( ":" )))); + } + return execpath; +} + +#ifdef TDEINIT_OOM_PROTECT +static int oom_pipe = -1; + +static void oom_protect_sighandler( int ) { +} + +static void reset_oom_protect() { + if( oom_pipe <= 0 ) + return; + struct sigaction act, oldact; + act.sa_handler = oom_protect_sighandler; + act.sa_flags = 0; + sigemptyset( &act.sa_mask ); + sigaction( SIGUSR1, &act, &oldact ); + sigset_t sigs, oldsigs; + sigemptyset( &sigs ); + sigaddset( &sigs, SIGUSR1 ); + sigprocmask( SIG_BLOCK, &sigs, &oldsigs ); + pid_t pid = getpid(); + if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) { + sigsuspend( &oldsigs ); // wait for the signal to come + } + sigprocmask( SIG_SETMASK, &oldsigs, NULL ); + sigaction( SIGUSR1, &oldact, NULL ); + close( oom_pipe ); + oom_pipe = -1; +} +#else +static void reset_oom_protect() { +} +#endif + +static pid_t launch(int argc, const char *_name, const char *args, + const char *cwd=0, int envc=0, const char *envs=0, + bool reset_env = false, + const char *tty=0, bool avoid_loops = false, + const char* startup_id_str = "0" ) +{ + int launcher = 0; + TQCString lib; + TQCString name; + TQCString exec; + + if (strcmp(_name, "tdelauncher") == 0) { + /* tdelauncher is launched in a special way: + * It has a communication socket on LAUNCHER_FD + */ + if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher)) + { + perror("[tdeinit] socketpair() failed!\n"); + exit(255); + } + launcher = 1; + } + + TQCString libpath; + TQCString execpath; + if (_name[0] != '/') + { + /* Relative name without '.la' */ + name = _name; + lib = name + ".la"; + exec = name; + libpath = TQFile::encodeName(KLibLoader::findLibrary( lib, s_instance )); + execpath = execpath_avoid_loops( exec, envc, envs, avoid_loops ); + } + else + { + lib = _name; + name = _name; + name = name.mid( name.findRev('/') + 1); + exec = _name; + if (lib.right(3) == ".la") + libpath = lib; + else + execpath = exec; + } + if (!args) + { + argc = 1; + } + + if (0 > pipe(d.fd)) + { + perror("[tdeinit] pipe() failed!\n"); + d.result = 3; + d.errorMsg = i18n("Unable to start new process.\n" + "The system may have reached the maximum number of open files possible or the maximum number of open files that you are allowed to use has been reached.").utf8(); + close(d.fd[0]); + close(d.fd[1]); + d.fork = 0; + return d.fork; + } + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + TDEStartupInfoId startup_id; + startup_id.initId( startup_id_str ); + if( !startup_id.none()) + init_startup_info( startup_id, name, envc, envs ); +#endif + + d.errorMsg = 0; + d.fork = fork(); + switch(d.fork) { + case -1: + perror("[tdeinit] fork() failed!\n"); + d.result = 3; + d.errorMsg = i18n("Unable to create new process.\n" + "The system may have reached the maximum number of processes possible or the maximum number of processes that you are allowed to use has been reached.").utf8(); + close(d.fd[0]); + close(d.fd[1]); + d.fork = 0; + break; + case 0: + /** Child **/ + close(d.fd[0]); + close_fds(); + if (launcher) + { + if (d.fd[1] == LAUNCHER_FD) + { + d.fd[1] = dup(d.fd[1]); // Evacuate from LAUNCHER_FD + } + if (d.launcher[1] != LAUNCHER_FD) + { + dup2( d.launcher[1], LAUNCHER_FD); // Make sure the socket has fd LAUNCHER_FD + close( d.launcher[1] ); + } + close( d.launcher[0] ); + } + reset_oom_protect(); + + if (cwd && *cwd) + chdir(cwd); + + if( reset_env ) // KWRAPPER/SHELL + { + + TQStrList unset_envs; + for( int tmp_env_count = 0; + environ[tmp_env_count]; + tmp_env_count++) + unset_envs.append( environ[ tmp_env_count ] ); + for( TQStrListIterator it( unset_envs ); + it.current() != NULL ; + ++it ) + { + TQCString tmp( it.current()); + int pos = tmp.find( '=' ); + if( pos >= 0 ) + unsetenv( tmp.left( pos )); + } + } + + for (int i = 0; i < envc; i++) + { + putenv((char *)envs); + while(*envs != 0) envs++; + envs++; + } + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + if( startup_id.none()) + TDEStartupInfo::resetStartupEnv(); + else + startup_id.setupStartupEnv(); +#endif + { + int r; + TQCString procTitle; + d.argv = (char **) malloc(sizeof(char *) * (argc+1)); + d.argv[0] = (char *) _name; + for (int i = 1; i < argc; i++) + { + d.argv[i] = (char *) args; + procTitle += " "; + procTitle += (char *) args; + while(*args != 0) args++; + args++; + } + d.argv[argc] = 0; + + /** Give the process a new name **/ +#ifdef HAVE_SYS_PRCTL_H + /* set the process name, so that killall works like intended */ + r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0); + if ( r == 0 ) + tdeinit_setproctitle( "%s [tdeinit]%s", name.data(), procTitle.data() ? procTitle.data() : "" ); + else + tdeinit_setproctitle( "[tdeinit] %s%s", name.data(), procTitle.data() ? procTitle.data() : "" ); +#else + tdeinit_setproctitle( "[tdeinit] %s%s", name.data(), procTitle.data() ? procTitle.data() : "" ); +#endif + } + + d.handle = 0; + if (libpath.isEmpty() && execpath.isEmpty()) + { + TQString errorMsg = i18n("Could not find '%1' executable.").arg(TQFile::decodeName(_name)); + exitWithErrorMsg(errorMsg); + } + + if ( getenv("TDE_IS_PRELINKED") && !execpath.isEmpty() && !launcher) + libpath.truncate(0); + + if ( !libpath.isEmpty() ) + { + d.handle = lt_dlopen( TQFile::encodeName(libpath) ); + if (!d.handle ) + { + const char * ltdlError = lt_dlerror(); + if (execpath.isEmpty()) + { + // Error + TQString errorMsg = i18n("Could not open library '%1'.\n%2").arg(TQFile::decodeName(libpath)) + .arg(ltdlError ? TQFile::decodeName(ltdlError) : i18n("Unknown error")); + exitWithErrorMsg(errorMsg); + } + else + { + // Print warning + fprintf(stderr, "Could not open library %s: %s\n", lib.data(), ltdlError != 0 ? ltdlError : "(null)" ); + } + } + } + lt_dlopen_flag = d.lt_dlopen_flag; + if (!d.handle ) + { + d.result = 2; // Try execing + write(d.fd[1], &d.result, 1); + + // We set the close on exec flag. + // Closing of d.fd[1] indicates that the execvp succeeded! + fcntl(d.fd[1], F_SETFD, FD_CLOEXEC); + + setup_tty( tty ); + + execvp(execpath.data(), d.argv); + d.result = 1; // Error + write(d.fd[1], &d.result, 1); + close(d.fd[1]); + exit(255); + } + + d.sym = lt_dlsym( d.handle, "tdeinitmain"); + if (!d.sym ) + { + d.sym = lt_dlsym( d.handle, "kdemain" ); + if ( !d.sym ) + { +#if ! KDE_IS_VERSION( 3, 90, 0 ) + d.sym = lt_dlsym( d.handle, "main"); +#endif + if (!d.sym ) + { + const char * ltdlError = lt_dlerror(); + fprintf(stderr, "Could not find kdemain: %s\n", ltdlError != 0 ? ltdlError : "(null)" ); + TQString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2").arg(TQString(libpath)) + .arg(ltdlError ? TQFile::decodeName(ltdlError) : i18n("Unknown error")); + exitWithErrorMsg(errorMsg); + } + } + } + + d.result = 0; // Success + write(d.fd[1], &d.result, 1); + close(d.fd[1]); + + d.func = (int (*)(int, char *[])) d.sym; + if (d.debug_wait) + { + fprintf(stderr, "[tdeinit] Suspending process\n" + "[tdeinit] 'gdb tdeinit %d' to debug\n" + "[tdeinit] 'kill -SIGCONT %d' to continue\n", + getpid(), getpid()); + kill(getpid(), SIGSTOP); + } + else + { + setup_tty( tty ); + } + + exit( d.func(argc, d.argv)); /* Launch! */ + + break; + default: + /** Parent **/ + close(d.fd[1]); + if (launcher) + { + close(d.launcher[1]); + d.launcher_pid = d.fork; + } + bool exec = false; + for(;;) + { + d.n = read(d.fd[0], &d.result, 1); + if (d.n == 1) + { + if (d.result == 2) + { +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] %s is executable. Launching with exec.\n", _name ); +#endif + exec = true; + continue; + } + if (d.result == 3) + { + int l = 0; + d.n = read(d.fd[0], &l, sizeof(int)); + if (d.n == sizeof(int)) + { + TQCString tmp; + tmp.resize(l+1); + d.n = read(d.fd[0], tmp.data(), l); + tmp[l] = 0; + if (d.n == l) + d.errorMsg = tmp; + } + } + // Finished + break; + } + if (d.n == -1) + { + if (errno == ECHILD) { // a child died. + continue; + } + if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read + continue; + } + } + if (exec) + { + d.result = 0; + break; + } + if (d.n == 0) + { + perror("[tdeinit] Pipe closed unexpectedly"); + d.result = 1; // Error + break; + } + perror("[tdeinit] Error reading from pipe"); + d.result = 1; // Error + break; + } + close(d.fd[0]); + if (launcher && (d.result == 0)) + { + // Trader launched successful + d.launcher_pid = d.fork; + } + } +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + if( !startup_id.none()) + { + if( d.fork && d.result == 0 ) // launched successfully + complete_startup_info( startup_id, d.fork ); + else // failure, cancel ASN + complete_startup_info( startup_id, 0 ); + } +#endif + return d.fork; +} + +static void sig_child_handler(int) +{ + /* + * Write into the pipe of death. + * This way we are sure that we return from the select() + * + * A signal itself causes select to return as well, but + * this creates a race-condition in case the signal arrives + * just before we enter the select. + */ + char c = 0; + write(d.deadpipe[1], &c, 1); +} + +static void init_signals() +{ + struct sigaction act; + long options; + + if (pipe(d.deadpipe) != 0) + { + perror("[tdeinit] Aborting. Can't create pipe: "); + exit(255); + } + + options = fcntl(d.deadpipe[0], F_GETFL); + if (options == -1) + { + perror("[tdeinit] Aborting. Can't make pipe non-blocking: "); + exit(255); + } + + if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1) + { + perror("[tdeinit] Aborting. Can't make pipe non-blocking: "); + exit(255); + } + + /* + * A SIGCHLD handler is installed which sends a byte into the + * pipe of death. This is to ensure that a dying child causes + * an exit from select(). + */ + act.sa_handler=sig_child_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGCHLD); + sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L); + act.sa_flags = SA_NOCLDSTOP; + + // CC: take care of SunOS which automatically restarts interrupted system + // calls (and thus does not have SA_RESTART) + +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif + sigaction( SIGCHLD, &act, 0L); + + act.sa_handler=SIG_IGN; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGPIPE); + sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L); + act.sa_flags = 0; + sigaction( SIGPIPE, &act, 0L); +} + +static void init_tdeinit_socket() +{ + struct sockaddr_un sa; + struct sockaddr_un sa_old; + kde_socklen_t socklen; + long options; + const char *home_dir = getenv("HOME"); + int max_tries = 10; + if (!home_dir || !home_dir[0]) + { + fprintf(stderr, "[tdeinit] Aborting. $HOME not set!"); + exit(255); + } + chdir(home_dir); + + { + TQCString path = home_dir; + TQCString readOnly = getenv("TDE_HOME_READONLY"); + if (access(path.data(), R_OK|W_OK)) + { + if (errno == ENOENT) + { + fprintf(stderr, "[tdeinit] Aborting. $HOME directory (%s) does not exist.\n", path.data()); + exit(255); + } + else if (readOnly.isEmpty()) + { + fprintf(stderr, "[tdeinit] Aborting. No write access to $HOME directory (%s).\n", path.data()); + exit(255); + } + } + path = getenv("ICEAUTHORITY"); + if (path.isEmpty()) + { + path = home_dir; + path += "/.ICEauthority"; + } + if (access(path.data(), R_OK|W_OK) && (errno != ENOENT)) + { + fprintf(stderr, "[tdeinit] Aborting. No write access to '%s'.\n", path.data()); + exit(255); + } + } + + /** Test if socket file is already present + * note that access() resolves symlinks, and so we check the actual + * socket file if it exists + */ + if (access(sock_file, W_OK) == 0) + { + int s; + struct sockaddr_un server; + +// fprintf(stderr, "[tdeinit] Warning, socket_file already exists!\n"); + /* + * create the socket stream + */ + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + perror("socket() failed: "); + exit(255); + } + server.sun_family = AF_UNIX; + strcpy(server.sun_path, sock_file); + socklen = sizeof(server); + + if(connect(s, (struct sockaddr *)&server, socklen) == 0) + { + fprintf(stderr, "[tdeinit] Shutting down running client.\n"); + tdelauncher_header request_header; + request_header.cmd = LAUNCHER_TERMINATE_TDEINIT; + request_header.arg_length = 0; + write(s, &request_header, sizeof(request_header)); + sleep(1); // Give it some time + } + close(s); + } + + /** Delete any stale socket file (and symlink) **/ + unlink(sock_file); + unlink(sock_file_old); + + /** create socket **/ + d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0); + if (d.wrapper < 0) + { + perror("[tdeinit] Aborting. socket() failed: "); + exit(255); + } + + options = fcntl(d.wrapper, F_GETFL); + if (options == -1) + { + perror("[tdeinit] Aborting. Can't make socket non-blocking: "); + close(d.wrapper); + exit(255); + } + + if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1) + { + perror("[tdeinit] Aborting. Can't make socket non-blocking: "); + close(d.wrapper); + exit(255); + } + + while (1) { + /** bind it **/ + socklen = sizeof(sa); + memset(&sa, 0, socklen); + sa.sun_family = AF_UNIX; + strcpy(sa.sun_path, sock_file); + if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0) + { + if (max_tries == 0) { + perror("[tdeinit] Aborting. bind() failed: "); + fprintf(stderr, "Could not bind to socket '%s'\n", sock_file); + close(d.wrapper); + exit(255); + } + max_tries--; + } else + break; + } + + /** set permissions **/ + if (chmod(sock_file, 0600) != 0) + { + perror("[tdeinit] Aborting. Can't set permissions on socket: "); + fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file); + unlink(sock_file); + close(d.wrapper); + exit(255); + } + + if(listen(d.wrapper, SOMAXCONN) < 0) + { + perror("[tdeinit] Aborting. listen() failed: "); + unlink(sock_file); + close(d.wrapper); + exit(255); + } + + /** create compatibility socket **/ + d.wrapper_old = socket(PF_UNIX, SOCK_STREAM, 0); + if (d.wrapper_old < 0) + { + // perror("[tdeinit] Aborting. socket() failed: "); + return; + } + + options = fcntl(d.wrapper_old, F_GETFL); + if (options == -1) + { + // perror("[tdeinit] Aborting. Can't make socket non-blocking: "); + close(d.wrapper_old); + d.wrapper_old = 0; + return; + } + + if (fcntl(d.wrapper_old, F_SETFL, options | O_NONBLOCK) == -1) + { + // perror("[tdeinit] Aborting. Can't make socket non-blocking: "); + close(d.wrapper_old); + d.wrapper_old = 0; + return; + } + + max_tries = 10; + while (1) { + /** bind it **/ + socklen = sizeof(sa_old); + memset(&sa_old, 0, socklen); + sa_old.sun_family = AF_UNIX; + strcpy(sa_old.sun_path, sock_file_old); + if(bind(d.wrapper_old, (struct sockaddr *)&sa_old, socklen) != 0) + { + if (max_tries == 0) { + // perror("[tdeinit] Aborting. bind() failed: "); + fprintf(stderr, "Could not bind to socket '%s'\n", sock_file_old); + close(d.wrapper_old); + d.wrapper_old = 0; + return; + } + max_tries--; + } else + break; + } + + /** set permissions **/ + if (chmod(sock_file_old, 0600) != 0) + { + fprintf(stderr, "Wrong permissions of socket '%s'\n", sock_file); + unlink(sock_file_old); + close(d.wrapper_old); + d.wrapper_old = 0; + return; + } + + if(listen(d.wrapper_old, SOMAXCONN) < 0) + { + // perror("[tdeinit] Aborting. listen() failed: "); + unlink(sock_file_old); + close(d.wrapper_old); + d.wrapper_old = 0; + } +} + +/* + * Read 'len' bytes from 'sock' into buffer. + * returns 0 on success, -1 on failure. + */ +static int read_socket(int sock, char *buffer, int len) +{ + ssize_t result; + int bytes_left = len; + while ( bytes_left > 0) + { + result = read(sock, buffer, bytes_left); + if (result > 0) + { + buffer += result; + bytes_left -= result; + } + else if (result == 0) + return -1; + else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) + return -1; + } + return 0; +} + +static void WaitPid( pid_t waitForPid) +{ + int result; + while(1) + { + result = waitpid(waitForPid, &d.exit_status, 0); + if ((result == -1) && (errno == ECHILD)) + return; + } +} + +static void launcher_died() +{ + if (!d.launcher_ok) + { + /* This is bad. */ + fprintf(stderr, "[tdeinit] Communication error with launcher. Exiting!\n"); + ::exit(255); + return; + } + + // TDELauncher died... restart +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] TDELauncher 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, "tdelauncher", 0 ); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Relaunching TDELauncher, 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; + } + + tdelauncher_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; + tdelauncher_header response_header; + long response_data; + long l; + memcpy( &l, request_data, sizeof( long )); + int argc = l; + const char *name = request_data + sizeof(long); + const char *args = name + strlen(name) + 1; + const char *cwd = 0; + int envc = 0; + const char *envs = 0; + const char *tty = 0; + int avoid_loops = 0; + const char *startup_id_str = "0"; + +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Got %s '%s' from %s.\n", + (request_header.cmd == LAUNCHER_EXEC ? "EXEC" : + (request_header.cmd == LAUNCHER_EXT_EXEC ? "EXT_EXEC" : + (request_header.cmd == LAUNCHER_EXEC_NEW ? "EXEC_NEW" : + (request_header.cmd == LAUNCHER_SHELL ? "SHELL" : "KWRAPPER" )))), + name, launcher ? "launcher" : "socket" ); +#endif + + const char *arg_n = args; + for(int i = 1; i < argc; i++) + { + arg_n = arg_n + strlen(arg_n) + 1; + } + + if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER ) + { + // Shell or kwrapper + cwd = arg_n; arg_n += strlen(cwd) + 1; + } + if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER + || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ) + { + memcpy( &l, arg_n, sizeof( long )); + envc = l; + arg_n += sizeof(long); + envs = arg_n; + for(int i = 0; i < envc; i++) + { + arg_n = arg_n + strlen(arg_n) + 1; + } + if( request_header.cmd == LAUNCHER_KWRAPPER ) + { + tty = arg_n; + arg_n += strlen( tty ) + 1; + } + } + + if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER + || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ) + { + memcpy( &l, arg_n, sizeof( long )); + avoid_loops = l; + arg_n += sizeof( long ); + } + + if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER + || request_header.cmd == LAUNCHER_EXT_EXEC ) + { + startup_id_str = arg_n; + arg_n += strlen( startup_id_str ) + 1; + } + + if ((request_header.arg_length > (arg_n - request_data)) && + (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )) + { + // Optional cwd + cwd = arg_n; arg_n += strlen(cwd) + 1; + } + + if ((arg_n - request_data) != request_header.arg_length) + { +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] EXEC request has invalid format.\n"); +#endif + free(request_data); + d.debug_wait = false; + return; + } + + // support for the old a bit broken way of setting DISPLAY for multihead + TQCString olddisplay = getenv(DISPLAY); + TQCString kdedisplay = getenv("TDE_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("TDE_DISPLAY"); + setenv(DISPLAY, olddisplay, true); + } + + if (pid && (d.result == 0)) + { + response_header.cmd = LAUNCHER_OK; + response_header.arg_length = sizeof(response_data); + response_data = pid; + write(sock, &response_header, sizeof(response_header)); + write(sock, &response_data, response_header.arg_length); + } + else + { + int l = d.errorMsg.length(); + if (l) l++; // Include trailing null. + response_header.cmd = LAUNCHER_ERROR; + response_header.arg_length = l; + write(sock, &response_header, sizeof(response_header)); + if (l) + write(sock, d.errorMsg.data(), l); + } + d.debug_wait = false; + } + else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV) + { + const char *env_name; + const char *env_value; + env_name = request_data; + env_value = env_name + strlen(env_name) + 1; + +#ifndef NDEBUG + if (launcher) + fprintf(stderr, "[tdeinit] Got SETENV '%s=%s' from tdelauncher.\n", env_name, env_value); + else + fprintf(stderr, "[tdeinit] Got SETENV '%s=%s' from socket.\n", env_name, env_value); +#endif + + if ( request_header.arg_length != + (int) (strlen(env_name) + strlen(env_value) + 2)) + { +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] SETENV request has invalid format.\n"); +#endif + free(request_data); + return; + } + setenv( env_name, env_value, 1); + } + else if (request_header.cmd == LAUNCHER_TERMINATE_KDE) + { +#ifndef NDEBUG + fprintf(stderr,"[tdeinit] Terminating Trinity.\n"); +#endif +#ifdef Q_WS_X11 + tdeinit_xio_errhandler( 0L ); +#endif + } + else if (request_header.cmd == LAUNCHER_TERMINATE_TDEINIT) + { +#ifndef NDEBUG + fprintf(stderr,"[tdeinit] Killing tdeinit/tdelauncher.\n"); +#endif + if (d.launcher_pid) + kill(d.launcher_pid, SIGTERM); + if (d.my_pid) + kill(d.my_pid, SIGTERM); + } + else if (request_header.cmd == LAUNCHER_DEBUG_WAIT) + { +#ifndef NDEBUG + fprintf(stderr,"[tdeinit] Debug wait activated.\n"); +#endif + d.debug_wait = true; + } + if (request_data) + free(request_data); +} + +static void handle_requests(pid_t waitForPid) +{ + int max_sock = d.wrapper; + if (d.wrapper_old > max_sock) + max_sock = d.wrapper_old; + if (d.launcher_pid && (d.launcher[0] > max_sock)) + max_sock = d.launcher[0]; +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef _WS_X11 + if (X11fd > max_sock) + max_sock = X11fd; +#endif + max_sock++; + + while(1) + { + fd_set rd_set; + fd_set wr_set; + fd_set e_set; + int result; + pid_t exit_pid; + char c; + + /* Flush the pipe of death */ + while( read(d.deadpipe[0], &c, 1) == 1); + + /* Handle dying children */ + do { + exit_pid = waitpid(-1, 0, WNOHANG); + if (exit_pid > 0) + { +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] PID %ld terminated.\n", (long) exit_pid); +#endif + if (waitForPid && (exit_pid == waitForPid)) + return; + + if (d.launcher_pid) + { + // TODO send process died message + tdelauncher_header request_header; + long request_data[2]; + request_header.cmd = LAUNCHER_DIED; + request_header.arg_length = sizeof(long) * 2; + request_data[0] = exit_pid; + request_data[1] = 0; /* not implemented yet */ + write(d.launcher[0], &request_header, sizeof(request_header)); + write(d.launcher[0], request_data, request_header.arg_length); + } + } + } + while( exit_pid > 0); + + FD_ZERO(&rd_set); + FD_ZERO(&wr_set); + FD_ZERO(&e_set); + + if (d.launcher_pid) + { + FD_SET(d.launcher[0], &rd_set); + } + FD_SET(d.wrapper, &rd_set); + if (d.wrapper_old) + { + FD_SET(d.wrapper_old, &rd_set); + } + FD_SET(d.deadpipe[0], &rd_set); +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + if(X11fd >= 0) FD_SET(X11fd, &rd_set); +#endif + + result = select(max_sock, &rd_set, &wr_set, &e_set, 0); + + /* Handle wrapper request */ + if ((result > 0) && (FD_ISSET(d.wrapper, &rd_set))) + { + struct sockaddr_un client; + kde_socklen_t sClient = sizeof(client); + int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient); + if (sock >= 0) + { +#if defined(TDEINIT_USE_XFT) && defined(TDEINIT_USE_FONTCONFIG) + if( FcGetVersion() < 20390 && !FcConfigUptoDate(NULL)) + FcInitReinitialize(); +#endif + if (fork() == 0) + { + close_fds(); + reset_oom_protect(); + handle_launcher_request(sock); + exit(255); /* Terminate process. */ + } + close(sock); + } + } + if ((result > 0) && (FD_ISSET(d.wrapper_old, &rd_set))) + { + struct sockaddr_un client; + kde_socklen_t sClient = sizeof(client); + int sock = accept(d.wrapper_old, (struct sockaddr *)&client, &sClient); + if (sock >= 0) + { +#if defined(TDEINIT_USE_XFT) && defined(TDEINIT_USE_FONTCONFIG) + if( FcGetVersion() < 20390 && !FcConfigUptoDate(NULL)) + FcInitReinitialize(); +#endif + if (fork() == 0) + { + close_fds(); + reset_oom_protect(); + handle_launcher_request(sock); + exit(255); /* Terminate process. */ + } + close(sock); + } + } + + /* Handle launcher request */ + if ((result > 0) && (d.launcher_pid) && (FD_ISSET(d.launcher[0], &rd_set))) + { + handle_launcher_request(); + if (waitForPid == d.launcher_pid) + return; + } + +//#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#ifdef Q_WS_X11 + /* Look for incoming X11 events */ + if((result > 0) && (X11fd >= 0)) + { + if(FD_ISSET(X11fd,&rd_set)) + { + if (X11display != 0) { + XEvent event_return; + while (XPending(X11display)) + XNextEvent(X11display, &event_return); + } + } + } +#endif + } +} + +static void tdeinit_library_path() +{ + TQStringList ltdl_library_path = + TQStringList::split(':', TQFile::decodeName(getenv("LTDL_LIBRARY_PATH"))); + TQStringList ld_library_path = + TQStringList::split(':', TQFile::decodeName(getenv("LD_LIBRARY_PATH"))); + + TQCString extra_path; + TQStringList candidates = s_instance->dirs()->resourceDirs("lib"); + for (TQStringList::ConstIterator it = candidates.begin(); + it != candidates.end(); + it++) + { + TQString d = *it; + if (ltdl_library_path.contains(d)) + continue; + if (ld_library_path.contains(d)) + continue; + if (d[d.length()-1] == '/') + { + d.truncate(d.length()-1); + if (ltdl_library_path.contains(d)) + continue; + if (ld_library_path.contains(d)) + continue; + } + if ((d == "/lib") || (d == "/usr/lib")) + continue; + + TQCString dir = TQFile::encodeName(d); + + if (access(dir, R_OK)) + continue; + + if ( !extra_path.isEmpty()) + extra_path += ":"; + extra_path += dir; + } + + if (lt_dlinit()) + { + const char * ltdlError = lt_dlerror(); + fprintf(stderr, "can't initialize dynamic loading: %s\n", ltdlError != 0 ? ltdlError : "(null)" ); + } + if (!extra_path.isEmpty()) + lt_dlsetsearchpath(extra_path.data()); + + TQCString display = getenv(DISPLAY); + if (display.isEmpty()) + { + fprintf(stderr, "[tdeinit] Aborting. $"DISPLAY" is not set.\n"); + exit(255); + } + int i; + if((i = display.findRev('.')) > display.findRev(':') && i >= 0) + display.truncate(i); + + TQCString socketName = TQFile::encodeName(locateLocal("socket", TQString("tdeinit-%1").arg(TQString(display)), s_instance)); + if (socketName.length() >= MAX_SOCK_FILE) + { + fprintf(stderr, "[tdeinit] Aborting. Socket name will be too long:\n"); + fprintf(stderr, " '%s'\n", socketName.data()); + exit(255); + } + strcpy(sock_file_old, socketName.data()); + + display.replace(":","_"); + socketName = TQFile::encodeName(locateLocal("socket", TQString("tdeinit_%1").arg(TQString(display)), s_instance)); + if (socketName.length() >= MAX_SOCK_FILE) + { + fprintf(stderr, "[tdeinit] Aborting. Socket name will be too long:\n"); + fprintf(stderr, " '%s'\n", socketName.data()); + exit(255); + } + strcpy(sock_file, socketName.data()); +} + +int tdeinit_xio_errhandler( Display *disp ) +{ + // disp is 0L when KDE shuts down. We don't want those warnings then. + + if ( disp ) + tqWarning( "[tdeinit] Fatal IO error: client killed" ); + + if (sock_file[0]) + { + /** Delete any stale socket file **/ + unlink(sock_file); + } + if (sock_file_old[0]) + { + /** Delete any stale socket file **/ + unlink(sock_file_old); + } + + // Don't kill our children in suicide mode, they may still be in use + if (d.suicide) + { + if (d.launcher_pid) + kill(d.launcher_pid, SIGTERM); + exit( 0 ); + } + + if ( disp ) + tqWarning( "[tdeinit] sending SIGHUP to children." ); + + /* this should remove all children we started */ + signal(SIGHUP, SIG_IGN); + kill(0, SIGHUP); + + sleep(2); + + if ( disp ) + tqWarning( "[tdeinit] sending SIGTERM to children." ); + + /* and if they don't listen to us, this should work */ + signal(SIGTERM, SIG_IGN); + kill(0, SIGTERM); + + if ( disp ) + tqWarning( "[tdeinit] Exit." ); + + exit( 0 ); + return 0; +} + +#ifdef Q_WS_X11 +int tdeinit_x_errhandler( Display *dpy, XErrorEvent *err ) +{ +#ifndef NDEBUG + char errstr[256]; + // tdeinit almost doesn't use X, and therefore there shouldn't be any X error + XGetErrorText( dpy, err->error_code, errstr, 256 ); + fprintf(stderr, "[tdeinit] TDE 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() +{ + XInitThreads(); + XSetIOErrorHandler(tdeinit_xio_errhandler); + XSetErrorHandler(tdeinit_x_errhandler); +} + +// Borrowed from tdebase/kaudio/kaudioserver.cpp +static int initXconnection() +{ + X11display = XOpenDisplay(NULL); + if ( X11display != 0 ) { + XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \ + 0, + BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)), + BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) ); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] opened connection to %s\n", DisplayString(X11display)); +#endif + int fd = XConnectionNumber( X11display ); + int on = 1; + (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on)); + return fd; + } else + fprintf(stderr, "[tdeinit] Can't connect to the X Server.\n" \ + "[tdeinit] Might not terminate at end of session.\n"); + + return -1; +} +#endif + +#ifdef __KCC +/* One of my horrible hacks. KCC includes in each "main" function a call + to _main(), which is provided by the C++ runtime system. It is + responsible for calling constructors for some static objects. That must + be done only once, so _main() is guarded against multiple calls. + For unknown reasons the designers of KAI's libKCC decided it would be + a good idea to actually abort() when it's called multiple times, instead + of ignoring further calls. This breaks our mechanism of KLM's, because + most KLM's have a main() function which is called from us. + The "solution" is to simply define our own _main(), which ignores multiple + calls, which is easy, and which does the same work as KAI'c _main(), + which is difficult. Currently (KAI 4.0f) it only calls __call_ctors(void) + (a C++ function), but if that changes we need to change our's too. + (matz) */ +/* + Those 'unknown reasons' are C++ standard forbidding recursive calls to main() + or any means that would possibly allow that (e.g. taking address of main()). + The correct solution is not using main() as entry point for tdeinit modules, + but only kdemain(). +*/ +extern "C" void _main(void); +extern "C" void __call_ctors__Fv(void); +static int main_called = 0; +void _main(void) +{ + if (main_called) + return; + main_called = 1; + __call_ctors__Fv (); +} +#endif + +static void secondary_child_handler(int) +{ + waitpid(-1, 0, WNOHANG); +} + +int main(int argc, char **argv, char **envp) +{ + int i; + pid_t pid; + int launch_dcop = 1; + int launch_tdelauncher = 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-tdelauncher") == 0) + launch_tdelauncher = 0; + if (strcmp(safe_argv[i], "--no-kded") == 0) + launch_kded = 0; + if (strcmp(safe_argv[i], "--suicide") == 0) + d.suicide = true; + if (strcmp(safe_argv[i], "--exit") == 0) + keep_running = 0; + if (strcmp(safe_argv[i], "--new-startup") == 0) + new_startup = 1; +#ifdef TDEINIT_OOM_PROTECT + if (strcmp(safe_argv[i], "--oom-pipe") == 0 && i+1<argc) + oom_pipe = atol(argv[i+1]); +#endif + if (strcmp(safe_argv[i], "--help") == 0) + { + printf("Usage: tdeinit [options]\n"); + // printf(" --no-dcop Do not start dcopserver\n"); + // printf(" --no-tdelauncher Do not start tdelauncher\n"); + printf(" --no-kded Do not start kded\n"); + printf(" --suicide Terminate when no TDE 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/tdelauncher 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 TDEInstance("tdeinit"); + + /** Prepare to change process name **/ + tdeinit_initsetproctitle(argc, argv, envp); + tdeinit_library_path(); + // Don't make our instance the global instance + // (do it only after tdeinit_library_path, that one indirectly uses TDEConfig, + // which seems to be buggy and always use TDEGlobal instead of the maching TDEInstance) + TDEGlobal::_instance = 0L; + // don't change envvars before tdeinit_initsetproctitle() + unsetenv("LD_BIND_NOW"); + unsetenv("DYLD_BIND_AT_LAUNCH"); + TDEApplication::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 ~/.trinity/tmp-<hostname>/tdeinit-<display> socket for incoming wrapper + * requests. + */ + init_tdeinit_socket(); + } + + if (launch_dcop) + { + if (d.suicide) + pid = launch( 3, "dcopserver", "--nosid\0--suicide" ); + else + pid = launch( 2, "dcopserver", "--nosid" ); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Launched DCOPServer, pid = %ld result = %d\n", (long) pid, d.result); +#endif + WaitPid(pid); + if (!WIFEXITED(d.exit_status) || (WEXITSTATUS(d.exit_status) != 0)) + { + fprintf(stderr, "[tdeinit] DCOPServer could not be started, aborting.\n"); + exit(1); + } + } +#ifndef __CYGWIN__ + if (!d.suicide && !getenv("TDE_IS_PRELINKED")) + { + TQString konq = locate("lib", "libkonq.la", s_instance); + if (!konq.isEmpty()) + (void) lt_dlopen(TQFile::encodeName(konq).data()); + } +#endif + if (launch_tdelauncher) + { + if( new_startup ) + pid = launch( 2, "tdelauncher", "--new-startup" ); + else + pid = launch( 1, "tdelauncher", 0 ); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Launched TDELauncher, pid = %ld result = %d\n", (long) pid, d.result); +#endif + handle_requests(pid); // Wait for tdelauncher to be ready + } + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 + X11fd = initXconnection(); +#endif + + { +#if defined(TDEINIT_USE_XFT) && defined(TDEINIT_USE_FONTCONFIG) + if( FcGetVersion() < 20390 ) + { + XftInit(0); + XftInitFtLibrary(); + } +#endif + TQFont::initialize(); + setlocale (LC_ALL, ""); + setlocale (LC_NUMERIC, "C"); +#ifdef Q_WS_X11 + if (XSupportsLocale ()) + { + // Similar to TQApplication::create_xim() + // but we need to use our own display + XOpenIM (X11display, 0, 0, 0); + } +#endif + } + + if (launch_kded) + { + if( new_startup ) + pid = launch( 2, "kded", "--new-startup" ); + else + pid = launch( 1, "kded", 0 ); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Launched KDED, pid = %ld result = %d\n", (long) pid, d.result); +#endif + handle_requests(pid); + } + + for(i = 1; i < argc; i++) + { + if (safe_argv[i][0] == '+') + { + pid = launch( 1, safe_argv[i]+1, 0); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Launched '%s', pid = %ld result = %d\n", safe_argv[i]+1, (long) pid, d.result); +#endif + handle_requests(pid); + } + else if (safe_argv[i][0] == '-' +#ifdef TDEINIT_OOM_PROTECT + || isdigit(safe_argv[i][0]) +#endif + ) + { + // Ignore + } + else + { + pid = launch( 1, safe_argv[i], 0 ); +#ifndef NDEBUG + fprintf(stderr, "[tdeinit] Launched '%s', pid = %ld result = %d\n", safe_argv[i], (long) pid, d.result); +#endif + } + } + + /** Free arguments **/ + for(i = 0; i < argc; i++) + { + free(safe_argv[i]); + } + free (safe_argv); + + tdeinit_setproctitle("tdeinit Running..."); + + if (!keep_running) + return 0; + + char c = 0; + write(d.initpipe[1], &c, 1); // Kdeinit is started. + close(d.initpipe[1]); + d.initpipe[1] = -1; + + handle_requests(0); + + return 0; +} + diff --git a/tdeinit/tdeioslave.cpp b/tdeinit/tdeioslave.cpp new file mode 100644 index 000000000..1690ca2f7 --- /dev/null +++ b/tdeinit/tdeioslave.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 <tqstring.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 libtdeio even if 'smart' linker is used */ +#include <tdeio/authinfo.h> +extern "C" TDEIO::AuthInfo* _tdeioslave_init_kio() { return new TDEIO::AuthInfo(); } + +int main(int argc, char **argv) +{ + if (argc < 5) + { + fprintf(stderr, "Usage: tdeioslave <slave-lib> <protocol> <tdelauncher-socket> <app-socket>\n\nThis program is part of TDE.\n"); + exit(1); + } + TQCString 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/tdeinit/tdelauncher.cpp b/tdeinit/tdelauncher.cpp new file mode 100644 index 000000000..f7ca08678 --- /dev/null +++ b/tdeinit/tdelauncher.cpp @@ -0,0 +1,1421 @@ +/* + 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 <tqfile.h> + +#include <tdeconfig.h> +#include <kdebug.h> +#include <klibloader.h> +#include <tdelocale.h> +#include <tdeprotocolmanager.h> +#include <kprotocolinfo.h> +#include <krun.h> +#include <kstandarddirs.h> +#include <tdetempfile.h> +#include <kurl.h> + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <tdestartupinfo.h> // schroder +#endif + + +#include "tdeio/global.h" +#include "tdeio/connection.h" +#include "tdeio/slaveinterface.h" + +#include "tdelauncher.h" +#include "tdelauncher_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 TDEIO; + +template class TQPtrList<TDELaunchRequest>; +template class TQPtrList<IdleSlave>; + +IdleSlave::IdleSlave(TDESocket *socket) +{ + mConn.init(socket); + mConn.connect(this, TQT_SLOT(gotInput())); + mConn.send( CMD_SLAVE_STATUS ); + mPid = 0; + mBirthDate = time(0); + mOnHold = false; +} + +void +IdleSlave::gotInput() +{ + int cmd; + TQByteArray 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 + { + TQDataStream stream( data, IO_ReadOnly ); + pid_t pid; + TQCString protocol; + TQString host; + TQ_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 TQString &app_socket) +{ + TQByteArray data; + TQDataStream 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 TQString &protocol, const TQString &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); +} + +TDELauncher::TDELauncher(int _tdeinitSocket, bool new_startup) +// : TDEApplication( false, false ), // No Styles, No GUI + : TDEApplication( false, true ), // TQClipboard tries to construct a QWidget so a GUI is technically needed, even though it is not used + DCOPObject("tdelauncher"), + tdeinitSocket(_tdeinitSocket), mAutoStart( new_startup ), + dontBlockReading(false), newStartup( new_startup ) +{ +#ifdef Q_WS_X11 + mCached_dpy = NULL; +#endif + connect(&mAutoTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotAutoStart())); + requestList.setAutoDelete(true); + mSlaveWaitRequest.setAutoDelete(true); + dcopClient()->setNotifications( true ); + connect(dcopClient(), TQT_SIGNAL( applicationRegistered( const TQCString &)), + this, TQT_SLOT( slotAppRegistered( const TQCString &))); + dcopClient()->connectDCOPSignal( "DCOPServer", "", "terminateKDE()", + objId(), "terminateKDE()", false ); + + TQString prefix = locateLocal("socket", "tdelauncher"); + KTempFile domainname(prefix, TQString::fromLatin1(".slave-socket")); + if (domainname.status() != 0) + { + // Sever error! + tqDebug("TDELauncher: Fatal error, can't create tempfile!"); + ::exit(1); + } + mPoolSocketName = domainname.name(); +#ifdef __CYGWIN__ + domainname.close(); + domainname.unlink(); +#endif + mPoolSocket = new TDEServerSocket(static_cast<const char*>(TQFile::encodeName(mPoolSocketName))); + connect(mPoolSocket, TQT_SIGNAL(accepted( TDESocket *)), + TQT_SLOT(acceptSlave(TDESocket *))); + + connect(&mTimer, TQT_SIGNAL(timeout()), TQT_SLOT(idleTimeout())); + + tdeinitNotifier = new TQSocketNotifier(tdeinitSocket, TQSocketNotifier::Read); + connect(tdeinitNotifier, TQT_SIGNAL( activated( int )), + this, TQT_SLOT( slotKDEInitData( int ))); + tdeinitNotifier->setEnabled( true ); + lastRequest = 0; + bProcessingQueue = false; + + mSlaveDebug = getenv("TDE_SLAVE_DEBUG_WAIT"); + if (!mSlaveDebug.isEmpty()) + { + tqWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'", mSlaveDebug.data()); + } + mSlaveValgrind = getenv("TDE_SLAVE_VALGRIND"); + if (!mSlaveValgrind.isEmpty()) + { + mSlaveValgrindSkin = getenv("TDE_SLAVE_VALGRIND_SKIN"); + tqWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'", mSlaveValgrind.data()); + } + tdelauncher_header request_header; + request_header.cmd = LAUNCHER_OK; + request_header.arg_length = 0; + write(tdeinitSocket, &request_header, sizeof(request_header)); +} + +TDELauncher::~TDELauncher() +{ + close(); +} + +void TDELauncher::close() +{ + if (!mPoolSocketName.isEmpty()) + { + TQCString filename = TQFile::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 +TDELauncher::destruct(int exit_code) +{ + if (kapp) ((TDELauncher*)kapp)->close(); + // We don't delete kapp here, that's intentional. + ::exit(exit_code); +} + +bool +TDELauncher::process(const TQCString &fun, const TQByteArray &data, + TQCString &replyType, TQByteArray &replyData) +{ + if ((fun == "exec_blind(TQCString,TQValueList<TQCString>)") + || (fun == "exec_blind(TQCString,TQValueList<TQCString>,TQValueList<TQCString>,TQCString)")) + { + TQDataStream stream(data, IO_ReadOnly); + replyType = "void"; + TQCString name; + TQValueList<TQCString> arg_list; + TQCString startup_id = "0"; + TQValueList<TQCString> envs; + stream >> name >> arg_list; + if( fun == "exec_blind(TQCString,TQValueList<TQCString>,TQValueList<TQCString>,TQCString)" ) + stream >> envs >> startup_id; + kdDebug(7016) << "TDELauncher: Got exec_blind('" << name << "', ...)" << endl; + exec_blind( name, arg_list, envs, startup_id); + return true; + } + if ((fun == "start_service_by_name(TQString,TQStringList)") || + (fun == "start_service_by_desktop_path(TQString,TQStringList)")|| + (fun == "start_service_by_desktop_name(TQString,TQStringList)")|| + (fun == "tdeinit_exec(TQString,TQStringList)") || + (fun == "tdeinit_exec_wait(TQString,TQStringList)") || + (fun == "start_service_by_name(TQString,TQStringList,TQValueList<TQCString>,TQCString)") || + (fun == "start_service_by_desktop_path(TQString,TQStringList,TQValueList<TQCString>,TQCString)")|| + (fun == "start_service_by_desktop_name(TQString,TQStringList,TQValueList<TQCString>,TQCString)") || + (fun == "start_service_by_name(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)") || + (fun == "start_service_by_desktop_path(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)")|| + (fun == "start_service_by_desktop_name(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)") || + (fun == "tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>)") || + (fun == "tdeinit_exec_wait(TQString,TQStringList,TQValueList<TQCString>)") || + (fun == "tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>,TQCString)") || + (fun == "tdeinit_exec_wait(TQString,TQStringList,TQValueList<TQCString>,TQCString)")) + { + TQDataStream stream(data, IO_ReadOnly); + bool bNoWait = false; + TQString serviceName; + TQStringList urls; + TQValueList<TQCString> envs; + TQCString startup_id = ""; + DCOPresult.result = -1; + DCOPresult.dcopName = 0; + DCOPresult.error = TQString::null; + DCOPresult.pid = 0; + stream >> serviceName >> urls; + if ((fun == "start_service_by_name(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)") || + (fun == "start_service_by_desktop_path(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)")|| + (fun == "start_service_by_desktop_name(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)")) + stream >> envs >> startup_id >> bNoWait; + else if ((fun == "start_service_by_name(TQString,TQStringList,TQValueList<TQCString>,TQCString)") || + (fun == "start_service_by_desktop_path(TQString,TQStringList,TQValueList<TQCString>,TQCString)")|| + (fun == "start_service_by_desktop_name(TQString,TQStringList,TQValueList<TQCString>,TQCString)")) + stream >> envs >> startup_id; + else if ((fun == "tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>)") || + (fun == "tdeinit_exec_wait(TQString,TQStringList,TQValueList<TQCString>)")) + stream >> envs; + else if ((fun == "tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>,TQCString)") || + (fun == "tdeinit_exec_wait(TQString,TQStringList,TQValueList<TQCString>,TQCString)")) + stream >> envs >> startup_id; + bool finished; + if (strncmp(fun, "start_service_by_name(", 22) == 0) + { + kdDebug(7016) << "TDELauncher: 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) << "TDELauncher: 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) << "TDELauncher: Got start_service_by_desktop_name('" << serviceName << "', ...)" << endl; + finished = start_service_by_desktop_name(serviceName, urls, envs, startup_id, bNoWait ); + } + else if ((fun == "tdeinit_exec(TQString,TQStringList)") + || (fun == "tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>)") + || (fun == "tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>,TQCString)")) + { + kdDebug(7016) << "TDELauncher: Got tdeinit_exec('" << serviceName << "', ...)" << endl; + finished = tdeinit_exec(serviceName, urls, envs, startup_id, false); + } + else + { + kdDebug(7016) << "TDELauncher: Got tdeinit_exec_wait('" << serviceName << "', ...)" << endl; + finished = tdeinit_exec(serviceName, urls, envs, startup_id, true); + } + if (!finished) + { + replyType = "serviceResult"; + TQDataStream stream2(replyData, IO_WriteOnly); + stream2 << DCOPresult.result << DCOPresult.dcopName << DCOPresult.error << DCOPresult.pid; + } + return true; + } + else if (fun == "requestSlave(TQString,TQString,TQString)") + { + TQDataStream stream(data, IO_ReadOnly); + TQString protocol; + TQString host; + TQString app_socket; + stream >> protocol >> host >> app_socket; + replyType = "TQString"; + TQString error; + pid_t pid = requestSlave(protocol, host, app_socket, error); + TQDataStream stream2(replyData, IO_WriteOnly); + stream2 << pid << error; + return true; + } + else if (fun == "requestHoldSlave(KURL,TQString)") + { + TQDataStream stream(data, IO_ReadOnly); + KURL url; + TQString app_socket; + stream >> url >> app_socket; + replyType = "pid_t"; + pid_t pid = requestHoldSlave(url, app_socket); + TQDataStream stream2(replyData, IO_WriteOnly); + stream2 << pid; + return true; + } + else if (fun == "waitForSlave(pid_t)") + { + TQDataStream stream(data, IO_ReadOnly); + pid_t pid; + stream >> pid; + waitForSlave(pid); + replyType = "void"; + return true; + + } + else if (fun == "setLaunchEnv(TQCString,TQCString)") + { + TQDataStream stream(data, IO_ReadOnly); + TQCString name; + TQCString value; + stream >> name >> value; + setLaunchEnv(name, value); + replyType = "void"; + return true; + } + else if (fun == "reparseConfiguration()") + { + TDEGlobal::config()->reparseConfiguration(); + kdDebug(7016) << "TDELauncher::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() << "TDELauncher::process ---> terminateKDE" << endl; + tdelauncher_header request_header; + request_header.cmd = LAUNCHER_TERMINATE_KDE; + request_header.arg_length = 0; + write(tdeinitSocket, &request_header, sizeof(request_header)); + destruct(0); + } + else if (fun == "autoStart()") + { + kdDebug() << "TDELauncher::process ---> autoStart" << endl; + autoStart(1); + replyType = "void"; + return true; + } + else if (fun == "autoStart(int)") + { + kdDebug() << "TDELauncher::process ---> autoStart(int)" << endl; + TQDataStream 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 +TDELauncher::interfaces() +{ + QCStringList ifaces = DCOPObject::interfaces(); + ifaces += "TDELauncher"; + return ifaces; +} + +QCStringList +TDELauncher::functions() +{ + QCStringList funcs = DCOPObject::functions(); + funcs << "void exec_blind(TQCString,TQValueList<TQCString>)"; + funcs << "void exec_blind(TQCString,TQValueList<TQCString>,TQValueList<TQCString>,TQCString)"; + funcs << "serviceResult start_service_by_name(TQString,TQStringList)"; + funcs << "serviceResult start_service_by_desktop_path(TQString,TQStringList)"; + funcs << "serviceResult start_service_by_desktop_name(TQString,TQStringList)"; + funcs << "serviceResult tdeinit_exec(TQString,TQStringList)"; + funcs << "serviceResult tdeinit_exec_wait(TQString,TQStringList)"; + funcs << "serviceResult start_service_by_name(TQString,TQStringList,TQValueList<TQCString>,TQCString)"; + funcs << "serviceResult start_service_by_desktop_path(TQString,TQStringList,TQValueList<TQCString>,TQCString)"; + funcs << "serviceResult start_service_by_desktop_name(TQString,TQStringList,TQValueList<TQCString>,TQCString)"; + funcs << "serviceResult start_service_by_name(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)"; + funcs << "serviceResult start_service_by_desktop_path(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)"; + funcs << "serviceResult start_service_by_desktop_name(TQString,TQStringList,TQValueList<TQCString>,TQCString,bool)"; + funcs << "serviceResult tdeinit_exec(TQString,TQStringList,TQValueList<TQCString>)"; + funcs << "serviceResult tdeinit_exec_wait(TQString,TQStringList,TQValueList<TQCString>)"; + funcs << "TQString requestSlave(TQString,TQString,TQString)"; + funcs << "pid_t requestHoldSlave(KURL,TQString)"; + funcs << "void waitForSlave(pid_t)"; + funcs << "void setLaunchEnv(TQCString,TQCString)"; + funcs << "void reparseConfiguration()"; +// funcs << "void terminateKDE()"; + funcs << "void autoStart()"; + funcs << "void autoStart(int)"; + return funcs; +} + +void TDELauncher::setLaunchEnv(const TQCString &name, const TQCString &_value) +{ + TQCString value(_value); + if (value.isNull()) + value = ""; + tdelauncher_header request_header; + TQByteArray 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(tdeinitSocket, &request_header, sizeof(request_header)); + write(tdeinitSocket, 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 +TDELauncher::slotKDEInitData(int) +{ + tdelauncher_header request_header; + TQByteArray requestData; + if( dontBlockReading ) + { + // in case we get a request to start an application and data arrive + // to tdeinitSocket 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( tdeinitSocket, &in ); + select( tdeinitSocket + 1, &in, 0, 0, &tm ); + if( !FD_ISSET( tdeinitSocket, &in )) + return; + } + dontBlockReading = false; + int result = read_socket(tdeinitSocket, (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(tdeinitSocket, (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 = TDELaunchRequest::Running; + break; + } + + case KService::DCOP_Unique: + { + lastRequest->status = TDELaunchRequest::Launching; + break; + } + + case KService::DCOP_Wait: + { + lastRequest->status = TDELaunchRequest::Launching; + break; + } + + case KService::DCOP_Multi: + { + lastRequest->status = TDELaunchRequest::Launching; + break; + } + } + lastRequest = 0; + return; + } + if (lastRequest && (request_header.cmd == LAUNCHER_ERROR)) + { + lastRequest->status = TDELaunchRequest::Error; + if (!requestData.isEmpty()) + lastRequest->errorMsg = TQString::fromUtf8((char *) requestData.data()); + lastRequest = 0; + return; + } + + kdWarning(7016) << "Unexpected command from KDEInit (" << (unsigned int) request_header.cmd + << ")" << endl; +} + +void +TDELauncher::processDied(pid_t pid, long /* exitStatus */) +{ + TDELaunchRequest *request = requestList.first(); + for(; request; request = requestList.next()) + { + if (request->pid == pid) + { + if (request->dcop_service_type == KService::DCOP_Wait) + request->status = TDELaunchRequest::Done; + else if ((request->dcop_service_type == KService::DCOP_Unique) && + (dcopClient()->isApplicationRegistered(request->dcop_name))) + request->status = TDELaunchRequest::Running; + else + request->status = TDELaunchRequest::Error; + requestDone(request); + return; + } + } +} + +void +TDELauncher::slotAppRegistered(const TQCString &appId) +{ + const char *cAppId = appId.data(); + if (!cAppId) return; + + TDELaunchRequest *request = requestList.first(); + TDELaunchRequest *nextRequest; + for(; request; request = nextRequest) + { + nextRequest = requestList.next(); + if (request->status != TDELaunchRequest::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 = TDELaunchRequest::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 = TDELaunchRequest::Running; + requestDone(request); + continue; + } + } +} + +void +TDELauncher::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 +TDELauncher::slotAutoStart() +{ + KService::Ptr s; + do + { + TQString service = mAutoStart.startService(); + if (service.isEmpty()) + { + // Done + if( !mAutoStart.phaseDone()) + { + mAutoStart.setPhaseDone(); + // Emit signal + if( newStartup ) + { + TQCString autoStartSignal; + autoStartSignal.sprintf( "autoStart%dDone()", mAutoStart.phase()); + emitDCOPSignal(autoStartSignal, TQByteArray()); + } + else + { + TQCString autoStartSignal( "autoStartDone()" ); + int phase = mAutoStart.phase(); + if ( phase > 1 ) + autoStartSignal.sprintf( "autoStart%dDone()", phase ); + emitDCOPSignal(autoStartSignal, TQByteArray()); + } + } + return; + } + s = new KService(service); + } + while (!start_service(s, TQStringList(), TQValueList<TQCString>(), "0", false, true)); + // Loop till we find a service that we can start. +} + +void +TDELauncher::requestDone(TDELaunchRequest *request) +{ + if ((request->status == TDELaunchRequest::Running) || + (request->status == TDELaunchRequest::Done)) + { + DCOPresult.result = 0; + DCOPresult.dcopName = request->dcop_name; + DCOPresult.error = TQString::null; + DCOPresult.pid = request->pid; + } + else + { + DCOPresult.result = 1; + DCOPresult.dcopName = ""; + DCOPresult.error = i18n("KDEInit could not launch '%1'.").arg(TQString(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 ) + { + TDEStartupInfoId id; + id.initId( request->startup_id ); + TDEStartupInfo::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) + { + TQByteArray replyData; + TQCString replyType; + replyType = "serviceResult"; + TQDataStream stream2(replyData, IO_WriteOnly); + stream2 << DCOPresult.result << DCOPresult.dcopName << DCOPresult.error << DCOPresult.pid; + dcopClient()->endTransaction( request->transaction, + replyType, replyData); + } + requestList.removeRef( request ); +} + +void +TDELauncher::requestStart(TDELaunchRequest *request) +{ + requestList.append( request ); + // Send request to tdeinit. + tdelauncher_header request_header; + TQByteArray requestData; + int length = 0; + length += sizeof(long); // Nr of. Args + length += request->name.length() + 1; // Cmd + for(TQValueList<TQCString>::Iterator it = request->arg_list.begin(); + it != request->arg_list.end(); + it++) + { + length += (*it).length() + 1; // Args... + } + length += sizeof(long); // Nr of. envs + for(TQValueList<TQCString>::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(TQValueList<TQCString>::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(TQValueList<TQCString>::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(tdeinitSocket, &request_header, sizeof(request_header)); + write(tdeinitSocket, requestData.data(), request_header.arg_length); + + // Wait for pid to return. + lastRequest = request; + dontBlockReading = false; + do { + slotKDEInitData( tdeinitSocket ); + } + while (lastRequest != 0); + dontBlockReading = true; +} + +void +TDELauncher::exec_blind( const TQCString &name, const TQValueList<TQCString> &arg_list, + const TQValueList<TQCString> &envs, const TQCString& startup_id ) +{ + TDELaunchRequest *request = new TDELaunchRequest; + 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 = TDELaunchRequest::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, TQValueList< TQCString >()); + 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 +TDELauncher::start_service_by_name(const TQString &serviceName, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& 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 +TDELauncher::start_service_by_desktop_path(const TQString &serviceName, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& 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 +TDELauncher::start_service_by_desktop_name(const TQString &serviceName, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& 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 +TDELauncher::start_service(KService::Ptr service, const TQStringList &_urls, + const TQValueList<TQCString> &envs, const TQCString& startup_id, bool blind, bool autoStart) +{ + TQStringList 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; + } + TDELaunchRequest *request = new TDELaunchRequest; + 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. + TQStringList::ConstIterator it = urls.begin(); + for(++it; + it != urls.end(); + ++it) + { + TQStringList singleUrl; + singleUrl.append(*it); + TQCString 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); + } + TQString 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)) + { + TQVariant v = service->property("X-DCOP-ServiceName"); + if (v.isValid()) + request->dcop_name = v.toString().utf8(); + if (request->dcop_name.isEmpty()) + { + request->dcop_name = TQFile::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 +TDELauncher::send_service_startup_info( TDELaunchRequest *request, KService::Ptr service, const TQCString& startup_id, + const TQValueList<TQCString> &envs ) +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 // TDEStartup* isn't implemented for Qt/Embedded yet + request->startup_id = "0"; + if( startup_id == "0" ) + return; + bool silent; + TQCString wmclass; + if( !KRun::checkStartupNotify( TQString::null, service, &silent, &wmclass )) + return; + TDEStartupInfoId id; + id.initId( startup_id ); + const char* dpy_str = NULL; + for( TQValueList<TQCString>::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; + + TDEStartupInfoData 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( TDEStartupInfoData::Yes ); + // the rest will be sent by tdeinit + TDEStartupInfo::sendStartupX( dpy, id, data ); + if( mCached_dpy != dpy && mCached_dpy != NULL ) + XCloseDisplay( mCached_dpy ); + mCached_dpy = dpy; + return; +#else + return; +#endif +} + +void +TDELauncher::cancel_service_startup_info( TDELaunchRequest* request, const TQCString& startup_id, + const TQValueList<TQCString> &envs ) +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +//#ifdef Q_WS_X11 // TDEStartup* 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( TQValueList<TQCString>::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; + TDEStartupInfoId id; + id.initId( startup_id ); + TDEStartupInfo::sendFinishX( dpy, id ); + if( mCached_dpy != dpy && mCached_dpy != NULL ) + XCloseDisplay( mCached_dpy ); + mCached_dpy = dpy; + } +#endif +} + +bool +TDELauncher::tdeinit_exec(const TQString &app, const TQStringList &args, + const TQValueList<TQCString> &envs, TQCString startup_id, bool wait) +{ + TDELaunchRequest *request = new TDELaunchRequest; + request->autoStart = false; + + for(TQStringList::ConstIterator it = args.begin(); + it != args.end(); + it++) + { + TQString 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 != "tdebuildsycoca" ) // 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, TQValueList< TQCString >()); + else // no .desktop file, no startup info + cancel_service_startup_info( request, startup_id, envs ); + } + request->transaction = dcopClient()->beginTransaction(); + queueRequest(request); + return true; +} + +void +TDELauncher::queueRequest(TDELaunchRequest *request) +{ + requestQueue.append( request ); + if (!bProcessingQueue) + { + bProcessingQueue = true; + TQTimer::singleShot(0, this, TQT_SLOT( slotDequeue() )); + } +} + +void +TDELauncher::slotDequeue() +{ + do { + TDELaunchRequest *request = requestQueue.take(0); + // process request + request->status = TDELaunchRequest::Launching; + requestStart(request); + if (request->status != TDELaunchRequest::Launching) + { + // Request handled. + requestDone( request ); + continue; + } + } while(requestQueue.count()); + bProcessingQueue = false; +} + +void +TDELauncher::createArgs( TDELaunchRequest *request, const KService::Ptr service , + const TQStringList &urls) +{ + TQStringList params = KRun::processDesktopExec(*service, urls, false); + + for(TQStringList::ConstIterator it = params.begin(); + it != params.end(); ++it) + { + request->arg_list.append((*it).local8Bit()); + } + request->cwd = TQFile::encodeName(service->path()); +} + +///// IO-Slave functions + +pid_t +TDELauncher::requestHoldSlave(const KURL &url, const TQString &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 +TDELauncher::requestSlave(const TQString &protocol, + const TQString &host, + const TQString &app_socket, + TQString &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, TQString::null, false)) + break; + } + } + if (slave) + { + mSlaveList.removeRef(slave); + slave->connect(app_socket); + return slave->pid(); + } + + TQString _name = KProtocolInfo::exec(protocol); + if (_name.isEmpty()) + { + error = i18n("Unknown protocol '%1'.\n").arg(protocol); + return 0; + } + + TQCString name = _name.latin1(); // ex: "tdeio_ftp" + TQCString arg1 = protocol.latin1(); + TQCString arg2 = TQFile::encodeName(mPoolSocketName); + TQCString arg3 = TQFile::encodeName(app_socket); + TQValueList<TQCString> arg_list; + arg_list.append(arg1); + arg_list.append(arg2); + arg_list.append(arg3); + +// kdDebug(7016) << "TDELauncher: launching new slave " << _name << " with protocol=" << protocol << endl; + if (mSlaveDebug == arg1) + { + tdelauncher_header request_header; + request_header.cmd = LAUNCHER_DEBUG_WAIT; + request_header.arg_length = 0; + write(tdeinitSocket, &request_header, sizeof(request_header)); + } + if (mSlaveValgrind == arg1) + { + arg_list.prepend(TQFile::encodeName(KLibLoader::findLibrary(name))); + arg_list.prepend(TQFile::encodeName(locate("exe", "tdeioslave"))); + name = "valgrind"; + if (!mSlaveValgrindSkin.isEmpty()) { + arg_list.prepend(TQCString("--tool=") + mSlaveValgrindSkin); + } else + arg_list.prepend("--tool=memcheck"); + } + + TDELaunchRequest *request = new TDELaunchRequest; + 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 = TDELaunchRequest::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(TQString(name)); + } + return pid; +} + +void +TDELauncher::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 +TDELauncher::acceptSlave(TDESocket *slaveSocket) +{ + IdleSlave *slave = new IdleSlave(slaveSocket); + // Send it a SLAVE_STATUS command. + mSlaveList.append(slave); + connect(slave, TQT_SIGNAL(destroyed()), this, TQT_SLOT(slotSlaveGone())); + connect(slave, TQT_SIGNAL(statusUpdate(IdleSlave *)), + this, TQT_SLOT(slotSlaveStatus(IdleSlave *))); + if (!mTimer.isActive()) + { + mTimer.start(1000*10); + } +} + +void +TDELauncher::slotSlaveStatus(IdleSlave *slave) +{ + SlaveWaitRequest *waitRequest = mSlaveWaitRequest.first(); + while(waitRequest) + { + if (waitRequest->pid == slave->pid()) + { + TQByteArray replyData; + TQCString replyType; + replyType = "void"; + dcopClient()->endTransaction( waitRequest->transaction, replyType, replyData); + mSlaveWaitRequest.removeRef(waitRequest); + waitRequest = mSlaveWaitRequest.current(); + } + else + { + waitRequest = mSlaveWaitRequest.next(); + } + } +} + +void +TDELauncher::slotSlaveGone() +{ + IdleSlave *slave = (IdleSlave *) sender(); + mSlaveList.removeRef(slave); + if ((mSlaveList.count() == 0) && (mTimer.isActive())) + { + mTimer.stop(); + } +} + +void +TDELauncher::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 "tdelauncher.moc" diff --git a/tdeinit/tdelauncher.h b/tdeinit/tdelauncher.h new file mode 100644 index 000000000..791958980 --- /dev/null +++ b/tdeinit/tdelauncher.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 <tqstring.h> +#include <tqvaluelist.h> +#include <tqsocketnotifier.h> +#include <tqptrlist.h> +#include <tqtimer.h> + +#include <dcopclient.h> +#include <tdeio/connection.h> +#include <ksock.h> +#include <kurl.h> +#include <kuniqueapplication.h> + +#include <kservice.h> + +#include "autostart.h" + +class IdleSlave : public TQObject +{ + Q_OBJECT +public: + IdleSlave(TDESocket *socket); + bool match( const TQString &protocol, const TQString &host, bool connected); + void connect( const TQString &app_socket); + pid_t pid() const { return mPid;} + int age(time_t now); + void reparseConfiguration(); + bool onHold(const KURL &url); + TQString protocol() const {return mProtocol;} + +signals: + void statusUpdate(IdleSlave *); + +protected slots: + void gotInput(); + +protected: + TDEIO::Connection mConn; + TQString mProtocol; + TQString mHost; + bool mConnected; + pid_t mPid; + time_t mBirthDate; + bool mOnHold; + KURL mUrl; +}; + +class SlaveWaitRequest +{ +public: + pid_t pid; + DCOPClientTransaction *transaction; +}; + +class TDELaunchRequest +{ +public: + TQCString name; + TQValueList<TQCString> arg_list; + TQCString 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; + TQString errorMsg; +#ifdef Q_WS_X11 + TQCString startup_id; // "" is the default, "0" for none + TQCString startup_dpy; // Display to send startup notification to. +#endif + TQValueList<TQCString> envs; // env. variables to be app's environment + TQCString cwd; +}; + +struct serviceResult +{ + int result; // 0 means success. > 0 means error (-1 means pending) + TQCString dcopName; // Contains DCOP name on success + TQString error; // Contains error description on failure. + pid_t pid; +}; + +class TDELauncher : public TDEApplication, public DCOPObject +{ + Q_OBJECT + +public: + TDELauncher(int _tdeinitSocket, bool new_startup); + + ~TDELauncher(); + + void close(); + static void destruct(int exit_code); // exit! + + // DCOP + virtual bool process(const TQCString &fun, const TQByteArray &data, + TQCString &replyType, TQByteArray &replyData); + virtual QCStringList functions(); + virtual QCStringList interfaces(); + +protected: + void processDied(pid_t pid, long exitStatus); + + void requestStart(TDELaunchRequest *request); + void requestDone(TDELaunchRequest *request); + + void setLaunchEnv(const TQCString &name, const TQCString &value); + void exec_blind(const TQCString &name, const TQValueList<TQCString> &arg_list, + const TQValueList<TQCString> &envs, const TQCString& startup_id = "" ); + bool start_service(KService::Ptr service, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& startup_id = "", + bool blind = false, bool autoStart = false ); + bool start_service_by_name(const TQString &serviceName, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& startup_id, bool blind); + bool start_service_by_desktop_path(const TQString &serviceName, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& startup_id, bool blind); + bool start_service_by_desktop_name(const TQString &serviceName, const TQStringList &urls, + const TQValueList<TQCString> &envs, const TQCString& startup_id, bool blind); + bool tdeinit_exec(const TQString &app, const TQStringList &args, + const TQValueList<TQCString> &envs, TQCString startup_id, bool wait); + + void waitForSlave(pid_t pid); + + void autoStart(int phase); + + void createArgs( TDELaunchRequest *request, const KService::Ptr service, + const TQStringList &url); + + pid_t requestHoldSlave(const KURL &url, const TQString &app_socket); + pid_t requestSlave(const TQString &protocol, const TQString &host, + const TQString &app_socket, TQString &error); + + + void queueRequest(TDELaunchRequest *); + + void send_service_startup_info( TDELaunchRequest *request, KService::Ptr service, const TQCString& startup_id, + const TQValueList<TQCString> &envs ); + void cancel_service_startup_info( TDELaunchRequest *request, const TQCString& startup_id, + const TQValueList<TQCString> &envs ); + +public slots: + void slotAutoStart(); + void slotDequeue(); + void slotKDEInitData(int); + void slotAppRegistered(const TQCString &appId); + void slotSlaveStatus(IdleSlave *); + void acceptSlave( TDESocket *); + void slotSlaveGone(); + void idleTimeout(); + +protected: + TQPtrList<TDELaunchRequest> requestList; // Requests being handled + TQPtrList<TDELaunchRequest> requestQueue; // Requests waiting to being handled + int tdeinitSocket; + TQSocketNotifier *tdeinitNotifier; + serviceResult DCOPresult; + TDELaunchRequest *lastRequest; + TQPtrList<SlaveWaitRequest> mSlaveWaitRequest; + TQString mPoolSocketName; + TDEServerSocket *mPoolSocket; + TQPtrList<IdleSlave> mSlaveList; + TQTimer mTimer; + TQTimer mAutoTimer; + bool bProcessingQueue; + AutoStart mAutoStart; + TQCString mSlaveDebug; + TQCString mSlaveValgrind; + TQCString mSlaveValgrindSkin; + bool dontBlockReading; + bool newStartup; +#ifdef Q_WS_X11 + Display *mCached_dpy; +#endif +}; +#endif diff --git a/tdeinit/tdelauncher_cmds.h b/tdeinit/tdelauncher_cmds.h new file mode 100644 index 000000000..5763e5a38 --- /dev/null +++ b/tdeinit/tdelauncher_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; +} tdelauncher_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 tdeinit. + * 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 tdeinit 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->tdeinit_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_TDEINIT + * + * Suicide is painless + */ +#define LAUNCHER_TERMINATE_TDEINIT 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->tdeinit_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->tdeinit_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->tdeinit_wrapper link in $PATH + */ + +#define LAUNCHER_FD 42 +/* + * File descriptor to use for communication with tdeinit. + */ + +#endif diff --git a/tdeinit/tdelauncher_main.cpp b/tdeinit/tdelauncher_main.cpp new file mode 100644 index 000000000..747050668 --- /dev/null +++ b/tdeinit/tdelauncher_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 "tdeapplication.h" +#include "tdelauncher.h" +#include "tdecmdlineargs.h" +#include "kcrash.h" +#include "kdebug.h" +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <tqcstring.h> +#include <tdelocale.h> + +#include "tdelauncher_cmds.h" + +static void sig_handler(int sig_num) +{ + // No recursion + signal( SIGHUP, SIG_IGN); + signal( SIGTERM, SIG_IGN); + fprintf(stderr, "[tdelauncher] Exiting on signal %d\n", sig_num); + TDELauncher::destruct(255); +} + +static TDECmdLineOptions options[] = +{ + { "new-startup", "Internal", 0 }, + TDECmdLineLastOption +}; + +extern "C" KDE_EXPORT int kdemain( int argc, char**argv ) +{ + // Started via tdeinit. + if (fcntl(LAUNCHER_FD, F_GETFD) == -1) + { + fprintf(stderr, "%s", i18n("[tdelauncher] This program is not supposed to be started manually.\n" + "[tdelauncher] It is started automatically by tdeinit.\n").local8Bit().data()); + return 1; + } + + TQCString cname = TDEApplication::launcher(); + char *name = cname.data(); + TDECmdLineArgs::init(argc, argv, name, "TDELauncher", "A service launcher.", + "v1.0"); + + TDELauncher::addCmdLineOptions(); + TDECmdLineArgs::addCmdLineOptions( options ); + + // WABA: Make sure not to enable session management. + putenv(strdup("SESSION_MANAGER=")); + + // Allow the locale to initialize properly + TDELocale::setMainCatalogue("tdelibs"); + + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + + int maxTry = 3; + while(true) + { + TQCString dcopName = TDEApplication::dcopClient()->registerAs(name, false); + if (dcopName.isEmpty()) + { + kdWarning() << "[tdelauncher] DCOP communication problem!" << endl; + return 1; + } + if (dcopName == cname) + break; // Good! + + if (--maxTry == 0) + { + kdWarning() << "[tdelauncher] Another instance of tdelauncher is already running!" << endl; + return 1; + } + + // Wait a bit... + kdWarning() << "[tdelauncher] Waiting for already running tdelauncher to exit." << endl; + sleep(1); + + // Try again... + } + + TDELauncher *launcher = new TDELauncher(LAUNCHER_FD, args->isSet("new-startup")); + launcher->dcopClient()->setDefaultObject( name ); + launcher->dcopClient()->setDaemonMode( true ); + + TDECrash::setEmergencySaveFunction(sig_handler); + signal( SIGHUP, sig_handler); + signal( SIGPIPE, SIG_IGN); + signal( SIGTERM, sig_handler); + + launcher->exec(); + return 0; +} + diff --git a/tdeinit/tdestartupconfig.cpp b/tdeinit/tdestartupconfig.cpp new file mode 100644 index 000000000..373ac6787 --- /dev/null +++ b/tdeinit/tdestartupconfig.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 starttde +without the need to launch anything linked to KDE libraries (which may need +some time to load). + +The configuration options are written to $TDEHOME/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 $TDEHOME/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 tdeconf_update script is used to update such option, tdestartupconfig is run +before tdeconf_update and therefore cannot see the change in time. To avoid this +problem, together with the tdeconf_update script also the matching global config +file should be updated (any change, tdestartupconfig 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 tdedostartupconfig 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 tdehome[ 1024 ]; + if( getenv( "TDEHOME" )) + strlcpy( tdehome, getenv( "TDEHOME" ), 1024 ); + else if( getenv( "HOME" )) + { + strlcpy( tdehome, getenv( "HOME" ), 1024 ); + strlcat( tdehome, "/.trinity", 1024 ); + } + else + return 1; + char filename[ 1024 ]; + strlcpy( filename, tdehome, 1024 ); + strlcat( filename, "/share/config/startupconfig", 1024 ); + if( access( filename, R_OK ) != 0 ) + { + int ret = system( "tdedostartupconfig" ); + return WEXITSTATUS( ret ); + } + strlcpy( filename, tdehome, 1024 ); + strlcat( filename, "/share/config/startupconfigfiles", 1024 ); + struct stat st; + if( stat( filename, &st ) != 0 ) + { + int ret = system( "tdedostartupconfig" ); + return WEXITSTATUS( ret ); + } + time_t config_time = st.st_mtime; + FILE* config = fopen( filename, "r" ); + if( config == NULL ) + { + int ret = system( "tdedostartupconfig" ); + return WEXITSTATUS( ret ); + } + strlcpy( filename, tdehome, 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( "tdedostartupconfig" ); + return WEXITSTATUS( ret ); + } + return 0; + } diff --git a/tdeinit/tests/Makefile.am b/tdeinit/tests/Makefile.am new file mode 100644 index 000000000..e1157bf72 --- /dev/null +++ b/tdeinit/tests/Makefile.am @@ -0,0 +1,10 @@ +# $Id$ + +INCLUDES= -I$(srcdir)/../.. $(all_includes) +####### Files + +check_PROGRAMS = tdelaunchertest + +tdelaunchertest_SOURCES = tdelaunchertest.cpp +tdelaunchertest_LDADD = $(LIB_KIO) + diff --git a/tdeinit/tests/tdelaunchertest.cpp b/tdeinit/tests/tdelaunchertest.cpp new file mode 100644 index 000000000..07eaf21f7 --- /dev/null +++ b/tdeinit/tests/tdelaunchertest.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 <tqstring.h> +#include <tdeapplication.h> +#include <dcopclient.h> +#include <stdio.h> +#include <tqvaluelist.h> +#include <kservice.h> + + +int main(int argc, char *argv[]) +{ + TDEApplication::tdeinitExec("konsole"); + + TDEApplication k(argc, argv, "tdelaunchertest"); + + kapp->dcopClient()->registerAs( kapp->name()) ; + +#if 0 + TQString error; + TQCString dcopService; + int pid; + int result = TDEApplication::startServiceByDesktopName( + TQString::fromLatin1("konsole"), TQString::null, &error, &dcopService, &pid ); + + printf("Result = %d, error = \"%s\", dcopService = \"%s\", pid = %d\n", + result, error.ascii(), dcopService.data(), pid); + + result = TDEApplication::startServiceByDesktopName( + TQString::fromLatin1("konqueror"), TQString::null, &error, &dcopService, &pid ); + + printf("Result = %d, error = \"%s\", dcopService = \"%s\", pid = %d\n", + result, error.ascii(), dcopService.data(), pid); +#endif +} + diff --git a/tdeinit/wrapper.c b/tdeinit/wrapper.c new file mode 100644 index 000000000..d22ba5080 --- /dev/null +++ b/tdeinit/wrapper.c @@ -0,0 +1,546 @@ +/* + 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 "tdelauncher_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 tqglobal.h but instead distinguish + Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS + on the commandline (which in tqglobal.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("TDEHOME"); + char *display; + + sock_file[0] = sock_file[MAX_SOCK_FILE] = 0; + + if (!kde_home || !kde_home[0]) + { + kde_home = "~/.trinity/"; + } + + if (kde_home[0] == '~') + { + if (!home_dir || !home_dir[0]) + { + fprintf(stderr, "[tdeinit wrapper] Warning: $HOME not set!\n"); + return -1; + } + if (strlen(home_dir) > (MAX_SOCK_FILE-100)) + { + fprintf(stderr, "[tdeinit wrapper] 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( getenv("XAUTHLOCALHOSTNAME")) + strncat(sock_file, getenv("XAUTHLOCALHOSTNAME"), MAX_SOCK_FILE - strlen(sock_file) - 1); + else if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0) + { + perror("[tdeinit wrapper] Warning: Could not determine hostname: "); + return -1; + } + sock_file[sizeof(sock_file)-1] = '\0'; + + /* append $DISPLAY */ + display = getDisplay(); + if (display == NULL) + { + fprintf(stderr, "[tdeinit wrapper] Error: Could not determine display.\n"); + return -1; + } + + if (strlen(sock_file)+strlen(display)+strlen("/tdeinit_")+2 > MAX_SOCK_FILE) + { + fprintf(stderr, "[tdeinit wrapper] Warning: Socket name will be too long.\n"); + free (display); + return -1; + } + strcat(sock_file, "/tdeinit_"); + strcat(sock_file, display); + free(display); + + if (strlen(sock_file) >= sizeof(server.sun_path)) + { + fprintf(stderr, "[tdeinit wrapper] Warning: Path of socket file exceeds UNIX_PATH_MAX.\n"); + return -1; + } + + /* + * create the socket stream + */ + s = socket(PF_UNIX, SOCK_STREAM, 0); + if (s < 0) + { + perror("[tdeinit wrapper] Warning: socket creation 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("[tdeinit wrapper] Warning: socket connection 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; + tdelauncher_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, "tdeinit_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, "tdeinit_shutdown") == 0) + { + if( argc > 1) + { + fprintf(stderr, "[tdeinit wrapper] Usage: %s\n\n", start); + fprintf(stderr, "[tdeinit wrapper] Shuts down tdeinit master process and terminates all processes spawned from it.\n"); + exit( 255 ); + } + sock = openSocket(); + if( sock < 0 ) + { + fprintf( stderr, "[tdeinit wrapper] Error: Can't contact tdeinit!\n" ); + exit( 255 ); + } + header.cmd = LAUNCHER_TERMINATE_KDE; + header.arg_length = 0; + write_socket(sock, (char *) &header, sizeof(header)); + return 0; + } + + if (wrapper || ext_wrapper || kwrapper) + { + argv++; + argc--; + if (argc < 1) + { + fprintf(stderr, "[tdeinit wrapper] Usage: %s <application> [<args>]\n", start); + exit(255); /* usage should be documented somewhere ... */ + } + start = argv[0]; + } + + sock = openSocket(); + if( sock < 0 ) /* couldn't contact tdeinit, start argv[ 0 ] directly */ + { + execvp( argv[ 0 ], argv ); + fprintf( stderr, "[tdeinit wrapper] 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, "[tdeinit wrapper] 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, "[tdeinit wrapper] Oops. Invalid format.\n"); + exit(255); + } + + write_socket(sock, buffer, size); + free( buffer ); + + if (read_socket(sock, (char *) &header, sizeof(header))==-1) + { + fprintf(stderr, "[tdeinit wrapper] Communication error.\n"); + exit(255); + } + + if (header.cmd == LAUNCHER_OK) + { + long pid; + buffer = (char *) malloc(header.arg_length); + if (buffer == NULL) + { + fprintf(stderr, "[tdeinit wrapper] Error: malloc() failed\n"); + exit(255); + } + read_socket(sock, buffer, header.arg_length); + pid = *((long *) buffer); + if ( !(!kwrapper) ) + kwrapper_run( pid ); + } + else if (header.cmd == LAUNCHER_ERROR) + { + fprintf(stderr, "[tdeinit wrapper] Could not launch '%s'.\n", start); + exit(255); + } + else + { + fprintf(stderr, "[tdeinit wrapper] Unexpected response (response = %ld).\n", header.cmd); + exit(255); + } + exit(0); +} |