Revision 0963d545 lib/utils.py

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