如何向子进程发送 Ctrl-C 控制字符或终端挂断消息?

发布于 2024-08-20 00:52:47 字数 280 浏览 7 评论 0 原文

我有一个在伪终端中运行的子进程。父进程不以 root 身份运行,但子进程通过 su 或 sudo 运行。因此,无法向子进程发送信号以强制其退出。我想通过以下方式之一强制它退出:

  • 模拟 Ctrl-C。
  • 模拟终端挂起。

我该怎么做?我已经有一个 pty master fd,并且我尝试过类似的操作:

write(master, &termios.c_cc[VINTR], 1)

但它没有做任何事情。

I have a child process which runs in a pseudo terminal. The parent process does not run as root, but the child process does, through su or sudo. Because of this it is not possible to send a signal to the child process to force it to exit. I want to force it to exit by one of these means:

  • emulating a Ctrl-C.
  • emulating a terminal hangup.

How do I do either of these? I already have a pty master fd, and I've tried something like this:

write(master, &termios.c_cc[VINTR], 1)

but it doesn't do anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

可是我不能没有你 2024-08-27 00:52:47

在我看来,如果你真的有一个pty(除非你用伪终端指的是其他东西),那么你所要做的就是向该FD发送一个Control-C。作为这一点的证据,我在 Python 中提交了以下代码(但与执行此操作所需的 C 相当接近):

import pty, os, sys, time

pid, fd = pty.fork()
if pid == 0:
   os.execv('/bin/sh', ['/bin/sh', '-c',
         'while true; do date; sleep 1; done'])
   sys.exit(0)
time.sleep(3)
os.write(fd, '^C')
print 'results:', os.read(fd, 1024)

这在 pty 下分叉了一个进程,该进程运行一个无限循环,打印
日期。然后父进程等待 3 秒并发送一个 control-C。

这会产生以下输出:

guin:/tmp$ time python /tmp/foo
results: Fri Feb  5 08:28:09 MST 2010
Fri Feb  5 08:28:10 MST 2010
Fri Feb  5 08:28:11 MST 2010

python /tmp/foo  0.02s user 0.01s system 1% cpu 3.042 total
guin:/tmp$

它运行了 3 秒多一点,打印了 3 次日期,然后退出。

It seems to me that if you truly have a pty (unless you mean something else by pseudo terminal), that all you have to do is send a Control-C to that FD. As evidence of this, I submit the following code in Python (but fairly close to the C required to do it):

import pty, os, sys, time

pid, fd = pty.fork()
if pid == 0:
   os.execv('/bin/sh', ['/bin/sh', '-c',
         'while true; do date; sleep 1; done'])
   sys.exit(0)
time.sleep(3)
os.write(fd, '^C')
print 'results:', os.read(fd, 1024)

This forks a process under a pty, which runs an infinite loop printing the
date. Then the parent waits 3 seconds and sends a control-C.

This results in the following output:

guin:/tmp$ time python /tmp/foo
results: Fri Feb  5 08:28:09 MST 2010
Fri Feb  5 08:28:10 MST 2010
Fri Feb  5 08:28:11 MST 2010

python /tmp/foo  0.02s user 0.01s system 1% cpu 3.042 total
guin:/tmp$

It ran just over 3 seconds, printed out the date 3 times, and exited.

妄司 2024-08-27 00:52:47

我最终采用了以下解决方案:

分叉后,我不是立即执行 sudo,而是 exec() 一个辅助子进程,该子进程又分叉并执行 sudo 并在其上调用 waitpid。因此,进程层次结构如下所示:

original process          <---- runs as user
  |
  +-- helper process      <---- runs as user, session leader,
         |                      has own pty, in pty's foreground process group
         |
         +--- sudo        <---- runs as root

通过终止辅助进程,pty 不再有前台进程。这将导致操作系统向整个前台进程组发送 SIGHUP,而不管用户是谁,因此 sudo 也会发出 SIGHUP 信号。

I eventually went with the following solution:

After forking, instead of exec'ing sudo immediately, I exec() a helper child process instead, which in turn forks and execs sudo and calls waitpid on it. So the process hierarchy looks like this:

original process          <---- runs as user
  |
  +-- helper process      <---- runs as user, session leader,
         |                      has own pty, in pty's foreground process group
         |
         +--- sudo        <---- runs as root

By killing the helper process, the pty does not have a foreground process anymore. This will cause the OS to send SIGHUP to the entire foreground process group, regardless of the user, so sudo is SIGHUP'ed too.

生来就爱笑 2024-08-27 00:52:47

我认为你需要使用ioctl来插入中断字符而不是write。不幸的是,这种机制似乎不可移植。对于 Linux 来说,这看起来可能有效:

ioctl(master, TIOCSTI, &termios.c_cc[VINTR]);

I think you need to use ioctl to insert the interrupt character instead of write. Unfortunately the mechanism for this does not seem to be portable. For linux it looks this might work:

ioctl(master, TIOCSTI, &termios.c_cc[VINTR]);
温柔戏命师 2024-08-27 00:52:47

有两种方法可以实现这一点:

  • 从子进程中捕获 SIGCHLD 信号并处理它,您可以 _exit(0) 来结束子进程
  • 有一个名为 ptree。你可以通过这样做来欺骗这个......在伪代码中:
obtain the parent's pid.
using _popen("ptree %d", parent_pid)
for each entry of child process
system ("kill -1 %d", child_process_pid)

想到了两个......抱歉,如果它对你没有进一步的帮助,

希望这有帮助,
此致,
汤姆.

There is two ways to achieve this:

  • From the child process, trap the SIGCHLD signal and handle it, you could _exit(0) to end the child process
  • There's a program called ptree. You could cheat this by doing it this way...in pseudocode:
obtain the parent's pid.
using _popen("ptree %d", parent_pid)
for each entry of child process
system ("kill -1 %d", child_process_pid)

There the two that comes to mind...sorry if its not of further help to you,

Hope this helps,
Best regards,
Tom.

木槿暧夏七纪年 2024-08-27 00:52:47

关闭主站应该向从站的控制进程组发出挂起信号。

Closing the master should signal a hangup to the controlling process group of the slave.

叹沉浮 2024-08-27 00:52:47

我要检查的第一件事是您是否需要将其设为从属端的控制终端。事实证明,这更复杂 比我记得的要好,默认情况下 ptys 可能不会成为控制对象。该链接适用于 Linux,其他系统应该根据其 SysV 与 BSD 特性选择其中之一,但看起来 TIOCSCTTY 是一个不错的选择。

其次,我会检查您是否在 termios 中设置了 ISIG;如果没有,VINTR 和 VQUIT 将不起作用。

当然,如果另一端正在捕获 SIGINT 和 SIGQUIT,则会遇到其他问题。

The first thing I'd check is if you need to make it the controlling terminal on the slave side. It turns out this is more complex than I remember, with ptys possibly not becoming controlling by default. That link is for Linux, other systems should do one or the other depending on their SysV vs. BSD-ness, but it looks like the TIOCSCTTY is a good bet to try.

Secondly, I'd check if you're setting ISIG in your termios; if not, VINTR and VQUIT won't work.

Of course, if the other end is catching SIGINT and SIGQUIT, you will have other issues.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文