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