views:

879

answers:

6

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.

A: 

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.

tommieb75
He can't directly signal the child because it is running under a different effective uid.
caf
@caf: hmmm....can you answer this? imagine if the child process is running on a pseudo terminal, take this under a linux example... if a binary file was to be cat'ted to the pseudo terminal say /dev/pty4 where the child process is at, would that cause a terminal hangup? i.e. cat binary_file > /dev/pty4
tommieb75
No, why would it? It will just deliver those bytes to the master side of the pseudo terminal pair.
caf
@caf: Ok. I thought it would cause the terminal's shell process to hang and henceforth forced to log off...that's what I was thinking...
tommieb75
A: 

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

caf
A: 

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]);
Geoff Reedy
I get a "permission denied"/"operation not permitted" error. If I run the process as root then I get "operation timed out".
Hongli
A: 

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.

Arthur Shipkowski
+1  A: 

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.

Sean Reifschneider
+2  A: 

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.

Hongli