1/*
2 This file is part of the KDE libraries
3 Copyright (c) 1999 Waldo Bastian <bastian@kde.org>
4 (c) 1999 Mario Weilguni <mweilguni@sime.com>
5 (c) 2001 Lubos Lunak <l.lunak@kde.org>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License version 2 as published by the Free Software Foundation.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include <config.h>
23#include <config-kstandarddirs.h>
24
25#include "klauncher_cmds.h"
26
27#include <sys/types.h>
28#include <sys/param.h>
29#include <sys/socket.h>
30#include <sys/stat.h>
31#include <sys/un.h>
32
33#include <errno.h>
34#include <string.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <unistd.h>
38#include <pwd.h>
39#include <signal.h>
40
41extern char **environ;
42
43static char *getDisplay()
44{
45 const char *display;
46 char *result;
47 char *screen;
48 char *colon;
49 char *i;
50
51/*
52 don't test for a value from qglobal.h but instead distinguish
53 Qt/X11 from Qt/Embedded by the fact that Qt/E apps have -DQWS
54 on the commandline (which in qglobal.h however triggers Q_WS_QWS,
55 but we don't want to include that here) (Simon)
56#ifdef Q_WS_X11
57 */
58#if defined(NO_DISPLAY)
59 display = "NODISPLAY";
60#elif !defined(QWS)
61 display = getenv("DISPLAY");
62#else
63 display = getenv("QWS_DISPLAY");
64#endif
65 if (!display || !*display)
66 {
67 display = ":0";
68 }
69 result = (char*)malloc(strlen(display)+1);
70 if (result == NULL)
71 return NULL;
72
73 strcpy(result, display);
74 screen = strrchr(result, '.');
75 colon = strrchr(result, ':');
76 if (screen && (screen > colon))
77 *screen = '\0';
78 while((i = strchr(result, ':')))
79 *i = '_';
80#ifdef __APPLE__
81 while((i = strchr(result, '/')))
82 *i = '_';
83#endif
84 return result;
85}
86
87/*
88 * Write 'len' bytes from 'buffer' into 'sock'.
89 * returns 0 on success, -1 on failure.
90 */
91static int write_socket(int sock, char *buffer, int len)
92{
93 ssize_t result;
94 int bytes_left = len;
95 while ( bytes_left > 0)
96 {
97 result = write(sock, buffer, bytes_left);
98 if (result > 0)
99 {
100 buffer += result;
101 bytes_left -= result;
102 }
103 else if (result == 0)
104 return -1;
105 else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
106 return -1;
107 }
108 return 0;
109}
110
111/*
112 * Read 'len' bytes from 'sock' into 'buffer'.
113 * returns 0 on success, -1 on failure.
114 */
115static int read_socket(int sock, char *buffer, int len)
116{
117 ssize_t result;
118 int bytes_left = len;
119 while ( bytes_left > 0)
120 {
121 result = read(sock, buffer, bytes_left);
122 if (result > 0)
123 {
124 buffer += result;
125 bytes_left -= result;
126 }
127 else if (result == 0)
128 return -1;
129 else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN))
130 return -1;
131 }
132 return 0;
133}
134
135static int openSocket()
136{
137 kde_socklen_t socklen;
138 int s;
139 struct sockaddr_un server;
140#define MAX_SOCK_FILE 255
141 char sock_file[MAX_SOCK_FILE + 1];
142 const char *home_dir = getenv("HOME");
143 const char *kde_home = getenv("KDEHOME");
144 char *display;
145
146 sock_file[0] = sock_file[MAX_SOCK_FILE] = 0;
147
148 if (!kde_home || !kde_home[0])
149 {
150 kde_home = "~/" KDE_DEFAULT_HOME "/";
151 }
152
153 if (kde_home[0] == '~')
154 {
155 if (!home_dir || !home_dir[0])
156 {
157 fprintf(stderr, "Warning: $HOME not set!\n");
158 return -1;
159 }
160 if (strlen(home_dir) > (MAX_SOCK_FILE-100))
161 {
162 fprintf(stderr, "Warning: Home directory path too long!\n");
163 return -1;
164 }
165 kde_home++;
166 strncpy(sock_file, home_dir, MAX_SOCK_FILE);
167 }
168 strncat(sock_file, kde_home, MAX_SOCK_FILE - strlen(sock_file));
169
170 /** Strip trailing '/' **/
171 if ( sock_file[strlen(sock_file)-1] == '/')
172 sock_file[strlen(sock_file)-1] = 0;
173
174 strncat(sock_file, "/socket-", MAX_SOCK_FILE - strlen(sock_file));
175 if (gethostname(sock_file+strlen(sock_file), MAX_SOCK_FILE - strlen(sock_file) - 1) != 0)
176 {
177 perror("Warning: Could not determine hostname: ");
178 return -1;
179 }
180 sock_file[sizeof(sock_file)-1] = '\0';
181
182 /* append $DISPLAY */
183 display = getDisplay();
184#if !defined (NO_DISPLAY)
185 if (display == NULL)
186 {
187 fprintf(stderr, "Error: Could not determine display.\n");
188 return -1;
189 }
190#endif
191
192 if (strlen(sock_file)+strlen(display)+strlen("/kdeinit4_")+2 > MAX_SOCK_FILE)
193 {
194 fprintf(stderr, "Warning: Socket name will be too long.\n");
195 free (display);
196 return -1;
197 }
198 strcat(sock_file, "/kdeinit4_");
199#if !defined (NO_DISPLAY)
200 strcat(sock_file, display);
201 free(display);
202#endif
203
204 if (strlen(sock_file) >= sizeof(server.sun_path))
205 {
206 fprintf(stderr, "Warning: Path of socketfile exceeds UNIX_PATH_MAX.\n");
207 return -1;
208 }
209
210 /*
211 * create the socket stream
212 */
213 s = socket(PF_UNIX, SOCK_STREAM, 0);
214 if (s < 0)
215 {
216 perror("Warning: socket() failed: ");
217 return -1;
218 }
219
220 server.sun_family = AF_UNIX;
221 strcpy(server.sun_path, sock_file);
222 socklen = sizeof(server);
223 if(connect(s, (struct sockaddr *)&server, socklen) == -1)
224 {
225 fprintf(stderr, "kdeinit4_wrapper: Warning: connect(%s) failed:", sock_file);
226 perror(" ");
227 close(s);
228 return -1;
229 }
230 return s;
231}
232
233static pid_t kwrapper_pid;
234
235static void sig_pass_handler( int signo );
236static void setup_signals( void );
237
238static void setup_signal_handler( int signo, int clean )
239{
240 struct sigaction sa;
241 if( clean )
242 sa.sa_handler = SIG_DFL;
243 else
244 sa.sa_handler = sig_pass_handler;
245 sigemptyset( &sa.sa_mask );
246 sigaddset( &sa.sa_mask, signo );
247 sa.sa_flags = 0; /* don't use SA_RESTART */
248 sigaction( signo, &sa, 0 );
249}
250
251static void sig_pass_handler( int signo )
252{
253 int save_errno = errno;
254 if( signo == SIGTSTP )
255 kill( kwrapper_pid, SIGSTOP ); /* pass the signal to the real process */
256 else /* SIGTSTP wouldn't work ... I don't think is much */
257 kill( kwrapper_pid, signo ); /* of a problem */
258
259 if( signo == SIGCONT )
260 setup_signals(); /* restore signals */
261 else if( signo == SIGCHLD )
262 ; /* nothing, ignore */
263 else /* do the default action ( most of them quit the app ) */
264 {
265 setup_signal_handler( signo, 1 );
266 raise( signo ); /* handle the signal again */
267 }
268
269 errno = save_errno;
270}
271
272static void setup_signals()
273{
274 setup_signal_handler( SIGHUP, 0 );
275 setup_signal_handler( SIGINT, 0 );
276 setup_signal_handler( SIGQUIT, 0 );
277 setup_signal_handler( SIGILL, 0 ); /* e.g. this one is probably doesn't make sense to pass */
278 setup_signal_handler( SIGABRT, 0 ); /* but anyway ... */
279 setup_signal_handler( SIGFPE, 0 );
280 /* SIGKILL can't be handled :( */
281 setup_signal_handler( SIGSEGV, 0 );
282 setup_signal_handler( SIGPIPE, 0 );
283 setup_signal_handler( SIGALRM, 0 );
284 setup_signal_handler( SIGTERM, 0 );
285 setup_signal_handler( SIGUSR1, 0 );
286 setup_signal_handler( SIGUSR2, 0 );
287 setup_signal_handler( SIGCHLD, 0 ); /* is this a good idea ??? */
288 setup_signal_handler( SIGCONT, 0 ); /* SIGSTOP can't be handled, but SIGTSTP and SIGCONT can */
289 /* SIGSTOP */ /* which should be enough */
290 setup_signal_handler( SIGTSTP, 0 );
291 setup_signal_handler( SIGTTIN, 0 ); /* is this a good idea ??? */
292 setup_signal_handler( SIGTTOU, 0 ); /* is this a good idea ??? */
293 /* some more ? */
294}
295
296static int kwrapper_run( pid_t wrapped, int sock )
297{
298 klauncher_header header;
299 char *buffer;
300 long pid, status;
301
302 kwrapper_pid = wrapped;
303 setup_signals();
304
305 read_socket(sock, (char *)&header, sizeof(header));
306
307 if (header.cmd != LAUNCHER_CHILD_DIED)
308 {
309 fprintf(stderr, "Unexpected response from KInit (response = %ld).\n", header.cmd);
310 exit(255);
311 }
312
313 buffer = (char *) malloc(header.arg_length);
314 if (buffer == NULL)
315 {
316 fprintf(stderr, "Error: malloc() failed\n");
317 exit(255);
318 }
319
320 read_socket(sock, buffer, header.arg_length);
321 pid = ((long *) buffer)[0];
322 if( pid != kwrapper_pid)
323 {
324 fprintf(stderr, "Unexpected LAUNCHER_CHILD_DIED from KInit - pid = %ld\n", pid);
325 exit(255);
326 }
327
328 status = ((long *) buffer)[1];
329 free(buffer);
330 return (int) status;
331}
332
333int main(int argc, char **argv)
334{
335 int i;
336 int wrapper = 0;
337 int ext_wrapper = 0;
338 int kwrapper = 0;
339 long arg_count;
340 long env_count;
341 klauncher_header header;
342 char *start, *p, *buffer;
343 char cwd[8192];
344 const char *tty = NULL;
345 long avoid_loops = 0;
346 const char* startup_id = NULL;
347 int sock;
348
349 long size = 0;
350
351 start = argv[0];
352 p = start + strlen(argv[0]);
353 while (--p > start)
354 {
355 if (*p == '/') break;
356 }
357 if ( p > start)
358 p++;
359 start = p;
360
361 if (strcmp(start, "kdeinit4_wrapper") == 0)
362 wrapper = 1;
363 else if (strcmp(start, "kshell4") == 0)
364 ext_wrapper = 1;
365 else if (strcmp(start, "kwrapper4") == 0)
366 kwrapper = 1;
367 else if (strcmp(start, "kdeinit4_shutdown") == 0)
368 {
369 if( argc > 1)
370 {
371 fprintf(stderr, "Usage: %s\n\n", start);
372 fprintf(stderr, "Shuts down kdeinit4 master process and terminates all processes spawned from it.\n");
373 exit( 255 );
374 }
375 sock = openSocket();
376 if( sock < 0 )
377 {
378 fprintf( stderr, "Error: Can not contact kdeinit4!\n" );
379 exit( 255 );
380 }
381 header.cmd = LAUNCHER_TERMINATE_KDE;
382 header.arg_length = 0;
383 write_socket(sock, (char *) &header, sizeof(header));
384 read_socket(sock, (char *) &header, 1); /* wait for the socket to close */
385 return 0;
386 }
387
388 if (wrapper || ext_wrapper || kwrapper)
389 {
390 argv++;
391 argc--;
392 if (argc < 1)
393 {
394 fprintf(stderr, "Usage: %s <application> [<args>]\n", start);
395 exit(255); /* usage should be documented somewhere ... */
396 }
397 start = argv[0];
398 }
399
400 sock = openSocket();
401 if( sock < 0 ) /* couldn't contact kdeinit4, start argv[ 0 ] directly */
402 {
403 execvp( argv[ 0 ], argv );
404 fprintf( stderr, "Error: Can not run %s !\n", argv[ 0 ] );
405 exit( 255 );
406 }
407
408 if( !wrapper && !ext_wrapper && !kwrapper )
409 { /* was called as a symlink */
410 avoid_loops = 1;
411#if defined(WE_ARE_KWRAPPER)
412 kwrapper = 1;
413#elif defined(WE_ARE_KSHELL)
414 ext_wrapper = 1;
415#else
416 wrapper = 1;
417#endif
418 }
419
420 arg_count = argc;
421 env_count = 0;
422
423 size += sizeof(long); /* Number of arguments*/
424
425 size += strlen(start)+1; /* Size of first argument. */
426
427 for(i = 1; i < argc; i++)
428 {
429 size += strlen(argv[i])+1;
430 }
431 if( wrapper )
432 {
433 size += sizeof(long); /* empty envs */
434 }
435 if (ext_wrapper || kwrapper)
436 {
437 if (!getcwd(cwd, 8192))
438 cwd[0] = '\0';
439 size += strlen(cwd)+1;
440
441 size += sizeof(long); /* Number of env.vars. */
442
443 for(; environ[env_count] ; env_count++)
444 {
445 int l = strlen(environ[env_count])+1;
446 size += l;
447 }
448
449 if( kwrapper )
450 {
451 tty = ttyname(1);
452 if (!tty || !isatty(2))
453 tty = "";
454 size += strlen(tty)+1;
455 }
456 }
457
458 size += sizeof( avoid_loops );
459
460 if( !wrapper )
461 {
462 startup_id = getenv( "DESKTOP_STARTUP_ID" );
463 if( startup_id == NULL )
464 startup_id = "";
465 size += strlen( startup_id ) + 1;
466 }
467
468 if (wrapper)
469 header.cmd = LAUNCHER_EXEC_NEW;
470 else if (kwrapper)
471 header.cmd = LAUNCHER_KWRAPPER;
472 else
473 header.cmd = LAUNCHER_SHELL;
474 header.arg_length = size;
475 write_socket(sock, (char *) &header, sizeof(header));
476
477 buffer = (char *) malloc(size);
478 if (buffer == NULL)
479 {
480 fprintf(stderr, "Error: malloc() failed.");
481 exit(255);
482 }
483 p = buffer;
484
485 memcpy(p, &arg_count, sizeof(arg_count));
486 p += sizeof(arg_count);
487
488 memcpy(p, start, strlen(start)+1);
489 p += strlen(start)+1;
490
491 for(i = 1; i < argc; i++)
492 {
493 memcpy(p, argv[i], strlen(argv[i])+1);
494 p += strlen(argv[i])+1;
495 }
496
497 if( wrapper )
498 {
499 long dummy = 0;
500 memcpy(p, &dummy, sizeof(dummy)); /* empty envc */
501 p+= sizeof( dummy );
502 }
503 if (ext_wrapper || kwrapper)
504 {
505 memcpy(p, cwd, strlen(cwd)+1);
506 p+= strlen(cwd)+1;
507
508 memcpy(p, &env_count, sizeof(env_count));
509 p+= sizeof(env_count);
510
511 for(i = 0; i < env_count; i++)
512 {
513 int l = strlen(environ[i])+1;
514 memcpy(p, environ[i], l);
515 p += l;
516 }
517
518 if( kwrapper )
519 {
520 memcpy(p, tty, strlen(tty)+1);
521 p+=strlen(tty)+1;
522 }
523 }
524
525 memcpy( p, &avoid_loops, sizeof( avoid_loops ));
526 p += sizeof( avoid_loops );
527
528 if( !wrapper )
529 {
530 memcpy(p, startup_id, strlen(startup_id)+1);
531 p+= strlen(startup_id)+1;
532 }
533
534 if( p - buffer != size ) /* should fail only if you change this source and do */
535 /* a stupid mistake, it should be assert() actually */
536 {
537 fprintf(stderr, "Oops. Invalid format.\n");
538 exit(255);
539 }
540
541 write_socket(sock, buffer, size);
542 free( buffer );
543
544 if (read_socket(sock, (char *) &header, sizeof(header))==-1)
545 {
546 fprintf(stderr, "Communication error with KInit.\n");
547 exit(255);
548 }
549
550 if (header.cmd == LAUNCHER_OK)
551 {
552 long pid;
553 buffer = (char *) malloc(header.arg_length);
554 if (buffer == NULL)
555 {
556 fprintf(stderr, "Error: malloc() failed\n");
557 exit(255);
558 }
559 read_socket(sock, buffer, header.arg_length);
560 pid = *((long *) buffer);
561 if( !kwrapper ) /* kwrapper shouldn't print any output */
562 printf("Launched ok, pid = %ld\n", pid);
563 else
564 exit( kwrapper_run( pid, sock ) );
565 }
566 else if (header.cmd == LAUNCHER_ERROR)
567 {
568 fprintf(stderr, "KInit could not launch '%s'.\n", start);
569 exit(255);
570 }
571 else
572 {
573 fprintf(stderr, "Unexpected response from KInit (response = %ld).\n", header.cmd);
574 exit(255);
575 }
576 exit(0);
577}
578