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