1 | /* |
2 | * This file is part of the KDE libraries |
3 | * Copyright (c) 1999-2000 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 | #define QT_NO_CAST_FROM_ASCII |
23 | |
24 | #include <config.h> |
25 | #include <config-kdeinit.h> |
26 | |
27 | #include <sys/types.h> |
28 | #include <sys/time.h> |
29 | #include <sys/resource.h> |
30 | #include <sys/stat.h> |
31 | #include <sys/socket.h> |
32 | #include <sys/un.h> |
33 | #include <sys/wait.h> |
34 | #ifdef HAVE_SYS_SELECT_H |
35 | #include <sys/select.h> // Needed on some systems. |
36 | #endif |
37 | |
38 | #include <ctype.h> |
39 | #include <errno.h> |
40 | #include <fcntl.h> |
41 | #include "proctitle.h" |
42 | #include <signal.h> |
43 | #include <stdio.h> |
44 | #include <stdlib.h> |
45 | #include <string.h> |
46 | #include <unistd.h> |
47 | #include <locale.h> |
48 | |
49 | #include <QtCore/QLibrary> |
50 | #include <QtCore/QString> |
51 | #include <QtCore/QFile> |
52 | #include <QtCore/QDate> |
53 | #include <QtCore/QFileInfo> |
54 | #include <QtCore/QRegExp> |
55 | #include <QtGui/QFont> |
56 | #include <kcomponentdata.h> |
57 | #include <klibrary.h> |
58 | #include <kdemacros.h> |
59 | #include <kstandarddirs.h> |
60 | #include <kglobal.h> |
61 | #include <kconfig.h> |
62 | #include <kapplication.h> |
63 | #include <klocale.h> |
64 | #include <kdebug.h> |
65 | #include <kde_file.h> |
66 | #include <ksavefile.h> |
67 | |
68 | #ifdef Q_OS_LINUX |
69 | #include <sys/prctl.h> |
70 | #ifndef PR_SET_NAME |
71 | #define PR_SET_NAME 15 |
72 | #endif |
73 | #endif |
74 | |
75 | #ifdef Q_WS_MACX |
76 | #include <kkernel_mac.h> |
77 | #endif |
78 | |
79 | #include <kdeversion.h> |
80 | |
81 | #include "klauncher_cmds.h" |
82 | |
83 | #ifdef Q_WS_X11 |
84 | #include <X11/Xlib.h> |
85 | #include <X11/Xatom.h> |
86 | #include <fixx11h.h> |
87 | #include <kstartupinfo.h> |
88 | #endif |
89 | |
90 | #ifdef Q_WS_X11 |
91 | static const char *[] = { |
92 | "libkio.so.5" , |
93 | "libkparts.so.4" , |
94 | #ifdef __KDE_HAVE_GCC_VISIBILITY |
95 | "libplasma.so.3" |
96 | #endif |
97 | }; |
98 | #endif |
99 | |
100 | // #define SKIP_PROCTITLE 1 |
101 | |
102 | extern char **environ; |
103 | |
104 | #ifdef Q_WS_X11 |
105 | static int X11fd = -1; |
106 | static Display *X11display = 0; |
107 | static int X11_startup_notify_fd = -1; |
108 | static Display *X11_startup_notify_display = 0; |
109 | #endif |
110 | static KComponentData *s_instance = 0; |
111 | #define MAX_SOCK_FILE 255 |
112 | static char sock_file[MAX_SOCK_FILE]; |
113 | |
114 | #ifdef Q_WS_X11 |
115 | #define DISPLAY "DISPLAY" |
116 | #elif defined(Q_WS_QWS) |
117 | #define DISPLAY "QWS_DISPLAY" |
118 | #elif defined(Q_WS_MACX) |
119 | #define DISPLAY "MAC_DISPLAY" |
120 | #elif defined(Q_WS_WIN) |
121 | #define DISPLAY "WIN_DISPLAY" |
122 | #else |
123 | #error Use QT/X11 or QT/Embedded |
124 | #endif |
125 | |
126 | /* Group data */ |
127 | static struct { |
128 | int maxname; |
129 | int fd[2]; |
130 | int launcher[2]; /* socket pair for launcher communication */ |
131 | int deadpipe[2]; /* pipe used to detect dead children */ |
132 | int initpipe[2]; |
133 | int wrapper; /* socket for wrapper communication */ |
134 | int accepted_fd; /* socket accepted and that must be closed in the child process */ |
135 | char result; |
136 | int exit_status; |
137 | pid_t fork; |
138 | pid_t launcher_pid; |
139 | pid_t kded_pid; |
140 | int n; |
141 | char **argv; |
142 | int (*func)(int, char *[]); |
143 | int (*launcher_func)(int); |
144 | bool debug_wait; |
145 | QByteArray errorMsg; |
146 | bool launcher_ok; |
147 | bool suicide; |
148 | } d; |
149 | |
150 | struct child |
151 | { |
152 | pid_t pid; |
153 | int sock; /* fd to write message when child is dead*/ |
154 | struct child *next; |
155 | }; |
156 | |
157 | static struct child *children; |
158 | |
159 | #ifdef Q_WS_X11 |
160 | extern "C" { |
161 | int kdeinit_xio_errhandler( Display * ); |
162 | int kdeinit_x_errhandler( Display *, XErrorEvent *err ); |
163 | } |
164 | #endif |
165 | |
166 | #ifdef KDEINIT_OOM_PROTECT |
167 | static int oom_pipe = -1; |
168 | #endif |
169 | |
170 | /* |
171 | * Clean up the file descriptor table by closing all file descriptors |
172 | * that are still open. |
173 | * |
174 | * This function is called very early in the main() function, so that |
175 | * we don't leak anything that was leaked to us. |
176 | */ |
177 | static void cleanup_fds() |
178 | { |
179 | int maxfd = FD_SETSIZE; |
180 | struct rlimit rl; |
181 | if (getrlimit(RLIMIT_NOFILE, &rl) == 0) |
182 | maxfd = rl.rlim_max; |
183 | for (int fd = 3; fd < maxfd; ++fd) |
184 | { |
185 | #ifdef KDEINIT_OOM_PROTECT |
186 | if( fd != oom_pipe ) |
187 | #endif |
188 | close(fd); |
189 | } |
190 | } |
191 | |
192 | /* |
193 | * Close fd's which are only useful for the parent process. |
194 | * Restore default signal handlers. |
195 | */ |
196 | static void close_fds() |
197 | { |
198 | while (struct child *child = children) { |
199 | close(child->sock); |
200 | children = child->next; |
201 | free(child); |
202 | } |
203 | |
204 | if (d.deadpipe[0] != -1) |
205 | { |
206 | close(d.deadpipe[0]); |
207 | d.deadpipe[0] = -1; |
208 | } |
209 | |
210 | if (d.deadpipe[1] != -1) |
211 | { |
212 | close(d.deadpipe[1]); |
213 | d.deadpipe[1] = -1; |
214 | } |
215 | |
216 | if (d.initpipe[0] != -1) |
217 | { |
218 | close(d.initpipe[0]); |
219 | d.initpipe[0] = -1; |
220 | } |
221 | |
222 | if (d.initpipe[1] != -1) |
223 | { |
224 | close(d.initpipe[1]); |
225 | d.initpipe[1] = -1; |
226 | } |
227 | |
228 | if (d.launcher[0] != -1) |
229 | { |
230 | close(d.launcher[0]); |
231 | d.launcher[0] = -1; |
232 | } |
233 | if (d.wrapper != -1) |
234 | { |
235 | close(d.wrapper); |
236 | d.wrapper = -1; |
237 | } |
238 | if (d.accepted_fd != -1) |
239 | { |
240 | close(d.accepted_fd); |
241 | d.accepted_fd = -1; |
242 | } |
243 | #ifdef Q_WS_X11 |
244 | if (X11fd >= 0) |
245 | { |
246 | close(X11fd); |
247 | X11fd = -1; |
248 | } |
249 | if (X11_startup_notify_fd >= 0 && X11_startup_notify_fd != X11fd ) |
250 | { |
251 | close(X11_startup_notify_fd); |
252 | X11_startup_notify_fd = -1; |
253 | } |
254 | #endif |
255 | |
256 | KDE_signal(SIGCHLD, SIG_DFL); |
257 | KDE_signal(SIGPIPE, SIG_DFL); |
258 | } |
259 | |
260 | /* Notify wrapper program that the child it started has finished. */ |
261 | static void child_died(pid_t exit_pid, int exit_status) |
262 | { |
263 | struct child *child, **childptr = &children; |
264 | |
265 | while ((child = *childptr)) |
266 | { |
267 | if (child->pid == exit_pid) |
268 | { |
269 | /* Send a message with the return value of the child on the control socket */ |
270 | klauncher_header ; |
271 | long request_data[2]; |
272 | request_header.cmd = LAUNCHER_CHILD_DIED; |
273 | request_header.arg_length = sizeof(long) * 2; |
274 | request_data[0] = exit_pid; |
275 | request_data[1] = exit_status; |
276 | write(child->sock, &request_header, sizeof(request_header)); |
277 | write(child->sock, request_data, request_header.arg_length); |
278 | close(child->sock); |
279 | |
280 | *childptr = child->next; |
281 | free(child); |
282 | return; |
283 | } |
284 | |
285 | childptr = &child->next; |
286 | } |
287 | } |
288 | |
289 | |
290 | static void exitWithErrorMsg(const QString &errorMsg) |
291 | { |
292 | fprintf( stderr, "%s\n" , errorMsg.toLocal8Bit().data() ); |
293 | QByteArray utf8ErrorMsg = errorMsg.toUtf8(); |
294 | d.result = 3; // Error with msg |
295 | write(d.fd[1], &d.result, 1); |
296 | int l = utf8ErrorMsg.length(); |
297 | write(d.fd[1], &l, sizeof(int)); |
298 | write(d.fd[1], utf8ErrorMsg.data(), l); |
299 | close(d.fd[1]); |
300 | exit(255); |
301 | } |
302 | |
303 | static void setup_tty( const char* tty ) |
304 | { |
305 | if( tty == NULL || *tty == '\0' ) |
306 | return; |
307 | int fd = KDE_open( tty, O_WRONLY ); |
308 | if( fd < 0 ) |
309 | { |
310 | perror( "kdeinit4: could not open() tty" ); |
311 | return; |
312 | } |
313 | if( dup2( fd, STDOUT_FILENO ) < 0 ) |
314 | { |
315 | perror( "kdeinit4: could not dup2() stdout tty" ); |
316 | } |
317 | if( dup2( fd, STDERR_FILENO ) < 0 ) |
318 | { |
319 | perror( "kdeinit4: could not dup2() stderr tty" ); |
320 | } |
321 | close( fd ); |
322 | } |
323 | |
324 | // from kdecore/netwm.cpp |
325 | static int get_current_desktop( Display* disp ) |
326 | { |
327 | int desktop = 0; // no desktop by default |
328 | #ifdef Q_WS_X11 // Only X11 supports multiple desktops |
329 | Atom net_current_desktop = XInternAtom( disp, "_NET_CURRENT_DESKTOP" , False ); |
330 | Atom type_ret; |
331 | int format_ret; |
332 | unsigned char *data_ret; |
333 | unsigned long nitems_ret, unused; |
334 | if( XGetWindowProperty( disp, DefaultRootWindow( disp ), net_current_desktop, |
335 | 0l, 1l, False, XA_CARDINAL, &type_ret, &format_ret, &nitems_ret, &unused, &data_ret ) |
336 | == Success) |
337 | { |
338 | if (type_ret == XA_CARDINAL && format_ret == 32 && nitems_ret == 1) |
339 | desktop = *((long *) data_ret) + 1; |
340 | if (data_ret) |
341 | XFree ((char*) data_ret); |
342 | } |
343 | #endif |
344 | return desktop; |
345 | } |
346 | |
347 | // var has to be e.g. "DISPLAY=", i.e. with = |
348 | const char* get_env_var( const char* var, int envc, const char* envs ) |
349 | { |
350 | if( envc > 0 ) |
351 | { // get the var from envs |
352 | const char* env_l = envs; |
353 | int ln = strlen( var ); |
354 | for (int i = 0; i < envc; i++) |
355 | { |
356 | if( strncmp( env_l, var, ln ) == 0 ) |
357 | return env_l + ln; |
358 | while(*env_l != 0) env_l++; |
359 | env_l++; |
360 | } |
361 | } |
362 | return NULL; |
363 | } |
364 | |
365 | #ifdef Q_WS_X11 |
366 | static void init_startup_info( KStartupInfoId& id, const char* bin, |
367 | int envc, const char* envs ) |
368 | { |
369 | const char* dpy = get_env_var( DISPLAY"=" , envc, envs ); |
370 | // this may be called in a child, so it can't use display open using X11display |
371 | // also needed for multihead |
372 | X11_startup_notify_display = XOpenDisplay( dpy ); |
373 | if( X11_startup_notify_display == NULL ) |
374 | return; |
375 | X11_startup_notify_fd = XConnectionNumber( X11_startup_notify_display ); |
376 | KStartupInfoData data; |
377 | int desktop = get_current_desktop( X11_startup_notify_display ); |
378 | data.setDesktop( desktop ); |
379 | data.setBin(QFile::decodeName(bin)); |
380 | KStartupInfo::sendChangeX( X11_startup_notify_display, id, data ); |
381 | XFlush( X11_startup_notify_display ); |
382 | } |
383 | |
384 | static void complete_startup_info( KStartupInfoId& id, pid_t pid ) |
385 | { |
386 | if( X11_startup_notify_display == NULL ) |
387 | return; |
388 | if( pid == 0 ) // failure |
389 | KStartupInfo::sendFinishX( X11_startup_notify_display, id ); |
390 | else |
391 | { |
392 | KStartupInfoData data; |
393 | data.addPid( pid ); |
394 | data.setHostname(); |
395 | KStartupInfo::sendChangeX( X11_startup_notify_display, id, data ); |
396 | } |
397 | XCloseDisplay( X11_startup_notify_display ); |
398 | X11_startup_notify_display = NULL; |
399 | X11_startup_notify_fd = -1; |
400 | } |
401 | #endif |
402 | |
403 | QByteArray execpath_avoid_loops( const QByteArray& exec, int envc, const char* envs, bool avoid_loops ) |
404 | { |
405 | QStringList paths; |
406 | const QRegExp pathSepRegExp(QString::fromLatin1("[:\b]" )); |
407 | if( envc > 0 ) /* use the passed environment */ |
408 | { |
409 | const char* path = get_env_var( "PATH=" , envc, envs ); |
410 | if( path != NULL ) |
411 | paths = QFile::decodeName(path).split(pathSepRegExp); |
412 | } else { |
413 | paths = QString::fromLocal8Bit(qgetenv("PATH" )).split(pathSepRegExp, QString::KeepEmptyParts); |
414 | } |
415 | QString execpath = |
416 | s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(QLatin1String(":" ))); |
417 | if (avoid_loops && !execpath.isEmpty()) { |
418 | const int pos = execpath.lastIndexOf(QLatin1Char('/')); |
419 | const QString bin_path = execpath.left(pos); |
420 | for( QStringList::Iterator it = paths.begin(); |
421 | it != paths.end(); |
422 | ++it ) { |
423 | if( *it == bin_path || *it == bin_path + QLatin1Char('/')) { |
424 | paths.erase( it ); |
425 | break; // --> |
426 | } |
427 | } |
428 | execpath = s_instance->dirs()->findExe(QFile::decodeName(exec), paths.join(QLatin1String(":" ))); |
429 | } |
430 | return QFile::encodeName(execpath); |
431 | } |
432 | |
433 | #ifdef KDEINIT_OOM_PROTECT |
434 | static void oom_protect_sighandler( int ) { |
435 | } |
436 | |
437 | static void reset_oom_protect() { |
438 | if( oom_pipe <= 0 ) |
439 | return; |
440 | struct sigaction act, oldact; |
441 | act.sa_handler = oom_protect_sighandler; |
442 | act.sa_flags = 0; |
443 | sigemptyset( &act.sa_mask ); |
444 | sigaction( SIGUSR1, &act, &oldact ); |
445 | sigset_t sigs, oldsigs; |
446 | sigemptyset( &sigs ); |
447 | sigaddset( &sigs, SIGUSR1 ); |
448 | sigprocmask( SIG_BLOCK, &sigs, &oldsigs ); |
449 | pid_t pid = getpid(); |
450 | if( write( oom_pipe, &pid, sizeof( pid_t )) > 0 ) { |
451 | sigsuspend( &oldsigs ); // wait for the signal to come |
452 | } else { |
453 | #ifndef NDEBUG |
454 | fprintf( stderr, "Failed to reset OOM protection: %d\n" , pid ); |
455 | #endif |
456 | } |
457 | sigprocmask( SIG_SETMASK, &oldsigs, NULL ); |
458 | sigaction( SIGUSR1, &oldact, NULL ); |
459 | close( oom_pipe ); |
460 | oom_pipe = -1; |
461 | } |
462 | #else |
463 | static void reset_oom_protect() { |
464 | } |
465 | #endif |
466 | |
467 | static pid_t launch(int argc, const char *_name, const char *args, |
468 | const char *cwd=0, int envc=0, const char *envs=0, |
469 | bool reset_env = false, |
470 | const char *tty=0, bool avoid_loops = false, |
471 | const char* startup_id_str = "0" ) // krazy:exclude=doublequote_chars |
472 | { |
473 | QString lib; |
474 | QByteArray name; |
475 | QByteArray exec; |
476 | QString libpath; |
477 | QByteArray execpath; |
478 | |
479 | if (_name[0] != '/') { |
480 | name = _name; |
481 | lib = QFile::decodeName(name); |
482 | exec = name; |
483 | KLibrary klib(QLatin1String("libkdeinit4_" ) + lib, *s_instance ); |
484 | libpath = klib.fileName(); |
485 | if( libpath.isEmpty()) { |
486 | KLibrary klib(lib, *s_instance); |
487 | libpath = klib.fileName(); |
488 | } |
489 | execpath = execpath_avoid_loops(exec, envc, envs, avoid_loops); |
490 | } else { |
491 | name = _name; |
492 | lib = QFile::decodeName(name); |
493 | name = name.mid(name.lastIndexOf('/') + 1); |
494 | exec = _name; |
495 | if (lib.endsWith(QLatin1String(".so" ))) |
496 | libpath = lib; |
497 | else { |
498 | // try to match an absolute path to an executable binary (either in bin/ or in libexec/) |
499 | // to a kdeinit module in the same prefix |
500 | if( lib.contains( QLatin1String( "/lib" KDELIBSUFF "/kde4/libexec/" ))) { |
501 | libpath = QString( lib ).replace( QLatin1String( "/lib" KDELIBSUFF "/kde4/libexec/" ), |
502 | QLatin1String("/lib" KDELIBSUFF "/libkdeinit4_" )) + QLatin1String(".so" ); |
503 | } else if( lib.contains( QLatin1String( "/bin/" ))) { |
504 | libpath = QString( lib ).replace( QLatin1String( "/bin/" ), |
505 | QLatin1String("/lib" KDELIBSUFF "/libkdeinit4_" )) + QLatin1String(".so" ); |
506 | } |
507 | // Don't confuse the user with "Could not load libkdeinit4_foo.so" if it doesn't exist |
508 | if (!QFile::exists(libpath)) { |
509 | libpath.clear(); |
510 | } |
511 | execpath = exec; |
512 | } |
513 | } |
514 | #ifndef NDEBUG |
515 | fprintf(stderr,"kdeinit4: preparing to launch %s\n" , libpath.isEmpty() |
516 | ? execpath.constData() : libpath.toUtf8().constData()); |
517 | #endif |
518 | if (!args) { |
519 | argc = 1; |
520 | } |
521 | |
522 | if (0 > pipe(d.fd)) |
523 | { |
524 | perror("kdeinit4: pipe() failed" ); |
525 | d.result = 3; |
526 | d.errorMsg = i18n("Unable to start new process.\n" |
527 | "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." ).toUtf8(); |
528 | d.fork = 0; |
529 | return d.fork; |
530 | } |
531 | |
532 | #ifdef Q_WS_X11 |
533 | KStartupInfoId startup_id; |
534 | startup_id.initId( startup_id_str ); |
535 | if( !startup_id.none()) |
536 | init_startup_info( startup_id, name, envc, envs ); |
537 | #endif |
538 | // find out this path before forking, doing it afterwards |
539 | // crashes on some platforms, notably OSX |
540 | #ifdef Q_WS_MAC |
541 | const QString bundlepath = s_instance->dirs()->findExe(QFile::decodeName(execpath)); |
542 | #endif |
543 | |
544 | d.errorMsg = 0; |
545 | d.fork = fork(); |
546 | switch(d.fork) { |
547 | case -1: |
548 | perror("kdeinit4: fork() failed" ); |
549 | d.result = 3; |
550 | d.errorMsg = i18n("Unable to create new process.\n" |
551 | "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." ).toUtf8(); |
552 | close(d.fd[0]); |
553 | close(d.fd[1]); |
554 | d.fork = 0; |
555 | break; |
556 | case 0: |
557 | { |
558 | /** Child **/ |
559 | close(d.fd[0]); |
560 | close_fds(); |
561 | reset_oom_protect(); |
562 | |
563 | // Try to chdir, either to the requested directory or to the user's document path by default. |
564 | // We ignore errors - if you write a desktop file with Exec=foo and Path=/doesnotexist, |
565 | // we still want to execute `foo` even if the chdir() failed. |
566 | if (cwd && *cwd) { |
567 | (void)chdir(cwd); |
568 | } |
569 | |
570 | if( reset_env ) // KWRAPPER/SHELL |
571 | { |
572 | |
573 | QList<QByteArray> unset_envs; |
574 | for( int tmp_env_count = 0; |
575 | environ[tmp_env_count]; |
576 | tmp_env_count++) |
577 | unset_envs.append( environ[ tmp_env_count ] ); |
578 | foreach(const QByteArray &tmp, unset_envs) |
579 | { |
580 | int pos = tmp.indexOf( '=' ); |
581 | if( pos >= 0 ) |
582 | unsetenv( tmp.left( pos )); |
583 | } |
584 | } |
585 | |
586 | for (int i = 0; i < envc; i++) |
587 | { |
588 | putenv((char *)envs); |
589 | while(*envs != 0) envs++; |
590 | envs++; |
591 | } |
592 | |
593 | #ifdef Q_WS_X11 |
594 | if( startup_id.none()) |
595 | KStartupInfo::resetStartupEnv(); |
596 | else |
597 | startup_id.setupStartupEnv(); |
598 | #endif |
599 | { |
600 | int r; |
601 | QByteArray procTitle; |
602 | d.argv = (char **) malloc(sizeof(char *) * (argc+1)); |
603 | d.argv[0] = (char *) _name; |
604 | #ifdef Q_WS_MAC |
605 | QString argvexe = s_instance->dirs()->findExe(QString::fromLatin1(d.argv[0])); |
606 | if (!argvexe.isEmpty()) { |
607 | QByteArray cstr = argvexe.toLocal8Bit(); |
608 | kDebug(7016) << "kdeinit4: launch() setting argv: " << cstr.data(); |
609 | d.argv[0] = strdup(cstr.data()); |
610 | } |
611 | #endif |
612 | for (int i = 1; i < argc; i++) |
613 | { |
614 | d.argv[i] = (char *) args; |
615 | procTitle += ' '; |
616 | procTitle += (char *) args; |
617 | while(*args != 0) args++; |
618 | args++; |
619 | } |
620 | d.argv[argc] = 0; |
621 | |
622 | #ifndef SKIP_PROCTITLE |
623 | /** Give the process a new name **/ |
624 | #ifdef Q_OS_LINUX |
625 | /* set the process name, so that killall works like intended */ |
626 | r = prctl(PR_SET_NAME, (unsigned long) name.data(), 0, 0, 0); |
627 | if ( r == 0 ) |
628 | proctitle_set( "%s [kdeinit]%s" , name.data(), procTitle.data() ? procTitle.data() : "" ); |
629 | else |
630 | proctitle_set( "kdeinit4: %s%s" , name.data(), procTitle.data() ? procTitle.data() : "" ); |
631 | #else |
632 | proctitle_set( "kdeinit4: %s%s" , name.data(), procTitle.data() ? procTitle.data() : "" ); |
633 | #endif |
634 | #endif |
635 | } |
636 | |
637 | if (libpath.isEmpty() && execpath.isEmpty()) |
638 | { |
639 | QString errorMsg = i18n("Could not find '%1' executable." , QFile::decodeName(_name)); |
640 | exitWithErrorMsg(errorMsg); |
641 | } |
642 | |
643 | |
644 | if ( !qgetenv("KDE_IS_PRELINKED" ).isEmpty() && !execpath.isEmpty()) |
645 | libpath.truncate(0); |
646 | |
647 | QLibrary l(libpath); |
648 | |
649 | if ( !libpath.isEmpty() ) |
650 | { |
651 | if (!l.load() || !l.isLoaded() ) |
652 | { |
653 | QString ltdlError (l.errorString()); |
654 | if (execpath.isEmpty()) |
655 | { |
656 | // Error |
657 | QString errorMsg = i18n("Could not open library '%1'.\n%2" , libpath, ltdlError); |
658 | exitWithErrorMsg(errorMsg); |
659 | } |
660 | else |
661 | { |
662 | // Print warning |
663 | fprintf(stderr, "Could not open library %s: %s\n" , qPrintable(lib), |
664 | qPrintable(ltdlError) ); |
665 | } |
666 | } |
667 | } |
668 | if (!l.isLoaded()) |
669 | { |
670 | d.result = 2; // Try execing |
671 | write(d.fd[1], &d.result, 1); |
672 | |
673 | // We set the close on exec flag. |
674 | // Closing of d.fd[1] indicates that the execvp succeeded! |
675 | fcntl(d.fd[1], F_SETFD, FD_CLOEXEC); |
676 | |
677 | setup_tty( tty ); |
678 | |
679 | QByteArray executable = execpath; |
680 | #ifdef Q_WS_MAC |
681 | if (!bundlepath.isEmpty()) |
682 | executable = QFile::encodeName(bundlepath); |
683 | #endif |
684 | |
685 | if (!executable.isEmpty()) |
686 | execvp(executable, d.argv); |
687 | |
688 | d.result = 1; // Error |
689 | write(d.fd[1], &d.result, 1); |
690 | close(d.fd[1]); |
691 | exit(255); |
692 | } |
693 | |
694 | void * sym = l.resolve( "kdeinitmain" ); |
695 | if (!sym ) |
696 | { |
697 | sym = l.resolve( "kdemain" ); |
698 | if ( !sym ) |
699 | { |
700 | QString ltdlError = l.errorString(); |
701 | fprintf(stderr, "Could not find kdemain: %s\n" , qPrintable(ltdlError) ); |
702 | QString errorMsg = i18n("Could not find 'kdemain' in '%1'.\n%2" , |
703 | libpath, ltdlError); |
704 | exitWithErrorMsg(errorMsg); |
705 | } |
706 | } |
707 | |
708 | d.result = 0; // Success |
709 | write(d.fd[1], &d.result, 1); |
710 | close(d.fd[1]); |
711 | |
712 | d.func = (int (*)(int, char *[])) sym; |
713 | if (d.debug_wait) |
714 | { |
715 | fprintf(stderr, "kdeinit4: Suspending process\n" |
716 | "kdeinit4: 'gdb kdeinit4 %d' to debug\n" |
717 | "kdeinit4: 'kill -SIGCONT %d' to continue\n" , |
718 | getpid(), getpid()); |
719 | kill(getpid(), SIGSTOP); |
720 | } |
721 | else |
722 | { |
723 | setup_tty( tty ); |
724 | } |
725 | |
726 | exit( d.func(argc, d.argv)); /* Launch! */ |
727 | |
728 | break; |
729 | } |
730 | default: |
731 | /** Parent **/ |
732 | close(d.fd[1]); |
733 | bool exec = false; |
734 | for(;;) |
735 | { |
736 | d.n = read(d.fd[0], &d.result, 1); |
737 | if (d.n == 1) |
738 | { |
739 | if (d.result == 2) |
740 | { |
741 | #ifndef NDEBUG |
742 | //fprintf(stderr, "kdeinit4: no kdeinit module, trying exec....\n"); |
743 | #endif |
744 | exec = true; |
745 | continue; |
746 | } |
747 | if (d.result == 3) |
748 | { |
749 | int l = 0; |
750 | d.n = read(d.fd[0], &l, sizeof(int)); |
751 | if (d.n == sizeof(int)) |
752 | { |
753 | QByteArray tmp; |
754 | tmp.resize(l+1); |
755 | d.n = read(d.fd[0], tmp.data(), l); |
756 | tmp[l] = 0; |
757 | if (d.n == l) |
758 | d.errorMsg = tmp; |
759 | } |
760 | } |
761 | // Finished |
762 | break; |
763 | } |
764 | if (d.n == -1) |
765 | { |
766 | if (errno == ECHILD) { // a child died. |
767 | continue; |
768 | } |
769 | if (errno == EINTR || errno == EAGAIN) { // interrupted or more to read |
770 | continue; |
771 | } |
772 | } |
773 | if (d.n == 0) |
774 | { |
775 | if (exec) { |
776 | d.result = 0; |
777 | } else { |
778 | fprintf(stderr,"kdeinit4: (%s %s) Pipe closed unexpectedly" , name.constData(), execpath.constData()); |
779 | perror("kdeinit4: Pipe closed unexpectedly" ); |
780 | d.result = 1; // Error |
781 | } |
782 | break; |
783 | } |
784 | perror("kdeinit4: Error reading from pipe" ); |
785 | d.result = 1; // Error |
786 | break; |
787 | } |
788 | close(d.fd[0]); |
789 | } |
790 | #ifdef Q_WS_X11 |
791 | if( !startup_id.none()) |
792 | { |
793 | if( d.fork && d.result == 0 ) // launched successfully |
794 | complete_startup_info( startup_id, d.fork ); |
795 | else // failure, cancel ASN |
796 | complete_startup_info( startup_id, 0 ); |
797 | } |
798 | #endif |
799 | return d.fork; |
800 | } |
801 | |
802 | extern "C" { |
803 | |
804 | static void sig_child_handler(int) |
805 | { |
806 | /* |
807 | * Write into the pipe of death. |
808 | * This way we are sure that we return from the select() |
809 | * |
810 | * A signal itself causes select to return as well, but |
811 | * this creates a race-condition in case the signal arrives |
812 | * just before we enter the select. |
813 | */ |
814 | char c = 0; |
815 | write(d.deadpipe[1], &c, 1); |
816 | } |
817 | |
818 | } |
819 | |
820 | static void init_signals() |
821 | { |
822 | struct sigaction act; |
823 | long options; |
824 | |
825 | if (pipe(d.deadpipe) != 0) |
826 | { |
827 | perror("kdeinit4: Aborting. Can not create pipe" ); |
828 | exit(255); |
829 | } |
830 | |
831 | options = fcntl(d.deadpipe[0], F_GETFL); |
832 | if (options == -1) |
833 | { |
834 | perror("kdeinit4: Aborting. Can not make pipe non-blocking" ); |
835 | exit(255); |
836 | } |
837 | |
838 | if (fcntl(d.deadpipe[0], F_SETFL, options | O_NONBLOCK) == -1) |
839 | { |
840 | perror("kdeinit4: Aborting. Can not make pipe non-blocking" ); |
841 | exit(255); |
842 | } |
843 | |
844 | /* |
845 | * A SIGCHLD handler is installed which sends a byte into the |
846 | * pipe of death. This is to ensure that a dying child causes |
847 | * an exit from select(). |
848 | */ |
849 | act.sa_handler=sig_child_handler; |
850 | sigemptyset(&(act.sa_mask)); |
851 | sigaddset(&(act.sa_mask), SIGCHLD); |
852 | sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L); |
853 | act.sa_flags = SA_NOCLDSTOP; |
854 | |
855 | // CC: take care of SunOS which automatically restarts interrupted system |
856 | // calls (and thus does not have SA_RESTART) |
857 | |
858 | #ifdef SA_RESTART |
859 | act.sa_flags |= SA_RESTART; |
860 | #endif |
861 | sigaction( SIGCHLD, &act, 0L); |
862 | |
863 | act.sa_handler=SIG_IGN; |
864 | sigemptyset(&(act.sa_mask)); |
865 | sigaddset(&(act.sa_mask), SIGPIPE); |
866 | sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0L); |
867 | act.sa_flags = 0; |
868 | sigaction( SIGPIPE, &act, 0L); |
869 | } |
870 | |
871 | static void init_kdeinit_socket() |
872 | { |
873 | struct sockaddr_un sa; |
874 | kde_socklen_t socklen; |
875 | long options; |
876 | const QByteArray home_dir = qgetenv("HOME" ); |
877 | int max_tries = 10; |
878 | if (home_dir.isEmpty()) |
879 | { |
880 | fprintf(stderr, "kdeinit4: Aborting. $HOME not set!" ); |
881 | exit(255); |
882 | } |
883 | if (chdir(home_dir) != 0) { |
884 | fprintf(stderr, "kdeinit4: Aborting. Couldn't enter '%s'!" , home_dir.constData()); |
885 | exit(255); |
886 | } |
887 | |
888 | { |
889 | QByteArray path = home_dir; |
890 | QByteArray readOnly = qgetenv("KDE_HOME_READONLY" ); |
891 | if (access(path.data(), R_OK|W_OK)) |
892 | { |
893 | if (errno == ENOENT) |
894 | { |
895 | fprintf(stderr, "kdeinit4: Aborting. $HOME directory (%s) does not exist.\n" , path.data()); |
896 | exit(255); |
897 | } |
898 | else if (readOnly.isEmpty()) |
899 | { |
900 | fprintf(stderr, "kdeinit4: Aborting. No write access to $HOME directory (%s).\n" , path.data()); |
901 | exit(255); |
902 | } |
903 | } |
904 | #if 0 // obsolete in kde4. Should we check writing to another file instead? |
905 | path = qgetenv("ICEAUTHORITY" ); |
906 | if (path.isEmpty()) |
907 | { |
908 | path = home_dir; |
909 | path += "/.ICEauthority" ; |
910 | } |
911 | if (access(path.data(), R_OK|W_OK) && (errno != ENOENT)) |
912 | { |
913 | fprintf(stderr, "kdeinit4: Aborting. No write access to '%s'.\n" , path.data()); |
914 | exit(255); |
915 | } |
916 | #endif |
917 | } |
918 | |
919 | /** Test if socket file is already present |
920 | * note that access() resolves symlinks, and so we check the actual |
921 | * socket file if it exists |
922 | */ |
923 | if (access(sock_file, W_OK) == 0) |
924 | { |
925 | int s; |
926 | struct sockaddr_un server; |
927 | |
928 | // fprintf(stderr, "kdeinit4: Warning, socket_file already exists!\n"); |
929 | /* |
930 | * create the socket stream |
931 | */ |
932 | s = socket(PF_UNIX, SOCK_STREAM, 0); |
933 | if (s < 0) |
934 | { |
935 | perror("socket() failed" ); |
936 | exit(255); |
937 | } |
938 | server.sun_family = AF_UNIX; |
939 | strcpy(server.sun_path, sock_file); |
940 | socklen = sizeof(server); |
941 | |
942 | if(connect(s, (struct sockaddr *)&server, socklen) == 0) |
943 | { |
944 | fprintf(stderr, "kdeinit4: Shutting down running client.\n" ); |
945 | klauncher_header ; |
946 | request_header.cmd = LAUNCHER_TERMINATE_KDEINIT; |
947 | request_header.arg_length = 0; |
948 | write(s, &request_header, sizeof(request_header)); |
949 | sleep(1); // Give it some time |
950 | } |
951 | close(s); |
952 | } |
953 | |
954 | /** Delete any stale socket file (and symlink) **/ |
955 | unlink(sock_file); |
956 | |
957 | /** create socket **/ |
958 | d.wrapper = socket(PF_UNIX, SOCK_STREAM, 0); |
959 | if (d.wrapper < 0) |
960 | { |
961 | perror("kdeinit4: Aborting. socket() failed" ); |
962 | exit(255); |
963 | } |
964 | |
965 | options = fcntl(d.wrapper, F_GETFL); |
966 | if (options == -1) |
967 | { |
968 | perror("kdeinit4: Aborting. Can not make socket non-blocking" ); |
969 | close(d.wrapper); |
970 | exit(255); |
971 | } |
972 | |
973 | if (fcntl(d.wrapper, F_SETFL, options | O_NONBLOCK) == -1) |
974 | { |
975 | perror("kdeinit4: Aborting. Can not make socket non-blocking" ); |
976 | close(d.wrapper); |
977 | exit(255); |
978 | } |
979 | |
980 | while (1) { |
981 | /** bind it **/ |
982 | socklen = sizeof(sa); |
983 | memset(&sa, 0, socklen); |
984 | sa.sun_family = AF_UNIX; |
985 | strcpy(sa.sun_path, sock_file); |
986 | if(bind(d.wrapper, (struct sockaddr *)&sa, socklen) != 0) |
987 | { |
988 | if (max_tries == 0) { |
989 | perror("kdeinit4: Aborting. bind() failed" ); |
990 | fprintf(stderr, "Could not bind to socket '%s'\n" , sock_file); |
991 | close(d.wrapper); |
992 | exit(255); |
993 | } |
994 | max_tries--; |
995 | } else |
996 | break; |
997 | } |
998 | |
999 | /** set permissions **/ |
1000 | if (chmod(sock_file, 0600) != 0) |
1001 | { |
1002 | perror("kdeinit4: Aborting. Can not set permissions on socket" ); |
1003 | fprintf(stderr, "Wrong permissions of socket '%s'\n" , sock_file); |
1004 | unlink(sock_file); |
1005 | close(d.wrapper); |
1006 | exit(255); |
1007 | } |
1008 | |
1009 | if(listen(d.wrapper, SOMAXCONN) < 0) |
1010 | { |
1011 | perror("kdeinit4: Aborting. listen() failed" ); |
1012 | unlink(sock_file); |
1013 | close(d.wrapper); |
1014 | exit(255); |
1015 | } |
1016 | } |
1017 | |
1018 | /* |
1019 | * Read 'len' bytes from 'sock' into buffer. |
1020 | * returns 0 on success, -1 on failure. |
1021 | */ |
1022 | static int read_socket(int sock, char *buffer, int len) |
1023 | { |
1024 | ssize_t result; |
1025 | int bytes_left = len; |
1026 | while ( bytes_left > 0) |
1027 | { |
1028 | result = read(sock, buffer, bytes_left); |
1029 | if (result > 0) |
1030 | { |
1031 | buffer += result; |
1032 | bytes_left -= result; |
1033 | } |
1034 | else if (result == 0) |
1035 | return -1; |
1036 | else if ((result == -1) && (errno != EINTR) && (errno != EAGAIN)) |
1037 | return -1; |
1038 | } |
1039 | return 0; |
1040 | } |
1041 | |
1042 | static void start_klauncher() |
1043 | { |
1044 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, d.launcher) < 0) { |
1045 | perror("kdeinit4: socketpair() failed" ); |
1046 | exit(255); |
1047 | } |
1048 | char args[32]; |
1049 | strcpy(args, "--fd=" ); |
1050 | sprintf(args + 5, "%d" , d.launcher[1]); |
1051 | d.launcher_pid = launch( 2, "klauncher" , args ); |
1052 | close(d.launcher[1]); |
1053 | #ifndef NDEBUG |
1054 | fprintf(stderr, "kdeinit4: Launched KLauncher, pid = %ld, result = %d\n" , |
1055 | (long) d.launcher_pid, d.result); |
1056 | #endif |
1057 | } |
1058 | |
1059 | static void launcher_died() |
1060 | { |
1061 | if (!d.launcher_ok) |
1062 | { |
1063 | /* This is bad. */ |
1064 | fprintf(stderr, "kdeinit4: Communication error with launcher. Exiting!\n" ); |
1065 | ::exit(255); |
1066 | return; |
1067 | } |
1068 | |
1069 | // KLauncher died... restart |
1070 | #ifndef NDEBUG |
1071 | fprintf(stderr, "kdeinit4: KLauncher died unexpectedly.\n" ); |
1072 | #endif |
1073 | // Make sure it's really dead. |
1074 | if (d.launcher_pid) |
1075 | { |
1076 | kill(d.launcher_pid, SIGKILL); |
1077 | sleep(1); // Give it some time |
1078 | } |
1079 | |
1080 | d.launcher_ok = false; |
1081 | d.launcher_pid = 0; |
1082 | close(d.launcher[0]); |
1083 | d.launcher[0] = -1; |
1084 | |
1085 | start_klauncher(); |
1086 | } |
1087 | |
1088 | static bool handle_launcher_request(int sock, const char *who) |
1089 | { |
1090 | (void)who; // for NDEBUG |
1091 | |
1092 | klauncher_header ; |
1093 | char *request_data = 0L; |
1094 | int result = read_socket(sock, (char *) &request_header, sizeof(request_header)); |
1095 | if (result != 0) |
1096 | { |
1097 | return false; |
1098 | } |
1099 | |
1100 | if ( request_header.arg_length != 0 ) |
1101 | { |
1102 | request_data = (char *) malloc(request_header.arg_length); |
1103 | |
1104 | result = read_socket(sock, request_data, request_header.arg_length); |
1105 | if (result != 0) |
1106 | { |
1107 | free(request_data); |
1108 | return false; |
1109 | } |
1110 | } |
1111 | |
1112 | //kDebug() << "Got cmd" << request_header.cmd << commandToString(request_header.cmd); |
1113 | if (request_header.cmd == LAUNCHER_OK) |
1114 | { |
1115 | d.launcher_ok = true; |
1116 | } |
1117 | else if (request_header.arg_length && |
1118 | ((request_header.cmd == LAUNCHER_EXEC) || |
1119 | (request_header.cmd == LAUNCHER_EXT_EXEC) || |
1120 | (request_header.cmd == LAUNCHER_SHELL ) || |
1121 | (request_header.cmd == LAUNCHER_KWRAPPER) || |
1122 | (request_header.cmd == LAUNCHER_EXEC_NEW))) |
1123 | { |
1124 | pid_t pid; |
1125 | klauncher_header ; |
1126 | long response_data; |
1127 | long l; |
1128 | memcpy( &l, request_data, sizeof( long )); |
1129 | int argc = l; |
1130 | const char *name = request_data + sizeof(long); |
1131 | const char *args = name + strlen(name) + 1; |
1132 | const char *cwd = 0; |
1133 | int envc = 0; |
1134 | const char *envs = 0; |
1135 | const char *tty = 0; |
1136 | int avoid_loops = 0; |
1137 | const char *startup_id_str = "0" ; // krazy:exclude=doublequote_chars |
1138 | |
1139 | #ifndef NDEBUG |
1140 | fprintf(stderr, "kdeinit4: Got %s '%s' from %s.\n" , |
1141 | commandToString(request_header.cmd), |
1142 | name, who); |
1143 | #endif |
1144 | |
1145 | const char *arg_n = args; |
1146 | for(int i = 1; i < argc; i++) |
1147 | { |
1148 | arg_n = arg_n + strlen(arg_n) + 1; |
1149 | } |
1150 | |
1151 | if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER ) |
1152 | { |
1153 | // Shell or kwrapper |
1154 | cwd = arg_n; arg_n += strlen(cwd) + 1; |
1155 | } |
1156 | if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER |
1157 | || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ) |
1158 | { |
1159 | memcpy( &l, arg_n, sizeof( long )); |
1160 | envc = l; |
1161 | arg_n += sizeof(long); |
1162 | envs = arg_n; |
1163 | for(int i = 0; i < envc; i++) |
1164 | { |
1165 | arg_n = arg_n + strlen(arg_n) + 1; |
1166 | } |
1167 | if( request_header.cmd == LAUNCHER_KWRAPPER ) |
1168 | { |
1169 | tty = arg_n; |
1170 | arg_n += strlen( tty ) + 1; |
1171 | } |
1172 | } |
1173 | |
1174 | if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER |
1175 | || request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW ) |
1176 | { |
1177 | memcpy( &l, arg_n, sizeof( long )); |
1178 | avoid_loops = l; |
1179 | arg_n += sizeof( long ); |
1180 | } |
1181 | |
1182 | if( request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER |
1183 | || request_header.cmd == LAUNCHER_EXT_EXEC ) |
1184 | { |
1185 | startup_id_str = arg_n; |
1186 | arg_n += strlen( startup_id_str ) + 1; |
1187 | } |
1188 | |
1189 | if ((request_header.arg_length > (arg_n - request_data)) && |
1190 | (request_header.cmd == LAUNCHER_EXT_EXEC || request_header.cmd == LAUNCHER_EXEC_NEW )) |
1191 | { |
1192 | // Optional cwd |
1193 | cwd = arg_n; arg_n += strlen(cwd) + 1; |
1194 | } |
1195 | |
1196 | if ((arg_n - request_data) != request_header.arg_length) |
1197 | { |
1198 | #ifndef NDEBUG |
1199 | fprintf(stderr, "kdeinit4: EXEC request has invalid format.\n" ); |
1200 | #endif |
1201 | free(request_data); |
1202 | d.debug_wait = false; |
1203 | return true; // sure? |
1204 | } |
1205 | |
1206 | // support for the old a bit broken way of setting DISPLAY for multihead |
1207 | QByteArray olddisplay = qgetenv(DISPLAY); |
1208 | QByteArray kdedisplay = qgetenv("KDE_DISPLAY" ); |
1209 | bool reset_display = (! olddisplay.isEmpty() && |
1210 | ! kdedisplay.isEmpty() && |
1211 | olddisplay != kdedisplay); |
1212 | |
1213 | if (reset_display) |
1214 | setenv(DISPLAY, kdedisplay, true); |
1215 | |
1216 | pid = launch( argc, name, args, cwd, envc, envs, |
1217 | request_header.cmd == LAUNCHER_SHELL || request_header.cmd == LAUNCHER_KWRAPPER, |
1218 | tty, avoid_loops, startup_id_str ); |
1219 | |
1220 | if (reset_display) { |
1221 | unsetenv("KDE_DISPLAY" ); |
1222 | setenv(DISPLAY, olddisplay, true); |
1223 | } |
1224 | |
1225 | if (pid && (d.result == 0)) |
1226 | { |
1227 | response_header.cmd = LAUNCHER_OK; |
1228 | response_header.arg_length = sizeof(response_data); |
1229 | response_data = pid; |
1230 | write(sock, &response_header, sizeof(response_header)); |
1231 | write(sock, &response_data, response_header.arg_length); |
1232 | |
1233 | /* add new child to list */ |
1234 | struct child *child = (struct child *) malloc(sizeof(struct child)); |
1235 | child->pid = pid; |
1236 | child->sock = dup(sock); |
1237 | child->next = children; |
1238 | children = child; |
1239 | } |
1240 | else |
1241 | { |
1242 | int l = d.errorMsg.length(); |
1243 | if (l) l++; // Include trailing null. |
1244 | response_header.cmd = LAUNCHER_ERROR; |
1245 | response_header.arg_length = l; |
1246 | write(sock, &response_header, sizeof(response_header)); |
1247 | if (l) |
1248 | write(sock, d.errorMsg.data(), l); |
1249 | } |
1250 | d.debug_wait = false; |
1251 | } |
1252 | else if (request_header.arg_length && request_header.cmd == LAUNCHER_SETENV) |
1253 | { |
1254 | const char *env_name; |
1255 | const char *env_value; |
1256 | env_name = request_data; |
1257 | env_value = env_name + strlen(env_name) + 1; |
1258 | |
1259 | #ifndef NDEBUG |
1260 | fprintf(stderr, "kdeinit4: Got SETENV '%s=%s' from %s.\n" , env_name, env_value, who); |
1261 | #endif |
1262 | |
1263 | if ( request_header.arg_length != |
1264 | (int) (strlen(env_name) + strlen(env_value) + 2)) |
1265 | { |
1266 | #ifndef NDEBUG |
1267 | fprintf(stderr, "kdeinit4: SETENV request has invalid format.\n" ); |
1268 | #endif |
1269 | free(request_data); |
1270 | return true; // sure? |
1271 | } |
1272 | setenv( env_name, env_value, 1); |
1273 | } |
1274 | else if (request_header.cmd == LAUNCHER_TERMINATE_KDE) |
1275 | { |
1276 | #ifndef NDEBUG |
1277 | fprintf(stderr,"kdeinit4: terminate KDE.\n" ); |
1278 | #endif |
1279 | #ifdef Q_WS_X11 |
1280 | kdeinit_xio_errhandler( 0L ); |
1281 | #endif |
1282 | } |
1283 | else if (request_header.cmd == LAUNCHER_TERMINATE_KDEINIT) |
1284 | { |
1285 | #ifndef NDEBUG |
1286 | fprintf(stderr,"kdeinit4: Got termination request (PID %ld).\n" , (long) getpid()); |
1287 | #endif |
1288 | if (d.launcher_pid) { |
1289 | kill(d.launcher_pid, SIGTERM); |
1290 | d.launcher_pid = 0; |
1291 | close(d.launcher[0]); |
1292 | d.launcher[0] = -1; |
1293 | } |
1294 | unlink(sock_file); |
1295 | if (children) { |
1296 | close(d.wrapper); |
1297 | d.wrapper = -1; |
1298 | #ifndef NDEBUG |
1299 | fprintf(stderr,"kdeinit4: Closed sockets, but not exiting until all children terminate.\n" ); |
1300 | #endif |
1301 | } else { |
1302 | raise(SIGTERM); |
1303 | } |
1304 | } |
1305 | else if (request_header.cmd == LAUNCHER_DEBUG_WAIT) |
1306 | { |
1307 | #ifndef NDEBUG |
1308 | fprintf(stderr,"kdeinit4: Debug wait activated.\n" ); |
1309 | #endif |
1310 | d.debug_wait = true; |
1311 | } |
1312 | if (request_data) |
1313 | free(request_data); |
1314 | return true; |
1315 | } |
1316 | |
1317 | static void handle_requests(pid_t waitForPid) |
1318 | { |
1319 | int max_sock = d.deadpipe[0]; |
1320 | if (d.wrapper > max_sock) |
1321 | max_sock = d.wrapper; |
1322 | if (d.launcher[0] > max_sock) |
1323 | max_sock = d.launcher[0]; |
1324 | #ifdef Q_WS_X11 |
1325 | if (X11fd > max_sock) |
1326 | max_sock = X11fd; |
1327 | #endif |
1328 | max_sock++; |
1329 | |
1330 | while(1) |
1331 | { |
1332 | fd_set rd_set; |
1333 | fd_set wr_set; |
1334 | fd_set e_set; |
1335 | int result; |
1336 | pid_t exit_pid; |
1337 | int exit_status; |
1338 | char c; |
1339 | |
1340 | /* Flush the pipe of death */ |
1341 | while( read(d.deadpipe[0], &c, 1) == 1) |
1342 | {} |
1343 | |
1344 | /* Handle dying children */ |
1345 | do { |
1346 | exit_pid = waitpid(-1, &exit_status, WNOHANG); |
1347 | if (exit_pid > 0) |
1348 | { |
1349 | #ifndef NDEBUG |
1350 | fprintf(stderr, "kdeinit4: PID %ld terminated.\n" , (long) exit_pid); |
1351 | #endif |
1352 | if (waitForPid && (exit_pid == waitForPid)) |
1353 | return; |
1354 | |
1355 | if( WIFEXITED( exit_status )) // fix process return value |
1356 | exit_status = WEXITSTATUS(exit_status); |
1357 | else if( WIFSIGNALED(exit_status)) |
1358 | exit_status = 128 + WTERMSIG( exit_status ); |
1359 | child_died(exit_pid, exit_status); |
1360 | |
1361 | if (d.wrapper < 0 && !children) { |
1362 | #ifndef NDEBUG |
1363 | fprintf(stderr, "kdeinit4: Last child terminated, exiting (PID %ld).\n" , |
1364 | (long) getpid()); |
1365 | #endif |
1366 | raise(SIGTERM); |
1367 | } |
1368 | } |
1369 | } |
1370 | while( exit_pid > 0); |
1371 | |
1372 | FD_ZERO(&rd_set); |
1373 | FD_ZERO(&wr_set); |
1374 | FD_ZERO(&e_set); |
1375 | |
1376 | if (d.launcher[0] >= 0) |
1377 | FD_SET(d.launcher[0], &rd_set); |
1378 | if (d.wrapper >= 0) |
1379 | FD_SET(d.wrapper, &rd_set); |
1380 | FD_SET(d.deadpipe[0], &rd_set); |
1381 | #ifdef Q_WS_X11 |
1382 | if(X11fd >= 0) FD_SET(X11fd, &rd_set); |
1383 | #endif |
1384 | |
1385 | result = select(max_sock, &rd_set, &wr_set, &e_set, 0); |
1386 | if (result < 0) { |
1387 | if (errno == EINTR || errno == EAGAIN) |
1388 | continue; |
1389 | perror("kdeinit4: Aborting. select() failed" ); |
1390 | return; |
1391 | } |
1392 | |
1393 | /* Handle wrapper request */ |
1394 | if (d.wrapper >= 0 && FD_ISSET(d.wrapper, &rd_set)) |
1395 | { |
1396 | struct sockaddr_un client; |
1397 | kde_socklen_t sClient = sizeof(client); |
1398 | int sock = accept(d.wrapper, (struct sockaddr *)&client, &sClient); |
1399 | if (sock >= 0) |
1400 | { |
1401 | d.accepted_fd = sock; |
1402 | handle_launcher_request(sock, "wrapper" ); |
1403 | close(sock); |
1404 | d.accepted_fd = -1; |
1405 | } |
1406 | } |
1407 | |
1408 | /* Handle launcher request */ |
1409 | if (d.launcher[0] >= 0 && FD_ISSET(d.launcher[0], &rd_set)) |
1410 | { |
1411 | if (!handle_launcher_request(d.launcher[0], "launcher" )) |
1412 | launcher_died(); |
1413 | if (waitForPid == d.launcher_pid) |
1414 | return; |
1415 | } |
1416 | |
1417 | #ifdef Q_WS_X11 |
1418 | /* Look for incoming X11 events */ |
1419 | if(X11fd >= 0 && FD_ISSET(X11fd,&rd_set)) { |
1420 | if (X11display != 0) { |
1421 | XEvent event_return; |
1422 | while (XPending(X11display)) |
1423 | XNextEvent(X11display, &event_return); |
1424 | } |
1425 | } |
1426 | #endif |
1427 | } |
1428 | } |
1429 | |
1430 | static void kdeinit_library_path() |
1431 | { |
1432 | const QStringList ltdl_library_path = |
1433 | QFile::decodeName(qgetenv("LTDL_LIBRARY_PATH" )).split(QLatin1Char(':'),QString::SkipEmptyParts); |
1434 | #ifdef Q_OS_DARWIN |
1435 | const QByteArray ldlibpath = qgetenv("DYLD_LIBRARY_PATH" ); |
1436 | #else |
1437 | const QByteArray ldlibpath = qgetenv("LD_LIBRARY_PATH" ); |
1438 | #endif |
1439 | const QStringList ld_library_path = |
1440 | QFile::decodeName(ldlibpath).split(QLatin1Char(':'),QString::SkipEmptyParts); |
1441 | |
1442 | QByteArray ; |
1443 | const QStringList candidates = s_instance->dirs()->resourceDirs("lib" ); |
1444 | for (QStringList::ConstIterator it = candidates.begin(); |
1445 | it != candidates.end(); |
1446 | ++it) |
1447 | { |
1448 | QString d = *it; |
1449 | if (ltdl_library_path.contains(d)) |
1450 | continue; |
1451 | if (ld_library_path.contains(d)) |
1452 | continue; |
1453 | if (d[d.length()-1] == QLatin1Char('/')) |
1454 | { |
1455 | d.truncate(d.length()-1); |
1456 | if (ltdl_library_path.contains(d)) |
1457 | continue; |
1458 | if (ld_library_path.contains(d)) |
1459 | continue; |
1460 | } |
1461 | if ((d == QLatin1String("/lib" )) || (d == QLatin1String("/usr/lib" ))) |
1462 | continue; |
1463 | |
1464 | QByteArray dir = QFile::encodeName(d); |
1465 | |
1466 | if (access(dir, R_OK)) |
1467 | continue; |
1468 | |
1469 | if ( !extra_path.isEmpty()) |
1470 | extra_path += ':'; |
1471 | extra_path += dir; |
1472 | } |
1473 | |
1474 | // if (!extra_path.isEmpty()) |
1475 | // lt_dlsetsearchpath(extra_path.data()); |
1476 | |
1477 | QByteArray display = qgetenv(DISPLAY); |
1478 | if (display.isEmpty()) |
1479 | { |
1480 | #if defined(Q_WS_X11) || defined(Q_WS_QWS) |
1481 | fprintf(stderr, "kdeinit4: Aborting. $" DISPLAY" is not set.\n" ); |
1482 | exit(255); |
1483 | #endif |
1484 | } |
1485 | int i; |
1486 | if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0) |
1487 | display.truncate(i); |
1488 | |
1489 | display.replace(':','_'); |
1490 | #ifdef __APPLE__ |
1491 | display.replace('/','_'); |
1492 | #endif |
1493 | // WARNING, if you change the socket name, adjust kwrapper too |
1494 | const QString socketFileName = QString::fromLatin1("kdeinit4_%1" ).arg(QLatin1String(display)); |
1495 | QByteArray socketName = QFile::encodeName(KStandardDirs::locateLocal("socket" , socketFileName, *s_instance)); |
1496 | if (socketName.length() >= MAX_SOCK_FILE) |
1497 | { |
1498 | fprintf(stderr, "kdeinit4: Aborting. Socket name will be too long:\n" ); |
1499 | fprintf(stderr, " '%s'\n" , socketName.data()); |
1500 | exit(255); |
1501 | } |
1502 | strcpy(sock_file, socketName.data()); |
1503 | } |
1504 | |
1505 | int kdeinit_xio_errhandler( Display *disp ) |
1506 | { |
1507 | // disp is 0L when KDE shuts down. We don't want those warnings then. |
1508 | |
1509 | if ( disp ) |
1510 | qWarning( "kdeinit4: Fatal IO error: client killed" ); |
1511 | |
1512 | if (sock_file[0]) |
1513 | { |
1514 | /** Delete any stale socket file **/ |
1515 | unlink(sock_file); |
1516 | } |
1517 | |
1518 | // Don't kill our children in suicide mode, they may still be in use |
1519 | if (d.suicide) |
1520 | { |
1521 | if (d.launcher_pid) |
1522 | kill(d.launcher_pid, SIGTERM); |
1523 | if (d.kded_pid) |
1524 | kill(d.kded_pid, SIGTERM); |
1525 | exit( 0 ); |
1526 | } |
1527 | |
1528 | if ( disp ) |
1529 | qWarning( "kdeinit4: sending SIGHUP to children." ); |
1530 | |
1531 | /* this should remove all children we started */ |
1532 | KDE_signal(SIGHUP, SIG_IGN); |
1533 | kill(0, SIGHUP); |
1534 | |
1535 | sleep(2); |
1536 | |
1537 | if ( disp ) |
1538 | qWarning( "kdeinit4: sending SIGTERM to children." ); |
1539 | |
1540 | /* and if they don't listen to us, this should work */ |
1541 | KDE_signal(SIGTERM, SIG_IGN); |
1542 | kill(0, SIGTERM); |
1543 | |
1544 | if ( disp ) |
1545 | qWarning( "kdeinit4: Exit." ); |
1546 | |
1547 | exit( 0 ); |
1548 | return 0; |
1549 | } |
1550 | |
1551 | #ifdef Q_WS_X11 |
1552 | int kdeinit_x_errhandler( Display *dpy, XErrorEvent *err ) |
1553 | { |
1554 | #ifndef NDEBUG |
1555 | char errstr[256]; |
1556 | // kdeinit almost doesn't use X, and therefore there shouldn't be any X error |
1557 | XGetErrorText( dpy, err->error_code, errstr, 256 ); |
1558 | fprintf(stderr, "kdeinit4(%d) : KDE detected X Error: %s %d\n" |
1559 | " Major opcode: %d\n" |
1560 | " Minor opcode: %d\n" |
1561 | " Resource id: 0x%lx\n" , |
1562 | getpid(), errstr, err->error_code, err->request_code, err->minor_code, err->resourceid ); |
1563 | |
1564 | //kDebug() << kBacktrace(); |
1565 | |
1566 | #else |
1567 | Q_UNUSED(dpy); |
1568 | Q_UNUSED(err); |
1569 | #endif |
1570 | return 0; |
1571 | } |
1572 | #endif |
1573 | |
1574 | #ifdef Q_WS_X11 |
1575 | // needs to be done sooner than initXconnection() because of also opening |
1576 | // another X connection for startup notification purposes |
1577 | static void setupX() |
1578 | { |
1579 | XSetIOErrorHandler(kdeinit_xio_errhandler); |
1580 | XSetErrorHandler(kdeinit_x_errhandler); |
1581 | /* |
1582 | Handle the tricky case of running via kdesu/su/sudo/etc. There the usual case |
1583 | is that kdesu (etc.) creates a file with xauth information, sets XAUTHORITY, |
1584 | runs the command and removes the xauth file after the command finishes. However, |
1585 | dbus and kdeinit daemon currently don't clean up properly and keeping running. |
1586 | Which means that running a KDE app via kdesu the second time talks to kdeinit |
1587 | with obsolete xauth information, which makes it unable to connect to X or launch |
1588 | any X11 applications. |
1589 | Even fixing the cleanup probably wouldn't be sufficient, since it'd be possible to |
1590 | launch one kdesu session, another one, exit the first one and the app from the second |
1591 | session would be using kdeinit from the first one. |
1592 | So the trick here is to duplicate the xauth file to another file in KDE's tmp |
1593 | location, make the file have a consistent name so that future sessions will use it |
1594 | as well, point XAUTHORITY there and never remove the file (except for possible |
1595 | tmp cleanup). |
1596 | */ |
1597 | if( !qgetenv( "XAUTHORITY" ).isEmpty()) { |
1598 | QByteArray display = qgetenv( DISPLAY ); |
1599 | int i; |
1600 | if((i = display.lastIndexOf('.')) > display.lastIndexOf(':') && i >= 0) |
1601 | display.truncate(i); |
1602 | display.replace(':','_'); |
1603 | #ifdef __APPLE__ |
1604 | display.replace('/','_'); |
1605 | #endif |
1606 | QString xauth = s_instance->dirs()->saveLocation( "tmp" ) + QLatin1String( "xauth-" ) |
1607 | + QString::number( getuid()) + QLatin1String( "-" ) + QString::fromLocal8Bit( display ); |
1608 | KSaveFile xauthfile( xauth ); |
1609 | QFile xauthfrom( QFile::decodeName( qgetenv( "XAUTHORITY" ))); |
1610 | if( !xauthfrom.open( QFile::ReadOnly ) || !xauthfile.open( QFile::WriteOnly ) |
1611 | || xauthfile.write( xauthfrom.readAll()) != xauthfrom.size() || !xauthfile.finalize()) { |
1612 | xauthfile.abort(); |
1613 | } else { |
1614 | setenv( "XAUTHORITY" , QFile::encodeName( xauth ), true ); |
1615 | } |
1616 | } |
1617 | } |
1618 | |
1619 | // Borrowed from kdebase/kaudio/kaudioserver.cpp |
1620 | static int initXconnection() |
1621 | { |
1622 | X11display = XOpenDisplay(NULL); |
1623 | if ( X11display != 0 ) { |
1624 | XCreateSimpleWindow(X11display, DefaultRootWindow(X11display), 0,0,1,1, \ |
1625 | 0, |
1626 | BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)), |
1627 | BlackPixelOfScreen(DefaultScreenOfDisplay(X11display)) ); |
1628 | #ifndef NDEBUG |
1629 | fprintf(stderr, "kdeinit4: opened connection to %s\n" , DisplayString(X11display)); |
1630 | #endif |
1631 | int fd = XConnectionNumber( X11display ); |
1632 | int on = 1; |
1633 | (void) setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, (int) sizeof(on)); |
1634 | return fd; |
1635 | } else |
1636 | fprintf(stderr, "kdeinit4: Can not connect to the X Server.\n" \ |
1637 | "kdeinit4: Might not terminate at end of session.\n" ); |
1638 | |
1639 | return -1; |
1640 | } |
1641 | #endif |
1642 | |
1643 | extern "C" { |
1644 | |
1645 | static void secondary_child_handler(int) |
1646 | { |
1647 | waitpid(-1, 0, WNOHANG); |
1648 | } |
1649 | |
1650 | } |
1651 | |
1652 | int main(int argc, char **argv, char **envp) |
1653 | { |
1654 | #ifndef _WIN32_WCE |
1655 | setlocale (LC_ALL, "" ); |
1656 | setlocale (LC_NUMERIC, "C" ); |
1657 | #endif |
1658 | |
1659 | pid_t pid; |
1660 | bool do_fork = true; |
1661 | int launch_klauncher = 1; |
1662 | int launch_kded = 1; |
1663 | int keep_running = 1; |
1664 | d.suicide = false; |
1665 | |
1666 | /** Save arguments first... **/ |
1667 | char **safe_argv = (char **) malloc( sizeof(char *) * argc); |
1668 | for(int i = 0; i < argc; i++) |
1669 | { |
1670 | safe_argv[i] = strcpy((char*)malloc(strlen(argv[i])+1), argv[i]); |
1671 | if (strcmp(safe_argv[i], "--no-klauncher" ) == 0) |
1672 | launch_klauncher = 0; |
1673 | if (strcmp(safe_argv[i], "--no-kded" ) == 0) |
1674 | launch_kded = 0; |
1675 | #ifdef Q_WS_MACX |
1676 | // make it nofork to match KUniqueApplication, technically command-line incompatible |
1677 | if (strcmp(safe_argv[i], "--nofork" ) == 0) |
1678 | #else |
1679 | if (strcmp(safe_argv[i], "--no-fork" ) == 0) |
1680 | #endif |
1681 | do_fork = false; |
1682 | if (strcmp(safe_argv[i], "--suicide" ) == 0) |
1683 | d.suicide = true; |
1684 | if (strcmp(safe_argv[i], "--exit" ) == 0) |
1685 | keep_running = 0; |
1686 | if (strcmp(safe_argv[i], "--version" ) == 0) |
1687 | { |
1688 | printf("Qt: %s\n" , qVersion()); |
1689 | printf("KDE: %s\n" , KDE_VERSION_STRING); |
1690 | exit(0); |
1691 | } |
1692 | #ifdef KDEINIT_OOM_PROTECT |
1693 | if (strcmp(safe_argv[i], "--oom-pipe" ) == 0 && i+1<argc) |
1694 | oom_pipe = atol(argv[i+1]); |
1695 | #endif |
1696 | if (strcmp(safe_argv[i], "--help" ) == 0) |
1697 | { |
1698 | printf("Usage: kdeinit4 [options]\n" ); |
1699 | // printf(" --no-dcop Do not start dcopserver\n"); |
1700 | #ifdef Q_WS_MACX |
1701 | printf(" --nofork Do not fork\n" ); |
1702 | #else |
1703 | printf(" --no-fork Do not fork\n" ); |
1704 | #endif |
1705 | // printf(" --no-klauncher Do not start klauncher\n"); |
1706 | printf(" --no-kded Do not start kded\n" ); |
1707 | printf(" --suicide Terminate when no KDE applications are left running\n" ); |
1708 | printf(" --version Show version information\n" ); |
1709 | // printf(" --exit Terminate when kded has run\n"); |
1710 | exit(0); |
1711 | } |
1712 | } |
1713 | |
1714 | cleanup_fds(); |
1715 | |
1716 | // Redirect stdout to stderr. We have no reason to use stdout anyway. |
1717 | // This minimizes our impact on commands used in pipes. |
1718 | (void)dup2(2, 1); |
1719 | |
1720 | if (do_fork) { |
1721 | #ifdef Q_WS_MACX |
1722 | mac_fork_and_reexec_self(); |
1723 | #else |
1724 | if (pipe(d.initpipe) != 0) { |
1725 | perror("kdeinit4: pipe failed" ); |
1726 | return 1; |
1727 | } |
1728 | |
1729 | // Fork here and let parent process exit. |
1730 | // Parent process may only exit after all required services have been |
1731 | // launched. (dcopserver/klauncher and services which start with '+') |
1732 | KDE_signal( SIGCHLD, secondary_child_handler); |
1733 | if (fork() > 0) // Go into background |
1734 | { |
1735 | close(d.initpipe[1]); |
1736 | d.initpipe[1] = -1; |
1737 | // wait till init is complete |
1738 | char c; |
1739 | while( read(d.initpipe[0], &c, 1) < 0) |
1740 | ; |
1741 | // then exit; |
1742 | close(d.initpipe[0]); |
1743 | d.initpipe[0] = -1; |
1744 | return 0; |
1745 | } |
1746 | close(d.initpipe[0]); |
1747 | d.initpipe[0] = -1; |
1748 | #endif |
1749 | } |
1750 | |
1751 | /** Make process group leader (for shutting down children later) **/ |
1752 | if(keep_running) |
1753 | setsid(); |
1754 | |
1755 | /** Create our instance **/ |
1756 | s_instance = new KComponentData("kdeinit4" , QByteArray(), KComponentData::SkipMainComponentRegistration); |
1757 | |
1758 | /** Prepare to change process name **/ |
1759 | #ifndef SKIP_PROCTITLE |
1760 | proctitle_init(argc, argv, envp); |
1761 | #endif |
1762 | |
1763 | kdeinit_library_path(); |
1764 | // Don't make our instance the global instance |
1765 | // (do it only after kdeinit_library_path, that one indirectly uses KConfig, |
1766 | // which seems to be buggy and always use KGlobal instead of the matching KComponentData) |
1767 | Q_ASSERT(!KGlobal::hasMainComponent()); |
1768 | // don't change envvars before proctitle_init() |
1769 | unsetenv("LD_BIND_NOW" ); |
1770 | unsetenv("DYLD_BIND_AT_LAUNCH" ); |
1771 | KApplication::loadedByKdeinit = true; |
1772 | |
1773 | d.maxname = strlen(argv[0]); |
1774 | d.launcher_pid = 0; |
1775 | d.kded_pid = 0; |
1776 | d.wrapper = -1; |
1777 | d.accepted_fd = -1; |
1778 | d.debug_wait = false; |
1779 | d.launcher_ok = false; |
1780 | children = NULL; |
1781 | init_signals(); |
1782 | #ifdef Q_WS_X11 |
1783 | setupX(); |
1784 | #endif |
1785 | |
1786 | if (keep_running) |
1787 | { |
1788 | /* |
1789 | * Create ~/.kde/tmp-<hostname>/kdeinit4-<display> socket for incoming wrapper |
1790 | * requests. |
1791 | */ |
1792 | init_kdeinit_socket(); |
1793 | } |
1794 | #ifdef Q_WS_X11 |
1795 | if (!d.suicide && qgetenv("KDE_IS_PRELINKED" ).isEmpty()) { |
1796 | const int = sizeof(extra_libs)/sizeof(extra_libs[0]); |
1797 | for (int i=0; i<extrasCount; i++) { |
1798 | QString = KStandardDirs::locate("lib" , QLatin1String(extra_libs[i]), *s_instance); |
1799 | |
1800 | // can't use KLibLoader here as it would unload the library |
1801 | // again |
1802 | if (!extra.isEmpty()) { |
1803 | QLibrary l(extra); |
1804 | l.setLoadHints(QLibrary::ExportExternalSymbolsHint); |
1805 | (void)l.load(); |
1806 | } |
1807 | #ifndef NDEBUG |
1808 | else { |
1809 | fprintf( stderr, "%s was not found.\n" , extra_libs[i] ); |
1810 | } |
1811 | #endif |
1812 | |
1813 | } |
1814 | } |
1815 | #endif |
1816 | if (launch_klauncher) |
1817 | { |
1818 | start_klauncher(); |
1819 | handle_requests(d.launcher_pid); // Wait for klauncher to be ready |
1820 | } |
1821 | |
1822 | #ifdef Q_WS_X11 |
1823 | X11fd = initXconnection(); |
1824 | #endif |
1825 | |
1826 | { |
1827 | QFont::initialize(); |
1828 | #ifdef Q_WS_X11 |
1829 | if (XSupportsLocale ()) |
1830 | { |
1831 | // Similar to QApplication::create_xim() |
1832 | // but we need to use our own display |
1833 | XOpenIM (X11display, 0, 0, 0); |
1834 | } |
1835 | #endif |
1836 | } |
1837 | |
1838 | if (launch_kded) |
1839 | { |
1840 | setenv("KDED_STARTED_BY_KDEINIT" , "1" , true); |
1841 | pid = launch( 1, KDED_EXENAME, 0 ); |
1842 | unsetenv("KDED_STARTED_BY_KDEINIT" ); |
1843 | #ifndef NDEBUG |
1844 | fprintf(stderr, "kdeinit4: Launched KDED, pid = %ld result = %d\n" , (long) pid, d.result); |
1845 | #endif |
1846 | d.kded_pid = pid; |
1847 | handle_requests(pid); |
1848 | } |
1849 | |
1850 | for(int i = 1; i < argc; i++) |
1851 | { |
1852 | if (safe_argv[i][0] == '+') |
1853 | { |
1854 | pid = launch( 1, safe_argv[i]+1, 0); |
1855 | #ifndef NDEBUG |
1856 | fprintf(stderr, "kdeinit4: Launched '%s', pid = %ld result = %d\n" , safe_argv[i]+1, (long) pid, d.result); |
1857 | #endif |
1858 | handle_requests(pid); |
1859 | } |
1860 | else if (safe_argv[i][0] == '-' |
1861 | #ifdef KDEINIT_OOM_PROTECT |
1862 | || isdigit(safe_argv[i][0]) |
1863 | #endif |
1864 | ) |
1865 | { |
1866 | // Ignore |
1867 | } |
1868 | else |
1869 | { |
1870 | pid = launch( 1, safe_argv[i], 0 ); |
1871 | #ifndef NDEBUG |
1872 | fprintf(stderr, "kdeinit4: Launched '%s', pid = %ld result = %d\n" , safe_argv[i], (long) pid, d.result); |
1873 | #endif |
1874 | } |
1875 | } |
1876 | |
1877 | /** Free arguments **/ |
1878 | for(int i = 0; i < argc; i++) |
1879 | { |
1880 | free(safe_argv[i]); |
1881 | } |
1882 | free (safe_argv); |
1883 | |
1884 | #ifndef SKIP_PROCTITLE |
1885 | proctitle_set("kdeinit4 Running..." ); |
1886 | #endif |
1887 | |
1888 | if (!keep_running) |
1889 | return 0; |
1890 | |
1891 | if (d.initpipe[1] != -1) |
1892 | { |
1893 | char c = 0; |
1894 | write(d.initpipe[1], &c, 1); // Kdeinit is started. |
1895 | close(d.initpipe[1]); |
1896 | d.initpipe[1] = -1; |
1897 | } |
1898 | |
1899 | handle_requests(0); |
1900 | |
1901 | return 0; |
1902 | } |
1903 | |
1904 | |
1905 | |