Revision 0963d545
b/lib/utils.py | ||
---|---|---|
158 | 158 |
return cmd_env |
159 | 159 |
|
160 | 160 |
|
161 |
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False): |
|
161 |
def RunCmd(cmd, env=None, output=None, cwd="/", reset_env=False, |
|
162 |
interactive=False): |
|
162 | 163 |
"""Execute a (shell) command. |
163 | 164 |
|
164 | 165 |
The command should not read from its standard input, as it will be |
... | ... | |
177 | 178 |
directory for the command; the default will be / |
178 | 179 |
@type reset_env: boolean |
179 | 180 |
@param reset_env: whether to reset or keep the default os environment |
181 |
@type interactive: boolean |
|
182 |
@param interactive: weather we pipe stdin, stdout and stderr |
|
183 |
(default behaviour) or run the command interactive |
|
180 | 184 |
@rtype: L{RunResult} |
181 | 185 |
@return: RunResult instance |
182 | 186 |
@raise errors.ProgrammerError: if we call this when forks are disabled |
... | ... | |
185 | 189 |
if no_fork: |
186 | 190 |
raise errors.ProgrammerError("utils.RunCmd() called with fork() disabled") |
187 | 191 |
|
192 |
if output and interactive: |
|
193 |
raise errors.ProgrammerError("Parameters 'output' and 'interactive' can" |
|
194 |
" not be provided at the same time") |
|
195 |
|
|
188 | 196 |
if isinstance(cmd, basestring): |
189 | 197 |
strcmd = cmd |
190 | 198 |
shell = True |
... | ... | |
202 | 210 |
|
203 | 211 |
try: |
204 | 212 |
if output is None: |
205 |
out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd) |
|
213 |
out, err, status = _RunCmdPipe(cmd, cmd_env, shell, cwd, interactive)
|
|
206 | 214 |
else: |
207 | 215 |
status = _RunCmdFile(cmd, cmd_env, shell, output, cwd) |
208 | 216 |
out = err = "" |
... | ... | |
418 | 426 |
os._exit(1) # pylint: disable-msg=W0212 |
419 | 427 |
|
420 | 428 |
|
421 |
def _RunCmdPipe(cmd, env, via_shell, cwd): |
|
429 |
def _RunCmdPipe(cmd, env, via_shell, cwd, interactive):
|
|
422 | 430 |
"""Run a command and return its output. |
423 | 431 |
|
424 | 432 |
@type cmd: string or list |
... | ... | |
429 | 437 |
@param via_shell: if we should run via the shell |
430 | 438 |
@type cwd: string |
431 | 439 |
@param cwd: the working directory for the program |
440 |
@type interactive: boolean |
|
441 |
@param interactive: Run command interactive (without piping) |
|
432 | 442 |
@rtype: tuple |
433 | 443 |
@return: (out, err, status) |
434 | 444 |
|
435 | 445 |
""" |
436 | 446 |
poller = select.poll() |
447 |
|
|
448 |
stderr = subprocess.PIPE |
|
449 |
stdout = subprocess.PIPE |
|
450 |
stdin = subprocess.PIPE |
|
451 |
|
|
452 |
if interactive: |
|
453 |
stderr = stdout = stdin = None |
|
454 |
|
|
437 | 455 |
child = subprocess.Popen(cmd, shell=via_shell, |
438 |
stderr=subprocess.PIPE,
|
|
439 |
stdout=subprocess.PIPE,
|
|
440 |
stdin=subprocess.PIPE,
|
|
456 |
stderr=stderr,
|
|
457 |
stdout=stdout,
|
|
458 |
stdin=stdin,
|
|
441 | 459 |
close_fds=True, env=env, |
442 | 460 |
cwd=cwd) |
443 | 461 |
|
444 |
child.stdin.close() |
|
445 |
poller.register(child.stdout, select.POLLIN) |
|
446 |
poller.register(child.stderr, select.POLLIN) |
|
447 | 462 |
out = StringIO() |
448 | 463 |
err = StringIO() |
449 |
fdmap = { |
|
450 |
child.stdout.fileno(): (out, child.stdout), |
|
451 |
child.stderr.fileno(): (err, child.stderr), |
|
452 |
} |
|
453 |
for fd in fdmap: |
|
454 |
SetNonblockFlag(fd, True) |
|
455 |
|
|
456 |
while fdmap: |
|
457 |
pollresult = RetryOnSignal(poller.poll) |
|
458 |
|
|
459 |
for fd, event in pollresult: |
|
460 |
if event & select.POLLIN or event & select.POLLPRI: |
|
461 |
data = fdmap[fd][1].read() |
|
462 |
# no data from read signifies EOF (the same as POLLHUP) |
|
463 |
if not data: |
|
464 |
if not interactive: |
|
465 |
child.stdin.close() |
|
466 |
poller.register(child.stdout, select.POLLIN) |
|
467 |
poller.register(child.stderr, select.POLLIN) |
|
468 |
fdmap = { |
|
469 |
child.stdout.fileno(): (out, child.stdout), |
|
470 |
child.stderr.fileno(): (err, child.stderr), |
|
471 |
} |
|
472 |
for fd in fdmap: |
|
473 |
SetNonblockFlag(fd, True) |
|
474 |
|
|
475 |
while fdmap: |
|
476 |
pollresult = RetryOnSignal(poller.poll) |
|
477 |
|
|
478 |
for fd, event in pollresult: |
|
479 |
if event & select.POLLIN or event & select.POLLPRI: |
|
480 |
data = fdmap[fd][1].read() |
|
481 |
# no data from read signifies EOF (the same as POLLHUP) |
|
482 |
if not data: |
|
483 |
poller.unregister(fd) |
|
484 |
del fdmap[fd] |
|
485 |
continue |
|
486 |
fdmap[fd][0].write(data) |
|
487 |
if (event & select.POLLNVAL or event & select.POLLHUP or |
|
488 |
event & select.POLLERR): |
|
464 | 489 |
poller.unregister(fd) |
465 | 490 |
del fdmap[fd] |
466 |
continue |
|
467 |
fdmap[fd][0].write(data) |
|
468 |
if (event & select.POLLNVAL or event & select.POLLHUP or |
|
469 |
event & select.POLLERR): |
|
470 |
poller.unregister(fd) |
|
471 |
del fdmap[fd] |
|
472 | 491 |
|
473 | 492 |
out = out.getvalue() |
474 | 493 |
err = err.getvalue() |
Also available in: Unified diff