diff options
Diffstat (limited to 'tdeinit/wrapper.c')
-rw-r--r-- | tdeinit/wrapper.c | 546 |
1 files changed, 546 insertions, 0 deletions
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); +} |