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 | |
41 | extern char **environ; |
42 | |
43 | static 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 | */ |
91 | static 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 | */ |
115 | static 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 | |
135 | static 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 | |
233 | static pid_t kwrapper_pid; |
234 | |
235 | static void sig_pass_handler( int signo ); |
236 | static void setup_signals( void ); |
237 | |
238 | static 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 | |
251 | static 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 | |
272 | static 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 | |
296 | static int kwrapper_run( pid_t wrapped, int sock ) |
297 | { |
298 | klauncher_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 | |
333 | int 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 ; |
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 | |