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