1 | /* |
2 | This file is part of the KDE libraries |
3 | Copyright (c) 1999 Waldo Bastian <bastian@kde.org> |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Library General Public |
7 | License version 2 as published by the Free Software Foundation. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Library General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to |
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | #define QT_NO_CAST_FROM_ASCII |
21 | |
22 | #include "klauncher.h" |
23 | #include "klauncher_cmds.h" |
24 | #include "klauncher_adaptor.h" |
25 | |
26 | #include <config.h> |
27 | |
28 | #include <stdio.h> |
29 | #include <unistd.h> |
30 | #include <stdlib.h> |
31 | #include <errno.h> |
32 | #include <signal.h> |
33 | #include <sys/time.h> |
34 | |
35 | #ifdef Q_WS_X11 |
36 | #include <kstartupinfo.h> |
37 | #include <X11/Xlib.h> |
38 | #endif |
39 | |
40 | #include <QtCore/QFile> |
41 | #include <qplatformdefs.h> |
42 | |
43 | #include <kconfig.h> |
44 | #include <kdebug.h> |
45 | #include <kde_file.h> |
46 | #include <klibrary.h> |
47 | #include <klocale.h> |
48 | #include <kprotocolmanager.h> |
49 | #include <kprotocolinfo.h> |
50 | #include <krun.h> |
51 | #include <kstandarddirs.h> |
52 | #include <ktemporaryfile.h> |
53 | #include <kdesktopfile.h> |
54 | #include <kurl.h> |
55 | |
56 | #include <kio/global.h> |
57 | #include <kio/connection.h> |
58 | #include <kio/slaveinterface.h> |
59 | |
60 | // Dispose slaves after being idle for SLAVE_MAX_IDLE seconds |
61 | #define SLAVE_MAX_IDLE 30 |
62 | |
63 | // #define KLAUNCHER_VERBOSE_OUTPUT |
64 | |
65 | static const char* const s_DBusStartupTypeToString[] = |
66 | { "DBusNone" , "DBusUnique" , "DBusMulti" , "DBusWait" , "ERROR" }; |
67 | |
68 | using namespace KIO; |
69 | |
70 | IdleSlave::IdleSlave(QObject *parent) |
71 | : QObject(parent) |
72 | { |
73 | QObject::connect(&mConn, SIGNAL(readyRead()), this, SLOT(gotInput())); |
74 | // Send it a SLAVE_STATUS command. |
75 | mConn.send( CMD_SLAVE_STATUS ); |
76 | mPid = 0; |
77 | mBirthDate = time(0); |
78 | mOnHold = false; |
79 | } |
80 | |
81 | template<int T> struct PIDType { typedef pid_t PID_t; } ; |
82 | template<> struct PIDType<2> { typedef qint16 PID_t; } ; |
83 | template<> struct PIDType<4> { typedef qint32 PID_t; } ; |
84 | |
85 | void |
86 | IdleSlave::gotInput() |
87 | { |
88 | int cmd; |
89 | QByteArray data; |
90 | if (mConn.read( &cmd, data) == -1) |
91 | { |
92 | // Communication problem with slave. |
93 | //kError(7016) << "SlavePool: No communication with slave." << endl; |
94 | deleteLater(); |
95 | } |
96 | else if (cmd == MSG_SLAVE_ACK) |
97 | { |
98 | deleteLater(); |
99 | } |
100 | else if (cmd != MSG_SLAVE_STATUS) |
101 | { |
102 | kError(7016) << "SlavePool: Unexpected data from slave." << endl; |
103 | deleteLater(); |
104 | } |
105 | else |
106 | { |
107 | QDataStream stream( data ); |
108 | PIDType<sizeof(pid_t)>::PID_t stream_pid; |
109 | pid_t pid; |
110 | QByteArray protocol; |
111 | QString host; |
112 | qint8 b; |
113 | stream >> stream_pid >> protocol >> host >> b; |
114 | pid = stream_pid; |
115 | // Overload with (bool) onHold, (KUrl) url. |
116 | if (!stream.atEnd()) |
117 | { |
118 | KUrl url; |
119 | stream >> url; |
120 | mOnHold = true; |
121 | mUrl = url; |
122 | } |
123 | |
124 | mPid = pid; |
125 | mConnected = (b != 0); |
126 | mProtocol = QString::fromLatin1(protocol); |
127 | mHost = host; |
128 | emit statusUpdate(this); |
129 | } |
130 | } |
131 | |
132 | void |
133 | IdleSlave::connect(const QString &app_socket) |
134 | { |
135 | QByteArray data; |
136 | QDataStream stream( &data, QIODevice::WriteOnly); |
137 | stream << app_socket; |
138 | mConn.send( CMD_SLAVE_CONNECT, data ); |
139 | // Timeout! |
140 | } |
141 | |
142 | void |
143 | IdleSlave::reparseConfiguration() |
144 | { |
145 | mConn.send( CMD_REPARSECONFIGURATION ); |
146 | } |
147 | |
148 | bool |
149 | IdleSlave::match(const QString &protocol, const QString &host, bool needConnected) const |
150 | { |
151 | if (mOnHold || protocol != mProtocol) { |
152 | return false; |
153 | } |
154 | if (host.isEmpty()) { |
155 | return true; |
156 | } |
157 | return (host == mHost) && (!needConnected || mConnected); |
158 | } |
159 | |
160 | bool |
161 | IdleSlave::onHold(const KUrl &url) const |
162 | { |
163 | if (!mOnHold) return false; |
164 | return (url == mUrl); |
165 | } |
166 | |
167 | int |
168 | IdleSlave::age(time_t now) const |
169 | { |
170 | return (int) difftime(now, mBirthDate); |
171 | } |
172 | |
173 | static KLauncher* g_klauncher_self; |
174 | |
175 | |
176 | // From qcore_unix_p.h. We could also port to QLocalSocket :) |
177 | #define K_EINTR_LOOP(var, cmd) \ |
178 | do { \ |
179 | var = cmd; \ |
180 | } while (var == -1 && errno == EINTR) |
181 | |
182 | ssize_t kde_safe_write(int fd, const void *buf, size_t count) |
183 | { |
184 | ssize_t ret = 0; |
185 | K_EINTR_LOOP(ret, QT_WRITE(fd, buf, count)); |
186 | if (ret < 0) |
187 | qWarning() << "write failed:" << strerror(errno); |
188 | return ret; |
189 | } |
190 | |
191 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
192 | KLauncher::KLauncher(int _kdeinitSocket) |
193 | : QObject(0), |
194 | kdeinitSocket(_kdeinitSocket) |
195 | #else |
196 | KLauncher::KLauncher() |
197 | : QObject(0) |
198 | #endif |
199 | { |
200 | #ifdef Q_WS_X11 |
201 | mCached_dpy = NULL; |
202 | #endif |
203 | Q_ASSERT( g_klauncher_self == NULL ); |
204 | g_klauncher_self = this; |
205 | |
206 | mAutoTimer.setSingleShot(true); |
207 | new KLauncherAdaptor(this); |
208 | QDBusConnection::sessionBus().registerObject(QLatin1String("/KLauncher" ), this); // same as ktoolinvocation.cpp |
209 | |
210 | connect(&mAutoTimer, SIGNAL(timeout()), this, SLOT(slotAutoStart())); |
211 | connect(QDBusConnection::sessionBus().interface(), |
212 | SIGNAL(serviceOwnerChanged(QString,QString,QString)), |
213 | SLOT(slotNameOwnerChanged(QString,QString,QString))); |
214 | |
215 | mConnectionServer.listenForRemote(); |
216 | connect(&mConnectionServer, SIGNAL(newConnection()), SLOT(acceptSlave())); |
217 | if (!mConnectionServer.isListening()) |
218 | { |
219 | // Severe error! |
220 | qDebug("KLauncher: Fatal error, can't create tempfile!" ); |
221 | ::_exit(1); |
222 | } |
223 | |
224 | connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout())); |
225 | |
226 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
227 | kdeinitNotifier = new QSocketNotifier(kdeinitSocket, QSocketNotifier::Read); |
228 | connect(kdeinitNotifier, SIGNAL(activated(int)), |
229 | this, SLOT(slotKDEInitData(int))); |
230 | kdeinitNotifier->setEnabled( true ); |
231 | #endif |
232 | lastRequest = 0; |
233 | bProcessingQueue = false; |
234 | |
235 | mSlaveDebug = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_DEBUG_WAIT" )); |
236 | if (!mSlaveDebug.isEmpty()) |
237 | { |
238 | qWarning("Klauncher running in slave-debug mode for slaves of protocol '%s'" , qPrintable(mSlaveDebug)); |
239 | } |
240 | mSlaveValgrind = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND" )); |
241 | if (!mSlaveValgrind.isEmpty()) |
242 | { |
243 | mSlaveValgrindSkin = QString::fromLocal8Bit(qgetenv("KDE_SLAVE_VALGRIND_SKIN" )); |
244 | qWarning("Klauncher running slaves through valgrind for slaves of protocol '%s'" , qPrintable(mSlaveValgrind)); |
245 | } |
246 | #ifdef USE_KPROCESS_FOR_KIOSLAVES |
247 | kDebug(7016) << "LAUNCHER_OK" ; |
248 | #else |
249 | klauncher_header ; |
250 | request_header.cmd = LAUNCHER_OK; |
251 | request_header.arg_length = 0; |
252 | kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); |
253 | #endif |
254 | } |
255 | |
256 | KLauncher::~KLauncher() |
257 | { |
258 | close(); |
259 | g_klauncher_self = NULL; |
260 | } |
261 | |
262 | void KLauncher::close() |
263 | { |
264 | #ifdef Q_WS_X11 |
265 | if( mCached_dpy != NULL ) |
266 | { |
267 | XCloseDisplay( mCached_dpy ); |
268 | mCached_dpy = NULL; |
269 | } |
270 | #endif |
271 | } |
272 | |
273 | void |
274 | KLauncher::destruct() |
275 | { |
276 | if (g_klauncher_self) |
277 | g_klauncher_self->close(); |
278 | // We don't delete the app here, that's intentional. |
279 | ::_exit(255); |
280 | } |
281 | |
282 | void KLauncher::setLaunchEnv(const QString &name, const QString &value) |
283 | { |
284 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
285 | klauncher_header ; |
286 | QByteArray requestData; |
287 | requestData.append(name.toLocal8Bit()).append('\0').append(value.toLocal8Bit()).append('\0'); |
288 | request_header.cmd = LAUNCHER_SETENV; |
289 | request_header.arg_length = requestData.size(); |
290 | kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); |
291 | kde_safe_write(kdeinitSocket, requestData.data(), request_header.arg_length); |
292 | #else |
293 | Q_UNUSED(name); |
294 | Q_UNUSED(value); |
295 | #endif |
296 | } |
297 | |
298 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
299 | /* |
300 | * Read 'len' bytes from 'sock' into buffer. |
301 | * returns -1 on failure, 0 on no data. |
302 | */ |
303 | static int |
304 | read_socket(int sock, char *buffer, int len) |
305 | { |
306 | ssize_t result; |
307 | int bytes_left = len; |
308 | while (bytes_left > 0) { |
309 | // in case we get a request to start an application and data arrive |
310 | // to kdeinitSocket at the same time, requestStart() will already |
311 | // call slotKDEInitData(), so we must check there's still something |
312 | // to read, otherwise this would block |
313 | |
314 | // Same thing if kdeinit dies without warning. |
315 | |
316 | fd_set in; |
317 | timeval tm = { 30, 0 }; // 30 seconds timeout, so we're not stuck in case kdeinit dies on us |
318 | FD_ZERO ( &in ); |
319 | FD_SET( sock, &in ); |
320 | select( sock + 1, &in, 0, 0, &tm ); |
321 | if( !FD_ISSET( sock, &in )) { |
322 | kDebug(7016) << "read_socket" << sock << "nothing to read, kdeinit4 must be dead" ; |
323 | return -1; |
324 | } |
325 | |
326 | result = read(sock, buffer, bytes_left); |
327 | if (result > 0) |
328 | { |
329 | buffer += result; |
330 | bytes_left -= result; |
331 | } |
332 | else if (result == 0) |
333 | return -1; |
334 | else if ((result == -1) && (errno != EINTR)) |
335 | return -1; |
336 | } |
337 | return 0; |
338 | } |
339 | #endif |
340 | |
341 | void |
342 | KLauncher::slotKDEInitData(int) |
343 | { |
344 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
345 | klauncher_header ; |
346 | QByteArray requestData; |
347 | |
348 | int result = read_socket(kdeinitSocket, (char *) &request_header, |
349 | sizeof( request_header)); |
350 | if (result == -1) |
351 | { |
352 | kDebug(7016) << "Exiting on read_socket errno:" << errno; |
353 | KDE_signal( SIGHUP, SIG_IGN); |
354 | KDE_signal( SIGTERM, SIG_IGN); |
355 | destruct(); // Exit! |
356 | } |
357 | requestData.resize(request_header.arg_length); |
358 | result = read_socket(kdeinitSocket, (char *) requestData.data(), |
359 | request_header.arg_length); |
360 | |
361 | processRequestReturn(request_header.cmd,requestData); |
362 | #endif |
363 | } |
364 | |
365 | void KLauncher::processRequestReturn(int status, const QByteArray &requestData) |
366 | { |
367 | if (status == LAUNCHER_CHILD_DIED) |
368 | { |
369 | long *request_data; |
370 | request_data = (long *) requestData.data(); |
371 | processDied(request_data[0], request_data[1]); |
372 | return; |
373 | } |
374 | if (lastRequest && (status == LAUNCHER_OK)) |
375 | { |
376 | long *request_data; |
377 | request_data = (long *) requestData.data(); |
378 | lastRequest->pid = (pid_t) (*request_data); |
379 | kDebug(7016).nospace() << lastRequest->name << " (pid " << lastRequest->pid << |
380 | ") up and running." ; |
381 | switch(lastRequest->dbus_startup_type) |
382 | { |
383 | case KService::DBusNone: |
384 | lastRequest->status = KLaunchRequest::Running; |
385 | break; |
386 | case KService::DBusUnique: |
387 | case KService::DBusWait: |
388 | case KService::DBusMulti: |
389 | lastRequest->status = KLaunchRequest::Launching; |
390 | break; |
391 | } |
392 | lastRequest = 0; |
393 | return; |
394 | } |
395 | if (lastRequest && (status == LAUNCHER_ERROR)) |
396 | { |
397 | lastRequest->status = KLaunchRequest::Error; |
398 | kDebug(7016) << lastRequest->name << " failed." << endl; |
399 | if (!requestData.isEmpty()) |
400 | lastRequest->errorMsg = QString::fromUtf8((char *) requestData.data()); |
401 | lastRequest = 0; |
402 | return; |
403 | } |
404 | |
405 | kWarning(7016)<< "Unexpected request return" << (unsigned int) status; |
406 | } |
407 | |
408 | void |
409 | KLauncher::processDied(pid_t pid, long exitStatus) |
410 | { |
411 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
412 | kDebug(7016) << pid << "exitStatus=" << exitStatus; |
413 | #else |
414 | Q_UNUSED(exitStatus); |
415 | // We should probably check the exitStatus for the uniqueapp case? |
416 | #endif |
417 | foreach (KLaunchRequest *request, requestList) |
418 | { |
419 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
420 | kDebug(7016) << " had pending request" << request->pid; |
421 | #endif |
422 | if (request->pid == pid) |
423 | { |
424 | if (request->dbus_startup_type == KService::DBusWait) |
425 | request->status = KLaunchRequest::Done; |
426 | else if ((request->dbus_startup_type == KService::DBusUnique) |
427 | && QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { |
428 | request->status = KLaunchRequest::Running; |
429 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
430 | kDebug(7016) << pid << "running as a unique app" ; |
431 | #endif |
432 | } else { |
433 | request->status = KLaunchRequest::Error; |
434 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
435 | kDebug(7016) << pid << "died, requestDone. status=" << request->status; |
436 | #endif |
437 | } |
438 | requestDone(request); |
439 | return; |
440 | } |
441 | } |
442 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
443 | kDebug(7016) << "found no pending requests for PID" << pid; |
444 | #endif |
445 | } |
446 | |
447 | static bool matchesPendingRequest(const QString& appId, const QString& pendingAppId) |
448 | { |
449 | // appId just registered, e.g. org.koffice.kword-12345 |
450 | // Let's see if this is what pendingAppId (e.g. org.koffice.kword or *.kword) was waiting for. |
451 | |
452 | const QString newAppId = appId.left(appId.lastIndexOf(QLatin1Char('-'))); // strip out the -12345 if present. |
453 | |
454 | //kDebug() << "appId=" << appId << "newAppId=" << newAppId << "pendingAppId=" << pendingAppId; |
455 | |
456 | if (pendingAppId.startsWith(QLatin1String("*." ))) { |
457 | const QString pendingName = pendingAppId.mid(2); |
458 | const QString appName = newAppId.mid(newAppId.lastIndexOf(QLatin1Char('.'))+1); |
459 | //kDebug() << "appName=" << appName; |
460 | return appName == pendingName; |
461 | } |
462 | |
463 | return newAppId == pendingAppId; |
464 | } |
465 | |
466 | void |
467 | KLauncher::slotNameOwnerChanged(const QString &appId, const QString &oldOwner, |
468 | const QString &newOwner) |
469 | { |
470 | Q_UNUSED(oldOwner); |
471 | if (appId.isEmpty() || newOwner.isEmpty()) |
472 | return; |
473 | |
474 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
475 | kDebug(7016) << "new app" << appId; |
476 | #endif |
477 | foreach (KLaunchRequest *request, requestList) |
478 | { |
479 | if (request->status != KLaunchRequest::Launching) |
480 | continue; |
481 | |
482 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
483 | kDebug(7016) << "had pending request" << request->name << s_DBusStartupTypeToString[request->dbus_startup_type] << "dbus_name" << request->dbus_name << request->tolerant_dbus_name; |
484 | #endif |
485 | // For unique services check the requested service name first |
486 | if (request->dbus_startup_type == KService::DBusUnique) { |
487 | if ((appId == request->dbus_name) || // just started |
488 | QDBusConnection::sessionBus().interface()->isServiceRegistered(request->dbus_name)) { // was already running |
489 | request->status = KLaunchRequest::Running; |
490 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
491 | kDebug(7016) << "OK, unique app" << request->dbus_name << "is running" ; |
492 | #endif |
493 | requestDone(request); |
494 | continue; |
495 | } else { |
496 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
497 | kDebug(7016) << "unique app" << request->dbus_name << "not running yet" ; |
498 | #endif |
499 | } |
500 | } |
501 | |
502 | const QString rAppId = !request->tolerant_dbus_name.isEmpty() ? request->tolerant_dbus_name : request->dbus_name; |
503 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
504 | //kDebug(7016) << "using" << rAppId << "for matching"; |
505 | #endif |
506 | if (rAppId.isEmpty()) |
507 | continue; |
508 | |
509 | if (matchesPendingRequest(appId, rAppId)) { |
510 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
511 | kDebug(7016) << "ok, request done" ; |
512 | #endif |
513 | request->dbus_name = appId; |
514 | request->status = KLaunchRequest::Running; |
515 | requestDone(request); |
516 | continue; |
517 | } |
518 | } |
519 | } |
520 | |
521 | void |
522 | KLauncher::autoStart(int phase) |
523 | { |
524 | if( mAutoStart.phase() >= phase ) |
525 | return; |
526 | mAutoStart.setPhase(phase); |
527 | if (phase == 0) |
528 | mAutoStart.loadAutoStartList(); |
529 | mAutoTimer.start(0); |
530 | } |
531 | |
532 | void |
533 | KLauncher::slotAutoStart() |
534 | { |
535 | KService::Ptr s; |
536 | do |
537 | { |
538 | QString service = mAutoStart.startService(); |
539 | if (service.isEmpty()) |
540 | { |
541 | // Done |
542 | if( !mAutoStart.phaseDone()) |
543 | { |
544 | mAutoStart.setPhaseDone(); |
545 | switch( mAutoStart.phase()) |
546 | { |
547 | case 0: |
548 | emit autoStart0Done(); |
549 | break; |
550 | case 1: |
551 | emit autoStart1Done(); |
552 | break; |
553 | case 2: |
554 | emit autoStart2Done(); |
555 | break; |
556 | } |
557 | } |
558 | return; |
559 | } |
560 | s = new KService(service); |
561 | } |
562 | while (!start_service(s, QStringList(), QStringList(), "0" , false, true, QDBusMessage())); |
563 | // Loop till we find a service that we can start. |
564 | } |
565 | |
566 | void |
567 | KLauncher::requestDone(KLaunchRequest *request) |
568 | { |
569 | if ((request->status == KLaunchRequest::Running) || |
570 | (request->status == KLaunchRequest::Done)) |
571 | { |
572 | requestResult.result = 0; |
573 | requestResult.dbusName = request->dbus_name; |
574 | requestResult.error = QString::fromLatin1("" ); // not null, cf assert further down |
575 | requestResult.pid = request->pid; |
576 | } |
577 | else |
578 | { |
579 | requestResult.result = 1; |
580 | requestResult.dbusName.clear(); |
581 | requestResult.error = i18n("KDEInit could not launch '%1'" , request->name); |
582 | if (!request->errorMsg.isEmpty()) |
583 | requestResult.error += QString::fromLatin1(":\n" ) + request->errorMsg; |
584 | requestResult.pid = 0; |
585 | |
586 | #ifdef Q_WS_X11 |
587 | if (!request->startup_dpy.isEmpty()) |
588 | { |
589 | Display* dpy = NULL; |
590 | if( (mCached_dpy != NULL) && |
591 | (request->startup_dpy == XDisplayString( mCached_dpy ))) |
592 | dpy = mCached_dpy; |
593 | if( dpy == NULL ) |
594 | dpy = XOpenDisplay(request->startup_dpy); |
595 | if( dpy ) |
596 | { |
597 | KStartupInfoId id; |
598 | id.initId(request->startup_id); |
599 | KStartupInfo::sendFinishX( dpy, id ); |
600 | if( mCached_dpy != dpy && mCached_dpy != NULL ) |
601 | XCloseDisplay( mCached_dpy ); |
602 | mCached_dpy = dpy; |
603 | } |
604 | } |
605 | #endif |
606 | } |
607 | |
608 | if (request->autoStart) |
609 | { |
610 | mAutoTimer.start(0); |
611 | } |
612 | |
613 | if (request->transaction.type() != QDBusMessage::InvalidMessage) |
614 | { |
615 | if ( requestResult.dbusName.isNull() ) // null strings can't be sent |
616 | requestResult.dbusName.clear(); |
617 | Q_ASSERT( !requestResult.error.isNull() ); |
618 | PIDType<sizeof(pid_t)>::PID_t stream_pid = requestResult.pid; |
619 | QDBusConnection::sessionBus().send(request->transaction.createReply(QVariantList() << requestResult.result |
620 | << requestResult.dbusName |
621 | << requestResult.error |
622 | << stream_pid)); |
623 | } |
624 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
625 | kDebug(7016) << "removing done request" << request->name << "PID" << request->pid; |
626 | #endif |
627 | |
628 | requestList.removeAll( request ); |
629 | delete request; |
630 | } |
631 | |
632 | static void appendLong(QByteArray &ba, long l) |
633 | { |
634 | const int sz = ba.size(); |
635 | ba.resize(sz + sizeof(long)); |
636 | memcpy(ba.data() + sz, &l, sizeof(long)); |
637 | } |
638 | |
639 | void |
640 | KLauncher::requestStart(KLaunchRequest *request) |
641 | { |
642 | #ifdef USE_KPROCESS_FOR_KIOSLAVES |
643 | requestList.append( request ); |
644 | lastRequest = request; |
645 | |
646 | KProcess *process = new KProcess; |
647 | process->setOutputChannelMode(KProcess::MergedChannels); |
648 | connect(process ,SIGNAL(readyReadStandardOutput()),this, SLOT(slotGotOutput()) ); |
649 | connect(process ,SIGNAL(finished(int,QProcess::ExitStatus)),this, SLOT(slotFinished(int,QProcess::ExitStatus)) ); |
650 | request->process = process; |
651 | |
652 | // process.setEnvironment(envlist); |
653 | QStringList args; |
654 | foreach (const QString &arg, request->arg_list) |
655 | args << arg; |
656 | |
657 | QString executable = request->name; |
658 | #ifdef Q_WS_MAC |
659 | const QString bundlepath = KStandardDirs::findExe(executable); |
660 | if (!bundlepath.isEmpty()) |
661 | executable = bundlepath; |
662 | #endif |
663 | process->setProgram(executable,args); |
664 | process->start(); |
665 | |
666 | if (!process->waitForStarted()) |
667 | { |
668 | processRequestReturn(LAUNCHER_ERROR,"" ); |
669 | } |
670 | else |
671 | { |
672 | request->pid = process->pid(); |
673 | QByteArray data((char *)&request->pid, sizeof(int)); |
674 | processRequestReturn(LAUNCHER_OK,data); |
675 | } |
676 | return; |
677 | |
678 | #else |
679 | requestList.append( request ); |
680 | // Send request to kdeinit. |
681 | klauncher_header ; |
682 | QByteArray requestData; |
683 | requestData.reserve(1024); |
684 | |
685 | appendLong(requestData, request->arg_list.count() + 1); |
686 | requestData.append(request->name.toLocal8Bit()); |
687 | requestData.append('\0'); |
688 | foreach (const QString &arg, request->arg_list) |
689 | requestData.append(arg.toLocal8Bit()).append('\0'); |
690 | appendLong(requestData, request->envs.count()); |
691 | foreach (const QString &env, request->envs) |
692 | requestData.append(env.toLocal8Bit()).append('\0'); |
693 | appendLong(requestData, 0); // avoid_loops, always false here |
694 | #ifdef Q_WS_X11 |
695 | bool startup_notify = !request->startup_id.isNull() && request->startup_id != "0" ; |
696 | if( startup_notify ) |
697 | requestData.append(request->startup_id).append('\0'); |
698 | #endif |
699 | if (!request->cwd.isEmpty()) |
700 | requestData.append(QFile::encodeName(request->cwd)).append('\0'); |
701 | |
702 | #ifdef Q_WS_X11 |
703 | request_header.cmd = startup_notify ? LAUNCHER_EXT_EXEC : LAUNCHER_EXEC_NEW; |
704 | #else |
705 | request_header.cmd = LAUNCHER_EXEC_NEW; |
706 | #endif |
707 | request_header.arg_length = requestData.length(); |
708 | |
709 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
710 | kDebug(7016) << "Asking kdeinit to start" << request->name << request->arg_list |
711 | << "cmd=" << commandToString(request_header.cmd); |
712 | #endif |
713 | |
714 | kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); |
715 | kde_safe_write(kdeinitSocket, requestData.data(), requestData.length()); |
716 | |
717 | // Wait for pid to return. |
718 | lastRequest = request; |
719 | do { |
720 | slotKDEInitData( kdeinitSocket ); |
721 | } |
722 | while (lastRequest != 0); |
723 | #endif |
724 | } |
725 | |
726 | void KLauncher::exec_blind(const QString &name, const QStringList &arg_list, const QStringList &envs, const QString &startup_id) |
727 | { |
728 | KLaunchRequest *request = new KLaunchRequest; |
729 | request->autoStart = false; |
730 | request->name = name; |
731 | request->arg_list = arg_list; |
732 | request->dbus_startup_type = KService::DBusNone; |
733 | request->pid = 0; |
734 | request->status = KLaunchRequest::Launching; |
735 | request->envs = envs; |
736 | // Find service, if any - strip path if needed |
737 | KService::Ptr service = KService::serviceByDesktopName( name.mid( name.lastIndexOf(QLatin1Char('/')) + 1 )); |
738 | if (service) |
739 | send_service_startup_info(request, service, startup_id.toLocal8Bit(), QStringList()); |
740 | else // no .desktop file, no startup info |
741 | cancel_service_startup_info( request, startup_id.toLocal8Bit(), envs ); |
742 | |
743 | requestStart(request); |
744 | // We don't care about this request any longer.... |
745 | requestDone(request); |
746 | } |
747 | |
748 | |
749 | // KDE5: remove |
750 | bool |
751 | KLauncher::start_service_by_name(const QString &serviceName, const QStringList &urls, |
752 | const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) |
753 | { |
754 | KService::Ptr service; |
755 | // Find service |
756 | #ifndef KDE_NO_DEPRECATED |
757 | service = KService::serviceByName(serviceName); |
758 | #endif |
759 | if (!service) |
760 | { |
761 | requestResult.result = ENOENT; |
762 | requestResult.error = i18n("Could not find service '%1'." , serviceName); |
763 | cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any |
764 | return false; |
765 | } |
766 | return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); |
767 | } |
768 | |
769 | bool |
770 | KLauncher::start_service_by_desktop_path(const QString &serviceName, const QStringList &urls, |
771 | const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) |
772 | { |
773 | KService::Ptr service; |
774 | // Find service |
775 | const QFileInfo fi(serviceName); |
776 | if (fi.isAbsolute() && fi.exists()) |
777 | { |
778 | // Full path |
779 | service = new KService(serviceName); |
780 | } |
781 | else |
782 | { |
783 | service = KService::serviceByDesktopPath(serviceName); |
784 | // TODO? |
785 | //if (!service) |
786 | // service = KService::serviceByStorageId(serviceName); // This method should be named start_service_by_storage_id ideally... |
787 | } |
788 | if (!service) |
789 | { |
790 | requestResult.result = ENOENT; |
791 | requestResult.error = i18n("Could not find service '%1'." , serviceName); |
792 | cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any |
793 | return false; |
794 | } |
795 | return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); |
796 | } |
797 | |
798 | bool |
799 | KLauncher::start_service_by_desktop_name(const QString &serviceName, const QStringList &urls, |
800 | const QStringList &envs, const QString& startup_id, bool blind, const QDBusMessage &msg) |
801 | { |
802 | KService::Ptr service = KService::serviceByDesktopName(serviceName); |
803 | if (!service) |
804 | { |
805 | requestResult.result = ENOENT; |
806 | requestResult.error = i18n("Could not find service '%1'." , serviceName); |
807 | cancel_service_startup_info( NULL, startup_id.toLocal8Bit(), envs ); // cancel it if any |
808 | return false; |
809 | } |
810 | return start_service(service, urls, envs, startup_id.toLocal8Bit(), blind, false, msg); |
811 | } |
812 | |
813 | bool |
814 | KLauncher::start_service(KService::Ptr service, const QStringList &_urls, |
815 | const QStringList &envs, const QByteArray &startup_id, |
816 | bool blind, bool autoStart, const QDBusMessage &msg) |
817 | { |
818 | QStringList urls = _urls; |
819 | bool runPermitted = KDesktopFile::isAuthorizedDesktopFile(service->entryPath()); |
820 | |
821 | if (!service->isValid() || !runPermitted) |
822 | { |
823 | requestResult.result = ENOEXEC; |
824 | if (service->isValid()) |
825 | requestResult.error = i18n("Service '%1' must be executable to run." , service->entryPath()); |
826 | else |
827 | requestResult.error = i18n("Service '%1' is malformatted." , service->entryPath()); |
828 | cancel_service_startup_info( NULL, startup_id, envs ); // cancel it if any |
829 | return false; |
830 | } |
831 | KLaunchRequest *request = new KLaunchRequest; |
832 | request->autoStart = autoStart; |
833 | |
834 | if ((urls.count() > 1) && !service->allowMultipleFiles()) |
835 | { |
836 | // We need to launch the application N times. That sucks. |
837 | // We ignore the result for application 2 to N. |
838 | // For the first file we launch the application in the |
839 | // usual way. The reported result is based on this |
840 | // application. |
841 | QStringList::ConstIterator it = urls.constBegin(); |
842 | for(++it; |
843 | it != urls.constEnd(); |
844 | ++it) |
845 | { |
846 | QStringList singleUrl; |
847 | singleUrl.append(*it); |
848 | QByteArray startup_id2 = startup_id; |
849 | if( !startup_id2.isEmpty() && startup_id2 != "0" ) |
850 | startup_id2 = "0" ; // can't use the same startup_id several times // krazy:exclude=doublequote_chars |
851 | start_service( service, singleUrl, envs, startup_id2, true, false, msg); |
852 | } |
853 | QString firstURL = *(urls.begin()); |
854 | urls.clear(); |
855 | urls.append(firstURL); |
856 | } |
857 | createArgs(request, service, urls); |
858 | |
859 | // We must have one argument at least! |
860 | if (!request->arg_list.count()) |
861 | { |
862 | requestResult.result = ENOEXEC; |
863 | requestResult.error = i18n("Service '%1' is malformatted." , service->entryPath()); |
864 | delete request; |
865 | cancel_service_startup_info( NULL, startup_id, envs ); |
866 | return false; |
867 | } |
868 | |
869 | request->name = request->arg_list.takeFirst(); |
870 | |
871 | if (request->name.endsWith(QLatin1String("/kioexec" ))) { |
872 | // Special case for kioexec; if createArgs said we were going to use it, |
873 | // then we have to expect a kioexec-PID, not a org.kde.finalapp... |
874 | // Testcase: konqueror www.kde.org, RMB on link, open with, kruler. |
875 | |
876 | request->dbus_startup_type = KService::DBusMulti; |
877 | request->dbus_name = QString::fromLatin1("org.kde.kioexec" ); |
878 | } else { |
879 | request->dbus_startup_type = service->dbusStartupType(); |
880 | |
881 | if ((request->dbus_startup_type == KService::DBusUnique) || |
882 | (request->dbus_startup_type == KService::DBusMulti)) { |
883 | const QVariant v = service->property(QLatin1String("X-DBUS-ServiceName" )); |
884 | if (v.isValid()) { |
885 | request->dbus_name = v.toString(); |
886 | } |
887 | if (request->dbus_name.isEmpty()) { |
888 | const QString binName = KRun::binaryName(service->exec(), true); |
889 | request->dbus_name = QString::fromLatin1("org.kde." ) + binName; |
890 | request->tolerant_dbus_name = QString::fromLatin1("*." ) + binName; |
891 | } |
892 | } |
893 | } |
894 | |
895 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
896 | kDebug(7016) << "name=" << request->name << "dbus_name=" << request->dbus_name |
897 | << "startup type=" << s_DBusStartupTypeToString[request->dbus_startup_type]; |
898 | #endif |
899 | |
900 | request->pid = 0; |
901 | request->envs = envs; |
902 | send_service_startup_info( request, service, startup_id, envs ); |
903 | |
904 | // Request will be handled later. |
905 | if (!blind && !autoStart) |
906 | { |
907 | msg.setDelayedReply(true); |
908 | request->transaction = msg; |
909 | } |
910 | queueRequest(request); |
911 | return true; |
912 | } |
913 | |
914 | void |
915 | KLauncher::send_service_startup_info( KLaunchRequest *request, KService::Ptr service, const QByteArray& startup_id, |
916 | const QStringList &envs ) |
917 | { |
918 | #ifdef Q_WS_X11 |
919 | request->startup_id = "0" ;// krazy:exclude=doublequote_chars |
920 | if (startup_id == "0" ) |
921 | return; |
922 | bool silent; |
923 | QByteArray wmclass; |
924 | if( !KRun::checkStartupNotify( QString(), service.data(), &silent, &wmclass )) |
925 | return; |
926 | KStartupInfoId id; |
927 | id.initId(startup_id); |
928 | QByteArray dpy_str; |
929 | foreach (const QString &env, envs) { |
930 | if (env.startsWith(QLatin1String("DISPLAY=" ))) |
931 | dpy_str = env.mid(8).toLocal8Bit(); |
932 | } |
933 | Display* dpy = NULL; |
934 | if (!dpy_str.isEmpty() && mCached_dpy != NULL && dpy_str != XDisplayString(mCached_dpy)) |
935 | dpy = mCached_dpy; |
936 | if (dpy == NULL) |
937 | dpy = XOpenDisplay(dpy_str); |
938 | request->startup_id = id.id(); |
939 | if (dpy == NULL) { |
940 | cancel_service_startup_info( request, startup_id, envs ); |
941 | return; |
942 | } |
943 | |
944 | request->startup_dpy = dpy_str; |
945 | |
946 | KStartupInfoData data; |
947 | data.setName( service->name()); |
948 | data.setIcon( service->icon()); |
949 | data.setDescription( i18n( "Launching %1" , service->name())); |
950 | if( !wmclass.isEmpty()) |
951 | data.setWMClass( wmclass ); |
952 | if( silent ) |
953 | data.setSilent( KStartupInfoData::Yes ); |
954 | data.setApplicationId( service->entryPath()); |
955 | // the rest will be sent by kdeinit |
956 | KStartupInfo::sendStartupX( dpy, id, data ); |
957 | if( mCached_dpy != dpy && mCached_dpy != NULL ) |
958 | XCloseDisplay( mCached_dpy ); |
959 | mCached_dpy = dpy; |
960 | return; |
961 | #else |
962 | return; |
963 | #endif |
964 | } |
965 | |
966 | void |
967 | KLauncher::cancel_service_startup_info( KLaunchRequest* request, const QByteArray& startup_id, |
968 | const QStringList &envs ) |
969 | { |
970 | #ifdef Q_WS_X11 |
971 | if( request != NULL ) |
972 | request->startup_id = "0" ; // krazy:exclude=doublequote_chars |
973 | if( !startup_id.isEmpty() && startup_id != "0" ) |
974 | { |
975 | QString dpy_str; |
976 | foreach (const QString &env, envs) { |
977 | if (env.startsWith(QLatin1String("DISPLAY=" ))) |
978 | dpy_str = env.mid(8); |
979 | } |
980 | Display* dpy = NULL; |
981 | if( !dpy_str.isEmpty() && mCached_dpy != NULL |
982 | && dpy_str != QLatin1String(XDisplayString( mCached_dpy )) ) |
983 | dpy = mCached_dpy; |
984 | if( dpy == NULL ) |
985 | dpy = XOpenDisplay( dpy_str.toLatin1().constData() ); |
986 | if( dpy == NULL ) |
987 | return; |
988 | KStartupInfoId id; |
989 | id.initId(startup_id); |
990 | KStartupInfo::sendFinishX( dpy, id ); |
991 | if( mCached_dpy != dpy && mCached_dpy != NULL ) |
992 | XCloseDisplay( mCached_dpy ); |
993 | mCached_dpy = dpy; |
994 | } |
995 | #endif |
996 | } |
997 | |
998 | bool |
999 | KLauncher::kdeinit_exec(const QString &app, const QStringList &args, |
1000 | const QString& workdir, const QStringList &envs, |
1001 | const QString &startup_id, bool wait, const QDBusMessage &msg) |
1002 | { |
1003 | KLaunchRequest *request = new KLaunchRequest; |
1004 | request->autoStart = false; |
1005 | request->arg_list = args; |
1006 | request->name = app; |
1007 | if (wait) |
1008 | request->dbus_startup_type = KService::DBusWait; |
1009 | else |
1010 | request->dbus_startup_type = KService::DBusNone; |
1011 | request->pid = 0; |
1012 | #ifdef Q_WS_X11 |
1013 | request->startup_id = startup_id.toLocal8Bit(); |
1014 | #endif |
1015 | request->envs = envs; |
1016 | request->cwd = workdir; |
1017 | #ifdef Q_WS_X11 |
1018 | if (!app.endsWith(QLatin1String("kbuildsycoca4" ))) { // avoid stupid loop |
1019 | // Find service, if any - strip path if needed |
1020 | const QString desktopName = app.mid(app.lastIndexOf(QLatin1Char('/')) + 1); |
1021 | KService::Ptr service = KService::serviceByDesktopName(desktopName); |
1022 | if (service) |
1023 | send_service_startup_info(request, service, |
1024 | request->startup_id, envs); |
1025 | else // no .desktop file, no startup info |
1026 | cancel_service_startup_info(request, request->startup_id, envs); |
1027 | } |
1028 | #endif |
1029 | msg.setDelayedReply(true); |
1030 | request->transaction = msg; |
1031 | queueRequest(request); |
1032 | return true; |
1033 | } |
1034 | |
1035 | void |
1036 | KLauncher::queueRequest(KLaunchRequest *request) |
1037 | { |
1038 | requestQueue.append( request ); |
1039 | if (!bProcessingQueue) |
1040 | { |
1041 | bProcessingQueue = true; |
1042 | QTimer::singleShot(0, this, SLOT(slotDequeue())); |
1043 | } |
1044 | } |
1045 | |
1046 | void |
1047 | KLauncher::slotDequeue() |
1048 | { |
1049 | do { |
1050 | KLaunchRequest *request = requestQueue.takeFirst(); |
1051 | // process request |
1052 | request->status = KLaunchRequest::Launching; |
1053 | requestStart(request); |
1054 | if (request->status != KLaunchRequest::Launching) |
1055 | { |
1056 | // Request handled. |
1057 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
1058 | kDebug(7016) << "Request handled already" ; |
1059 | #endif |
1060 | requestDone( request ); |
1061 | continue; |
1062 | } |
1063 | } while(requestQueue.count()); |
1064 | bProcessingQueue = false; |
1065 | } |
1066 | |
1067 | void |
1068 | KLauncher::createArgs( KLaunchRequest *request, const KService::Ptr service , |
1069 | const QStringList &urls) |
1070 | { |
1071 | const QStringList params = KRun::processDesktopExec(*service, urls); |
1072 | |
1073 | for(QStringList::ConstIterator it = params.begin(); |
1074 | it != params.end(); ++it) |
1075 | { |
1076 | request->arg_list.append(*it); |
1077 | } |
1078 | |
1079 | const QString& path = service->path(); |
1080 | if (!path.isEmpty()) { |
1081 | request->cwd = path; |
1082 | } else if (!urls.isEmpty()) { |
1083 | const KUrl url(urls.first()); |
1084 | if (url.isLocalFile()) { |
1085 | request->cwd = url.directory(); |
1086 | } |
1087 | } |
1088 | } |
1089 | |
1090 | ///// IO-Slave functions |
1091 | |
1092 | pid_t |
1093 | KLauncher::requestHoldSlave(const KUrl &url, const QString &app_socket) |
1094 | { |
1095 | IdleSlave *slave = 0; |
1096 | foreach (IdleSlave *p, mSlaveList) |
1097 | { |
1098 | if (p->onHold(url)) |
1099 | { |
1100 | slave = p; |
1101 | break; |
1102 | } |
1103 | } |
1104 | if (slave) |
1105 | { |
1106 | mSlaveList.removeAll(slave); |
1107 | slave->connect(app_socket); |
1108 | return slave->pid(); |
1109 | } |
1110 | return 0; |
1111 | } |
1112 | |
1113 | pid_t |
1114 | KLauncher::requestSlave(const QString &protocol, |
1115 | const QString &host, |
1116 | const QString &app_socket, |
1117 | QString &error) |
1118 | { |
1119 | IdleSlave *slave = 0; |
1120 | foreach (IdleSlave *p, mSlaveList) |
1121 | { |
1122 | if (p->match(protocol, host, true)) |
1123 | { |
1124 | slave = p; |
1125 | break; |
1126 | } |
1127 | } |
1128 | if (!slave) |
1129 | { |
1130 | foreach (IdleSlave *p, mSlaveList) |
1131 | { |
1132 | if (p->match(protocol, host, false)) |
1133 | { |
1134 | slave = p; |
1135 | break; |
1136 | } |
1137 | } |
1138 | } |
1139 | if (!slave) |
1140 | { |
1141 | foreach (IdleSlave *p, mSlaveList) |
1142 | { |
1143 | if (p->match(protocol, QString(), false)) |
1144 | { |
1145 | slave = p; |
1146 | break; |
1147 | } |
1148 | } |
1149 | } |
1150 | if (slave) |
1151 | { |
1152 | mSlaveList.removeAll(slave); |
1153 | slave->connect(app_socket); |
1154 | return slave->pid(); |
1155 | } |
1156 | |
1157 | QString name = KProtocolInfo::exec(protocol); |
1158 | if (name.isEmpty()) |
1159 | { |
1160 | error = i18n("Unknown protocol '%1'.\n" , protocol); |
1161 | return 0; |
1162 | } |
1163 | |
1164 | QStringList arg_list; |
1165 | #ifdef USE_KPROCESS_FOR_KIOSLAVES |
1166 | arg_list << name; |
1167 | arg_list << protocol; |
1168 | arg_list << mConnectionServer.address(); |
1169 | arg_list << app_socket; |
1170 | name = KStandardDirs::findExe(QLatin1String("kioslave" )); |
1171 | #else |
1172 | QString arg1 = protocol; |
1173 | QString arg2 = mConnectionServer.address(); |
1174 | QString arg3 = app_socket; |
1175 | arg_list.append(arg1); |
1176 | arg_list.append(arg2); |
1177 | arg_list.append(arg3); |
1178 | #endif |
1179 | |
1180 | kDebug(7016) << "KLauncher: launching new slave " << name << " with protocol=" << protocol |
1181 | << " args=" << arg_list << endl; |
1182 | |
1183 | #ifdef Q_OS_UNIX |
1184 | if (mSlaveDebug == protocol) |
1185 | { |
1186 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
1187 | klauncher_header ; |
1188 | request_header.cmd = LAUNCHER_DEBUG_WAIT; |
1189 | request_header.arg_length = 0; |
1190 | kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); |
1191 | #else |
1192 | name = QString::fromLatin1("gdb" ); |
1193 | #endif |
1194 | } |
1195 | if (mSlaveValgrind == protocol) { |
1196 | #ifndef USE_KPROCESS_FOR_KIOSLAVES // otherwise we've already done this |
1197 | KLibrary lib(name, KGlobal::mainComponent()); |
1198 | arg_list.prepend(lib.fileName()); |
1199 | arg_list.prepend(KStandardDirs::locate("exe" , QString::fromLatin1("kioslave" ))); |
1200 | #endif |
1201 | name = QString::fromLatin1("valgrind" ); |
1202 | |
1203 | if (!mSlaveValgrindSkin.isEmpty()) { |
1204 | arg_list.prepend(QLatin1String("--tool=" ) + mSlaveValgrindSkin); |
1205 | } else |
1206 | arg_list.prepend(QLatin1String("--tool=memcheck" )); |
1207 | } |
1208 | #endif |
1209 | KLaunchRequest *request = new KLaunchRequest; |
1210 | request->autoStart = false; |
1211 | request->name = name; |
1212 | request->arg_list = arg_list; |
1213 | request->dbus_startup_type = KService::DBusNone; |
1214 | request->pid = 0; |
1215 | #ifdef Q_WS_X11 |
1216 | request->startup_id = "0" ; // krazy:exclude=doublequote_chars |
1217 | #endif |
1218 | request->status = KLaunchRequest::Launching; |
1219 | requestStart(request); |
1220 | pid_t pid = request->pid; |
1221 | |
1222 | // kDebug(7016) << "Slave launched, pid = " << pid; |
1223 | |
1224 | // We don't care about this request any longer.... |
1225 | requestDone(request); |
1226 | if (!pid) |
1227 | { |
1228 | error = i18n("Error loading '%1'.\n" , name); |
1229 | } |
1230 | return pid; |
1231 | } |
1232 | |
1233 | bool KLauncher::checkForHeldSlave(const QString &url) |
1234 | { |
1235 | Q_FOREACH (const IdleSlave *p, mSlaveList) { |
1236 | if (p->onHold(url)) { |
1237 | return true; |
1238 | } |
1239 | } |
1240 | return false; |
1241 | } |
1242 | |
1243 | void |
1244 | KLauncher::waitForSlave(int pid, const QDBusMessage &msg) |
1245 | { |
1246 | foreach (IdleSlave *slave, mSlaveList) |
1247 | { |
1248 | if (slave->pid() == static_cast<pid_t>(pid)) |
1249 | return; // Already here. |
1250 | } |
1251 | SlaveWaitRequest *waitRequest = new SlaveWaitRequest; |
1252 | msg.setDelayedReply(true); |
1253 | waitRequest->transaction = msg; |
1254 | waitRequest->pid = static_cast<pid_t>(pid); |
1255 | mSlaveWaitRequest.append(waitRequest); |
1256 | } |
1257 | |
1258 | void |
1259 | KLauncher::acceptSlave() |
1260 | { |
1261 | IdleSlave *slave = new IdleSlave(this); |
1262 | mConnectionServer.setNextPendingConnection(&slave->mConn); |
1263 | mSlaveList.append(slave); |
1264 | connect(slave, SIGNAL(destroyed()), this, SLOT(slotSlaveGone())); |
1265 | connect(slave, SIGNAL(statusUpdate(IdleSlave*)), |
1266 | this, SLOT(slotSlaveStatus(IdleSlave*))); |
1267 | if (!mTimer.isActive()) |
1268 | { |
1269 | mTimer.start(1000*10); |
1270 | } |
1271 | } |
1272 | |
1273 | void |
1274 | KLauncher::slotSlaveStatus(IdleSlave *slave) |
1275 | { |
1276 | QMutableListIterator<SlaveWaitRequest *> it(mSlaveWaitRequest); |
1277 | while(it.hasNext()) |
1278 | { |
1279 | SlaveWaitRequest *waitRequest = it.next(); |
1280 | if (waitRequest->pid == slave->pid()) |
1281 | { |
1282 | QDBusConnection::sessionBus().send(waitRequest->transaction.createReply()); |
1283 | it.remove(); |
1284 | delete waitRequest; |
1285 | } |
1286 | } |
1287 | } |
1288 | |
1289 | void |
1290 | KLauncher::slotSlaveGone() |
1291 | { |
1292 | IdleSlave *slave = (IdleSlave *) sender(); |
1293 | mSlaveList.removeAll(slave); |
1294 | if ((mSlaveList.count() == 0) && (mTimer.isActive())) |
1295 | { |
1296 | mTimer.stop(); |
1297 | } |
1298 | } |
1299 | |
1300 | void |
1301 | KLauncher::idleTimeout() |
1302 | { |
1303 | bool keepOneFileSlave=true; |
1304 | time_t now = time(0); |
1305 | foreach (IdleSlave *slave, mSlaveList) |
1306 | { |
1307 | if ((slave->protocol()==QLatin1String("file" )) && (keepOneFileSlave)) |
1308 | keepOneFileSlave=false; |
1309 | else if (slave->age(now) > SLAVE_MAX_IDLE) |
1310 | { |
1311 | // killing idle slave |
1312 | delete slave; |
1313 | } |
1314 | } |
1315 | } |
1316 | |
1317 | void KLauncher::reparseConfiguration() |
1318 | { |
1319 | KProtocolManager::reparseConfiguration(); |
1320 | foreach (IdleSlave *slave, mSlaveList) |
1321 | slave->reparseConfiguration(); |
1322 | } |
1323 | |
1324 | |
1325 | void |
1326 | KLauncher::slotGotOutput() |
1327 | { |
1328 | #ifdef USE_KPROCESS_FOR_KIOSLAVES |
1329 | KProcess *p = static_cast<KProcess *>(sender()); |
1330 | QByteArray _stdout = p->readAllStandardOutput(); |
1331 | kDebug(7016) << _stdout.data(); |
1332 | #endif |
1333 | } |
1334 | |
1335 | void |
1336 | KLauncher::slotFinished(int exitCode, QProcess::ExitStatus exitStatus ) |
1337 | { |
1338 | #ifdef USE_KPROCESS_FOR_KIOSLAVES |
1339 | KProcess *p = static_cast<KProcess *>(sender()); |
1340 | kDebug(7016) << "process finished exitcode=" << exitCode << "exitStatus=" << exitStatus; |
1341 | |
1342 | foreach (KLaunchRequest *request, requestList) |
1343 | { |
1344 | if (request->process == p) |
1345 | { |
1346 | #ifdef KLAUNCHER_VERBOSE_OUTPUT |
1347 | kDebug(7016) << "found KProcess, request done" ; |
1348 | #endif |
1349 | if (exitCode == 0 && exitStatus == QProcess::NormalExit) |
1350 | request->status = KLaunchRequest::Done; |
1351 | else |
1352 | request->status = KLaunchRequest::Error; |
1353 | requestDone(request); |
1354 | request->process = 0; |
1355 | } |
1356 | } |
1357 | delete p; |
1358 | #else |
1359 | Q_UNUSED(exitCode); |
1360 | Q_UNUSED(exitStatus); |
1361 | #endif |
1362 | } |
1363 | |
1364 | |
1365 | void KLauncher::terminate_kdeinit() |
1366 | { |
1367 | kDebug(7016); |
1368 | #ifndef USE_KPROCESS_FOR_KIOSLAVES |
1369 | klauncher_header ; |
1370 | request_header.cmd = LAUNCHER_TERMINATE_KDEINIT; |
1371 | request_header.arg_length = 0; |
1372 | kde_safe_write(kdeinitSocket, &request_header, sizeof(request_header)); |
1373 | #endif |
1374 | } |
1375 | |
1376 | #include "klauncher.moc" |
1377 | |