summaryrefslogtreecommitdiff
path: root/noncore/net/networksetup/kprocess.cpp
Unidiff
Diffstat (limited to 'noncore/net/networksetup/kprocess.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/net/networksetup/kprocess.cpp919
1 files changed, 0 insertions, 919 deletions
diff --git a/noncore/net/networksetup/kprocess.cpp b/noncore/net/networksetup/kprocess.cpp
deleted file mode 100644
index 193ec9b..0000000
--- a/noncore/net/networksetup/kprocess.cpp
+++ b/dev/null
@@ -1,919 +0,0 @@
1/*
2
3 $Id$
4
5 This file is part of the KDE libraries
6 Copyright (C) 1997 Christian Czezatke (e9025461@student.tuwien.ac.at)
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
22*/
23
24
25//
26// KPROCESS -- A class for handling child processes in KDE without
27// having to take care of Un*x specific implementation details
28//
29// version 0.3.1, Jan 8th 1998
30//
31// (C) Christian Czezatke
32// e9025461@student.tuwien.ac.at
33//
34// Changes:
35//
36// March 2nd, 1998: Changed parameter list for KShellProcess:
37// Arguments are now placed in a single string so that
38// <shell> -c <commandstring> is passed to the shell
39// to make the use of "operator<<" consistent with KProcess
40
41#include "kprocess.h"
42#define _MAY_INCLUDE_KPROCESSCONTROLLER_
43#include "kprocctrl.h"
44
45//#include <config.h>
46
47#include <qglobal.h>
48#include <qmap.h>
49#include <qfile.h>
50#include <qsocketnotifier.h>
51#include <qregexp.h>
52
53#include <sys/time.h>
54#include <sys/types.h>
55#include <sys/stat.h>
56#include <sys/socket.h>
57
58#include <errno.h>
59#include <fcntl.h>
60#include <stdlib.h>
61#include <signal.h>
62#include <stdio.h>
63#include <string.h>
64#include <unistd.h>
65#ifdef HAVE_SYS_SELECT_H
66#include <sys/select.h>
67#endif
68#ifdef HAVE_INITGROUPS
69#include <grp.h>
70#endif
71#include <pwd.h>
72
73#include <qapplication.h>
74//#include <kdebug.h>
75
76/////////////////////////////
77// public member functions //
78/////////////////////////////
79
80class KProcessPrivate {
81public:
82 KProcessPrivate() : useShell(false) { }
83
84 bool useShell;
85 QMap<QString,QString> env;
86 QString wd;
87 QCString shell;
88};
89
90#define Q_CHECK_PTR(a)
91
92KProcess::KProcess()
93 : QObject(),
94 run_mode(NotifyOnExit),
95 runs(false),
96 pid_(0),
97 status(0),
98 keepPrivs(false),
99 innot(0),
100 outnot(0),
101 errnot(0),
102 communication(NoCommunication),
103 input_data(0),
104 input_sent(0),
105 input_total(0),
106 d(0)
107{
108 if (0 == KProcessController::theKProcessController) {
109 (void) new KProcessController();
110 Q_CHECK_PTR(KProcessController::theKProcessController);
111 }
112
113 KProcessController::theKProcessController->addKProcess(this);
114 out[0] = out[1] = -1;
115 in[0] = in[1] = -1;
116 err[0] = err[1] = -1;
117}
118
119void
120KProcess::setEnvironment(const QString &name, const QString &value)
121{
122 if (!d)
123 d = new KProcessPrivate;
124 d->env.insert(name, value);
125}
126
127void
128KProcess::setWorkingDirectory(const QString &dir)
129{
130 if (!d)
131 d = new KProcessPrivate;
132 d->wd = dir;
133}
134
135void
136KProcess::setupEnvironment()
137{
138 if (d)
139 {
140 QMap<QString,QString>::Iterator it;
141 for(it = d->env.begin(); it != d->env.end(); ++it)
142 setenv(QFile::encodeName(it.key()).data(),
143 QFile::encodeName(it.data()).data(), 1);
144 if (!d->wd.isEmpty())
145 chdir(QFile::encodeName(d->wd).data());
146 }
147}
148
149void
150KProcess::setRunPrivileged(bool keepPrivileges)
151{
152 keepPrivs = keepPrivileges;
153}
154
155bool
156KProcess::runPrivileged() const
157{
158 return keepPrivs;
159}
160
161
162KProcess::~KProcess()
163{
164 // destroying the KProcess instance sends a SIGKILL to the
165 // child process (if it is running) after removing it from the
166 // list of valid processes (if the process is not started as
167 // "DontCare")
168
169 KProcessController::theKProcessController->removeKProcess(this);
170 // this must happen before we kill the child
171 // TODO: block the signal while removing the current process from the process list
172
173 if (runs && (run_mode != DontCare))
174 kill(SIGKILL);
175
176 // Clean up open fd's and socket notifiers.
177 closeStdin();
178 closeStdout();
179 closeStderr();
180
181 // TODO: restore SIGCHLD and SIGPIPE handler if this is the last KProcess
182 delete d;
183}
184
185void KProcess::detach()
186{
187 KProcessController::theKProcessController->removeKProcess(this);
188
189 runs = false;
190 pid_ = 0;
191
192 // Clean up open fd's and socket notifiers.
193 closeStdin();
194 closeStdout();
195 closeStderr();
196}
197
198bool KProcess::setExecutable(const QString& proc)
199{
200 if (runs) return false;
201
202 if (proc.isEmpty()) return false;
203
204 if (!arguments.isEmpty())
205 arguments.remove(arguments.begin());
206 arguments.prepend(QFile::encodeName(proc));
207
208 return true;
209}
210
211KProcess &KProcess::operator<<(const QStringList& args)
212{
213 QStringList::ConstIterator it = args.begin();
214 for ( ; it != args.end() ; ++it )
215 arguments.append(QFile::encodeName(*it));
216 return *this;
217}
218
219KProcess &KProcess::operator<<(const QCString& arg)
220{
221 return operator<< (arg.data());
222}
223
224KProcess &KProcess::operator<<(const char* arg)
225{
226 arguments.append(arg);
227 return *this;
228}
229
230KProcess &KProcess::operator<<(const QString& arg)
231{
232 arguments.append(QFile::encodeName(arg));
233 return *this;
234}
235
236void KProcess::clearArguments()
237{
238 arguments.clear();
239}
240
241bool KProcess::start(RunMode runmode, Communication comm)
242{
243 uint i;
244 uint n = arguments.count();
245 char **arglist;
246
247 if (runs || (0 == n)) {
248 return false; // cannot start a process that is already running
249 // or if no executable has been assigned
250 }
251 run_mode = runmode;
252 status = 0;
253
254 QCString shellCmd;
255 if (d && d->useShell)
256 {
257 if (d->shell.isEmpty())
258 {
259 //kdDebug() << "Could not find a valid shell\n" << endl;
260 return false;
261 }
262
263 arglist = static_cast<char **>(malloc( (4)*sizeof(char *)));
264 for (i=0; i < n; i++) {
265 shellCmd += arguments[i];
266 shellCmd += " "; // CC: to separate the arguments
267 }
268
269 arglist[0] = d->shell.data();
270 arglist[1] = (char *) "-c";
271 arglist[2] = shellCmd.data();
272 arglist[3] = 0;
273 }
274 else
275 {
276 arglist = static_cast<char **>(malloc( (n+1)*sizeof(char *)));
277 for (i=0; i < n; i++)
278 arglist[i] = arguments[i].data();
279 arglist[n]= 0;
280 }
281
282 setupCommunication(comm);
283 //)
284 //kdDebug() << "Could not setup Communication!\n";
285
286 // We do this in the parent because if we do it in the child process
287 // gdb gets confused when the application runs from gdb.
288 uid_t uid = getuid();
289 gid_t gid = getgid();
290#ifdef HAVE_INITGROUPS
291 struct passwd *pw = getpwuid(uid);
292#endif
293
294 int fd[2];
295 if (0 > pipe(fd))
296 {
297 fd[0] = fd[1] = 0; // Pipe failed.. continue
298 }
299
300 runs = true;
301
302 QApplication::flushX();
303
304 // WABA: Note that we use fork() and not vfork() because
305 // vfork() has unclear semantics and is not standardized.
306 pid_ = fork();
307
308 if (0 == pid_) {
309 if (fd[0])
310 close(fd[0]);
311 if (!runPrivileged())
312 {
313 setgid(gid);
314#if defined( HAVE_INITGROUPS)
315 if(pw)
316 initgroups(pw->pw_name, pw->pw_gid);
317#endif
318 setuid(uid);
319 }
320 // The child process
321 commSetupDoneC();
322 //)
323 //kdDebug() << "Could not finish comm setup in child!" << endl;
324
325 setupEnvironment();
326
327 // Matthias
328 if (run_mode == DontCare)
329 setpgid(0,0);
330 // restore default SIGPIPE handler (Harri)
331 struct sigaction act;
332 sigemptyset(&(act.sa_mask));
333 sigaddset(&(act.sa_mask), SIGPIPE);
334 act.sa_handler = SIG_DFL;
335 act.sa_flags = 0;
336 sigaction(SIGPIPE, &act, 0L);
337
338 // We set the close on exec flag.
339 // Closing of fd[1] indicates that the execvp succeeded!
340 if (fd[1])
341 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
342 execvp(arglist[0], arglist);
343 char resultByte = 1;
344 if (fd[1])
345 write(fd[1], &resultByte, 1);
346 _exit(-1);
347 } else if (-1 == pid_) {
348 // forking failed
349
350 runs = false;
351 free(arglist);
352 return false;
353 } else {
354 if (fd[1])
355 close(fd[1]);
356 // the parent continues here
357
358 // Discard any data for stdin that might still be there
359 input_data = 0;
360
361 // Check whether client could be started.
362 if (fd[0]) for(;;)
363 {
364 char resultByte;
365 int n = ::read(fd[0], &resultByte, 1);
366 if (n == 1)
367 {
368 // Error
369 runs = false;
370 close(fd[0]);
371 free(arglist);
372 pid_ = 0;
373 return false;
374 }
375 if (n == -1)
376 {
377 if ((errno == ECHILD) || (errno == EINTR))
378 continue; // Ignore
379 }
380 break; // success
381 }
382 if (fd[0])
383 close(fd[0]);
384
385 if (!commSetupDoneP()){} // finish communication socket setup for the parent
386 //kdDebug() << "Could not finish comm setup in parent!" << endl;
387
388 if (run_mode == Block) {
389 commClose();
390
391 // The SIGCHLD handler of the process controller will catch
392 // the exit and set the status
393 while(runs)
394 {
395 KProcessController::theKProcessController->
396 slotDoHousekeeping(0);
397 }
398 runs = FALSE;
399 emit processExited(this);
400 }
401 }
402 free(arglist);
403 return true;
404}
405
406
407
408bool KProcess::kill(int signo)
409{
410 bool rv=false;
411
412 if (0 != pid_)
413 rv= (-1 != ::kill(pid_, signo));
414 // probably store errno somewhere...
415 return rv;
416}
417
418
419
420bool KProcess::isRunning() const
421{
422 return runs;
423}
424
425
426
427pid_t KProcess::pid() const
428{
429 return pid_;
430}
431
432
433
434bool KProcess::normalExit() const
435{
436 int _status = status;
437 return (pid_ != 0) && (!runs) && (WIFEXITED((_status)));
438}
439
440
441
442int KProcess::exitStatus() const
443{
444 int _status = status;
445 return WEXITSTATUS((_status));
446}
447
448
449
450bool KProcess::writeStdin(const char *buffer, int buflen)
451{
452 bool rv;
453
454 // if there is still data pending, writing new data
455 // to stdout is not allowed (since it could also confuse
456 // kprocess...
457 if (0 != input_data)
458 return false;
459
460 if (runs && (communication & Stdin)) {
461 input_data = buffer;
462 input_sent = 0;
463 input_total = buflen;
464 slotSendData(0);
465 innot->setEnabled(true);
466 rv = true;
467 } else
468 rv = false;
469 return rv;
470}
471
472void KProcess::suspend()
473{
474 if ((communication & Stdout) && outnot)
475 outnot->setEnabled(false);
476}
477
478void KProcess::resume()
479{
480 if ((communication & Stdout) && outnot)
481 outnot->setEnabled(true);
482}
483
484bool KProcess::closeStdin()
485{
486 bool rv;
487
488 if (communication & Stdin) {
489 communication = (Communication) (communication & ~Stdin);
490 delete innot;
491 innot = 0;
492 close(in[1]);
493 rv = true;
494 } else
495 rv = false;
496 return rv;
497}
498
499bool KProcess::closeStdout()
500{
501 bool rv;
502
503 if (communication & Stdout) {
504 communication = (Communication) (communication & ~Stdout);
505 delete outnot;
506 outnot = 0;
507 close(out[0]);
508 rv = true;
509 } else
510 rv = false;
511 return rv;
512}
513
514bool KProcess::closeStderr()
515{
516 bool rv;
517
518 if (communication & Stderr) {
519 communication = static_cast<Communication>(communication & ~Stderr);
520 delete errnot;
521 errnot = 0;
522 close(err[0]);
523 rv = true;
524 } else
525 rv = false;
526 return rv;
527}
528
529
530/////////////////////////////
531// protected slots //
532/////////////////////////////
533
534
535
536void KProcess::slotChildOutput(int fdno)
537{
538 if (!childOutput(fdno))
539 closeStdout();
540}
541
542
543void KProcess::slotChildError(int fdno)
544{
545 if (!childError(fdno))
546 closeStderr();
547}
548
549
550void KProcess::slotSendData(int)
551{
552 if (input_sent == input_total) {
553 innot->setEnabled(false);
554 input_data = 0;
555 emit wroteStdin(this);
556 } else
557 input_sent += ::write(in[1], input_data+input_sent, input_total-input_sent);
558}
559
560
561
562//////////////////////////////
563// private member functions //
564//////////////////////////////
565
566
567
568void KProcess::processHasExited(int state)
569{
570 if (runs)
571 {
572 runs = false;
573 status = state;
574
575 commClose(); // cleanup communication sockets
576
577 // also emit a signal if the process was run Blocking
578 if (DontCare != run_mode)
579 {
580 emit processExited(this);
581 }
582 }
583}
584
585
586
587int KProcess::childOutput(int fdno)
588{
589 if (communication & NoRead) {
590 int len = -1;
591 emit receivedStdout(fdno, len);
592 errno = 0; // Make sure errno doesn't read "EAGAIN"
593 return len;
594 }
595 else
596 {
597 char buffer[1024];
598 int len;
599
600 len = ::read(fdno, buffer, 1024);
601
602 if ( 0 < len) {
603 emit receivedStdout(this, buffer, len);
604 }
605 return len;
606 }
607}
608
609
610
611int KProcess::childError(int fdno)
612{
613 char buffer[1024];
614 int len;
615
616 len = ::read(fdno, buffer, 1024);
617
618 if ( 0 < len)
619 emit receivedStderr(this, buffer, len);
620 return len;
621}
622
623
624
625int KProcess::setupCommunication(Communication comm)
626{
627 int ok;
628
629 communication = comm;
630
631 ok = 1;
632 if (comm & Stdin)
633 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, in) >= 0;
634
635 if (comm & Stdout)
636 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, out) >= 0;
637
638 if (comm & Stderr)
639 ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, err) >= 0;
640
641 return ok;
642}
643
644
645
646int KProcess::commSetupDoneP()
647{
648 int ok = 1;
649
650 if (communication != NoCommunication) {
651 if (communication & Stdin)
652 close(in[0]);
653 if (communication & Stdout)
654 close(out[1]);
655 if (communication & Stderr)
656 close(err[1]);
657
658 // Don't create socket notifiers and set the sockets non-blocking if
659 // blocking is requested.
660 if (run_mode == Block) return ok;
661
662 if (communication & Stdin) {
663// ok &= (-1 != fcntl(in[1], F_SETFL, O_NONBLOCK));
664 innot = new QSocketNotifier(in[1], QSocketNotifier::Write, this);
665 Q_CHECK_PTR(innot);
666 innot->setEnabled(false); // will be enabled when data has to be sent
667 QObject::connect(innot, SIGNAL(activated(int)),
668 this, SLOT(slotSendData(int)));
669 }
670
671 if (communication & Stdout) {
672// ok &= (-1 != fcntl(out[0], F_SETFL, O_NONBLOCK));
673 outnot = new QSocketNotifier(out[0], QSocketNotifier::Read, this);
674 Q_CHECK_PTR(outnot);
675 QObject::connect(outnot, SIGNAL(activated(int)),
676 this, SLOT(slotChildOutput(int)));
677 if (communication & NoRead)
678 suspend();
679 }
680
681 if (communication & Stderr) {
682// ok &= (-1 != fcntl(err[0], F_SETFL, O_NONBLOCK));
683 errnot = new QSocketNotifier(err[0], QSocketNotifier::Read, this );
684 Q_CHECK_PTR(errnot);
685 QObject::connect(errnot, SIGNAL(activated(int)),
686 this, SLOT(slotChildError(int)));
687 }
688 }
689 return ok;
690}
691
692
693
694int KProcess::commSetupDoneC()
695{
696 int ok = 1;
697 struct linger so;
698 memset(&so, 0, sizeof(so));
699
700 if (communication & Stdin)
701 close(in[1]);
702 if (communication & Stdout)
703 close(out[0]);
704 if (communication & Stderr)
705 close(err[0]);
706
707 if (communication & Stdin)
708 ok &= dup2(in[0], STDIN_FILENO) != -1;
709 else {
710 int null_fd = open( "/dev/null", O_RDONLY );
711 ok &= dup2( null_fd, STDIN_FILENO ) != -1;
712 close( null_fd );
713 }
714 if (communication & Stdout) {
715 ok &= dup2(out[1], STDOUT_FILENO) != -1;
716 ok &= !setsockopt(out[1], SOL_SOCKET, SO_LINGER, (char*)&so, sizeof(so));
717 }
718 else {
719 int null_fd = open( "/dev/null", O_WRONLY );
720 ok &= dup2( null_fd, STDOUT_FILENO ) != -1;
721 close( null_fd );
722 }
723 if (communication & Stderr) {
724 ok &= dup2(err[1], STDERR_FILENO) != -1;
725 ok &= !setsockopt(err[1], SOL_SOCKET, SO_LINGER, reinterpret_cast<char *>(&so), sizeof(so));
726 }
727 else {
728 int null_fd = open( "/dev/null", O_WRONLY );
729 ok &= dup2( null_fd, STDERR_FILENO ) != -1;
730 close( null_fd );
731 }
732 return ok;
733}
734
735
736
737void KProcess::commClose()
738{
739 if (NoCommunication != communication) {
740 bool b_in = (communication & Stdin);
741 bool b_out = (communication & Stdout);
742 bool b_err = (communication & Stderr);
743 if (b_in)
744 delete innot;
745
746 if (b_out || b_err) {
747 // If both channels are being read we need to make sure that one socket buffer
748 // doesn't fill up whilst we are waiting for data on the other (causing a deadlock).
749 // Hence we need to use select.
750
751 // Once one or other of the channels has reached EOF (or given an error) go back
752 // to the usual mechanism.
753
754 int fds_ready = 1;
755 fd_set rfds;
756
757 int max_fd = 0;
758 if (b_out) {
759 fcntl(out[0], F_SETFL, O_NONBLOCK);
760 if (out[0] > max_fd)
761 max_fd = out[0];
762 delete outnot;
763 outnot = 0;
764 }
765 if (b_err) {
766 fcntl(err[0], F_SETFL, O_NONBLOCK);
767 if (err[0] > max_fd)
768 max_fd = err[0];
769 delete errnot;
770 errnot = 0;
771 }
772
773
774 while (b_out || b_err) {
775 // * If the process is still running we block until we
776 // receive data. (p_timeout = 0, no timeout)
777 // * If the process has already exited, we only check
778 // the available data, we don't wait for more.
779 // (p_timeout = &timeout, timeout immediately)
780 struct timeval timeout;
781 timeout.tv_sec = 0;
782 timeout.tv_usec = 0;
783 struct timeval *p_timeout = runs ? 0 : &timeout;
784
785 FD_ZERO(&rfds);
786 if (b_out)
787 FD_SET(out[0], &rfds);
788
789 if (b_err)
790 FD_SET(err[0], &rfds);
791
792 fds_ready = select(max_fd+1, &rfds, 0, 0, p_timeout);
793 if (fds_ready <= 0) break;
794
795 if (b_out && FD_ISSET(out[0], &rfds)) {
796 int ret = 1;
797 while (ret > 0) ret = childOutput(out[0]);
798 if ((ret == -1 && errno != EAGAIN) || ret == 0)
799 b_out = false;
800 }
801
802 if (b_err && FD_ISSET(err[0], &rfds)) {
803 int ret = 1;
804 while (ret > 0) ret = childError(err[0]);
805 if ((ret == -1 && errno != EAGAIN) || ret == 0)
806 b_err = false;
807 }
808 }
809 }
810
811 if (b_in) {
812 communication = (Communication) (communication & ~Stdin);
813 close(in[1]);
814 }
815 if (b_out) {
816 communication = (Communication) (communication & ~Stdout);
817 close(out[0]);
818 }
819 if (b_err) {
820 communication = (Communication) (communication & ~Stderr);
821 close(err[0]);
822 }
823 }
824}
825
826void KProcess::setUseShell(bool useShell, const char *shell)
827{
828 if (!d)
829 d = new KProcessPrivate;
830 d->useShell = useShell;
831 d->shell = shell;
832 if (d->shell.isEmpty())
833 d->shell = searchShell();
834}
835
836QString KProcess::quote(const QString &arg)
837{
838 QString res = arg;
839 res.replace(QRegExp(QString::fromLatin1("\'")),
840 QString::fromLatin1("'\"'\"'"));
841 res.prepend('\'');
842 res.append('\'');
843 return res;
844}
845
846QCString KProcess::searchShell()
847{
848 QCString tmpShell = QCString(getenv("SHELL")).stripWhiteSpace();
849 if (!isExecutable(tmpShell))
850 {
851 tmpShell = "/bin/sh";
852 }
853
854 return tmpShell;
855}
856
857bool KProcess::isExecutable(const QCString &filename)
858{
859 struct stat fileinfo;
860
861 if (filename.isEmpty()) return false;
862
863 // CC: we've got a valid filename, now let's see whether we can execute that file
864
865 if (-1 == stat(filename.data(), &fileinfo)) return false;
866 // CC: return false if the file does not exist
867
868 // CC: anyway, we cannot execute directories, block/character devices, fifos or sockets
869 if ( (S_ISDIR(fileinfo.st_mode)) ||
870 (S_ISCHR(fileinfo.st_mode)) ||
871 (S_ISBLK(fileinfo.st_mode)) ||
872#ifdef S_ISSOCK
873 // CC: SYSVR4 systems don't have that macro
874 (S_ISSOCK(fileinfo.st_mode)) ||
875#endif
876 (S_ISFIFO(fileinfo.st_mode)) ||
877 (S_ISDIR(fileinfo.st_mode)) ) {
878 return false;
879 }
880
881 // CC: now check for permission to execute the file
882 if (access(filename.data(), X_OK) != 0) return false;
883
884 // CC: we've passed all the tests...
885 return true;
886}
887
888void KProcess::virtual_hook( int, void* )
889{ /*BASE::virtual_hook( id, data );*/ }
890
891
892///////////////////////////
893// CC: Class KShellProcess
894///////////////////////////
895
896KShellProcess::KShellProcess(const char *shellname):
897 KProcess()
898{
899 setUseShell(true, shellname);
900}
901
902
903KShellProcess::~KShellProcess() {
904}
905
906QString KShellProcess::quote(const QString &arg)
907{
908 return KProcess::quote(arg);
909}
910
911bool KShellProcess::start(RunMode runmode, Communication comm)
912{
913 return KProcess::start(runmode, comm);
914}
915
916void KShellProcess::virtual_hook( int id, void* data )
917{ KProcess::virtual_hook( id, data ); }
918
919//#include "kprocess.moc"