伪终端问题(Mac / Linux):SIGTTOU&不合适的ioctl

时间:2013-03-20 16:50:58

标签: c linux macos

我正在研究一个伪终端库。代码以C代码实现,代码由基于Web的终端使用。只要我不使用sudo或登录,代码就可以工作。

这是我在Mac上运行服务器时遇到的错误:

sh-3.2$ sudo ls
Password:
[1]+  Stopped(SIGTTOU)
sh-3.2$

以上适用于Linux:

$ sudo ls
  readme.txt

但是,我在Linux上使用sudo bash获得以下内容:

$ sudo bash
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
]0;root@ubuntu: /tmproot@ubuntu:/tmp#

注意:上述方法有效,但我没有工作控制权。

我可能忘了在终端上设置一些控制位,但谷歌在找到它时并没有太大帮助。 另外,您是否知道任何能够详细解释Pseudo终端管理的好书。

我有setsid调用,但我没有使用openpty。打开pty时我使用以下代码:

static int createPty(lua_State* L, char* ttyName, int* pty)
{
   *pty = getpt();
   if (*pty < 0 || grantpt(*pty) < 0 || unlockpt(*pty) < 0)
      return lDoErr(L,"Cannot open PTY: %s",strerror(errno));
   if(ptsname_r(*pty, ttyName, PTY_NAME_SIZE-1))
      return lDoErr(L,"ptsname_r: %s",strerror(errno));
   return 0;
}

我已编辑下面的代码,此代码有效。我的第一个版本不起作用的原因是我试图创建两个PTY频道。我希望能够区分stdout和stderr,但Linux内核不允许多个TIOCSCTTY调用。

static int
childOpenTTY(const char* ttyName)
{
   struct termios termbuf;
   int fd=open(ttyName, O_RDWR);
   if(fd < 0)
      doClientError("open %s: %s",ttyName, strerror(errno));
   tcsetpgrp(fd, getpid());
   ioctl(fd,TIOCSCTTY,NULL);
   tcgetattr(fd, &termbuf);
   cfmakeraw(&termbuf); /* turn off NL to CR/NL mapping on output. */
   tcsetattr(fd, TCSANOW, &termbuf);
   return fd;
}

if( (ret = createPty(L, ttyName, &te->pty)) != 0)
   return ret;
if ((te->pid = zzbafork()) < 0)
   return lDoErr(L,"fork: %s",strerror(errno));
if(te->pid == 0)
{  /* Child process */
   static const char efmt[]={"Cannot set '%s' (dup2 err)"};
   int fd;
   if(setsid() < 0) /* make new process group */
      doClientError("setsid: %s",strerror(errno));
   fd=childOpenTTY(ttyName);
   if(dup2(fd, STDIN_FILENO) != STDIN_FILENO)
      doClientError(efmt,"stdin");
   if(dup2(fd, STDOUT_FILENO) != STDOUT_FILENO)
      doClientError(efmt,"stdout");
   if(dup2(fd, STDERR_FILENO) != STDERR_FILENO)
      doClientError(efmt,"stderr");
   if(fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
      close(fd);
   execve(cmd, (char**)cmdArgv, environ);
   /* execve should not return, unless error exec cmd */
   doClientError("Executing %s failed: %s",cmd,strerror(errno));
}

1 个答案:

答案 0 :(得分:0)

很难确定,因为这里没有显示实际代码,但我怀疑你正在进入POSIX风格的“会话”管理。您需要执行setsid调用,然后打开pty(从属端),使其成为控制终端。 openptylogin_tty例程为您执行低级垃圾处理工作;你在用那些吗?