Interactive shell stop pipe - c

My program is as follow:
//... init fd[2] as pipe ...
if (child==0){
close(fd[1]);
dup2(fd[0], 0);
execlp("/bin/sh","sh",NULL);
} else {
close(fd[0]);
char *line; int nbytes=100; int bytes=0;
line=(char*) malloc(nbytes+1);
while ( (bytes = getline((char **)&line,&nbytes,stdin))!= -1 ){
write(fd[1],line, bytes);
}
}
This run OK, however when I try to replace exec("/bin/sh","sh",NULL) with exec("/bin/sh","sh","-i",NULL) to force an interactive shell, my program stop after executing the first command.
I'm new to pipe so please help me understand the reason and make interactive shell work... I also feel that my code to read line and pass to the child pipe is a bit odd.. is there any better way to achieve the same behavior ?

You should close(fd[0]); after the dup2() in the child. If you supply an absolute or relative path like "/bin/sh", there's no point in using execlp(); it will only do a PATH-based search for a bare filename (program name). The cast in the call to getline() should be unnecessary; avoid such casts whenever possible. You should include at least exit(1); after execlp() just in case it fails; a diagnostic message would be a good idea too. You should close(fd[1]); after the loop in the parent to indicate EOF to the child. (Just for once, it doesn't matter if you don't detect the error return from malloc(); it is legitimate to pass the address of a pointer where the pointer holds NULL to the getline() function, and it will then try to allocate memory itself. Of course, if the main program fails to allocate memory, it is highly likely that getline() will also fail to allocate memory.)
Those changes lead to:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int fd[2];
pid_t child;
if (pipe(fd) != 0)
perror("pipe");
else if ((child = fork()) < 0)
perror("fork");
else if (child == 0)
{
close(fd[1]);
dup2(fd[0], 0);
close(fd[0]);
execl("/bin/sh", "sh", NULL);
perror("oops");
exit(1);
}
else
{
close(fd[0]);
size_t nbytes = 100;
int bytes = 0;
char *line = (char*)malloc(nbytes+1);
while ((bytes = getline(&line, &nbytes, stdin)) != -1)
{
write(fd[1], line, bytes);
}
close(fd[1]);
}
return(0);
}
This compiles without complaint under stringent compilation flags:
gcc -O3 -g -std=c99 -Wall -Wextra xf.c -o xf
When run (on Mac OS X 10.7.3) with the code above (invoking sh without the -i option), things behave reasonably sanely. You can type commands and the shell executes them. You can type 'exit' and the shell exits, but the program you wrote (which I called xf) doesn't exit until I type a new command. It then exits because of a SIGPIPE signal as it writes to the now readerless pipe. There is no prompt from this shell because its standard input is not a terminal (it is a pipe).
When the sub-shell is run with the -i option, then there seems to be a fight between the job control shells about which shell is in charge of the terminal. When I run it, I get:
$ ps -f
UID PID PPID C STIME TTY TIME CMD
503 381 372 0 Wed08PM ttys001 0:00.07 -sh
503 21908 381 0 9:32PM ttys001 0:00.01 sh
$ ./xf
sh-3.2$
[1]+ Stopped(SIGTTIN) ./xf
$
$ ps -f
UID PID PPID C STIME TTY TIME CMD
503 381 372 0 Wed08PM ttys001 0:00.07 -sh
503 21908 381 0 9:32PM ttys001 0:00.01 sh
503 22000 21908 0 9:36PM ttys001 0:00.00 ./xf
503 22001 22000 0 9:36PM ttys001 0:00.00 sh -i
$ ls
awk.data osfile-keep.c pthread-2.c send.c xf
const-stuff.c perl.data pthread-3.c so.8854855.sql xf.c
fifocircle.c piped-merge-sort.c quine.c strandsort.c xf.dSYM
madump.c powa.c recv.c unwrap.c xxx.sql
makefile pthread-1.c regress.c vap.c yyy.sql
$ jobs
[1]+ Stopped(SIGTTIN) ./xf
$ fg %1
./xf
exit
$
(The initial -sh is the login shell for my terminal window. In that, I've run sh to get a sub-shell, and I've set the prompt PS1='$ ' to make the prompt distinctive.)
AFAICT, the sh-3.2$ prompt comes from the sh -i shell. The parent shell seems to be reading input, and has dumped the xf program into the background, which is not very civilized of it. The ps -f output doesn't show the ps command, which is a nuisance. I did manage to get the ls command to show up in a ps listing in one run, and it was the child of the original shell, not the sh -i run by xf. When I bring xf into the foreground, it immediately exits (presumably it reads 0 bytes from standard input, which indicates EOF, and so getline() returns -1, and everything shuts up shop. The exit is from the sh -i; it echoes it. It never got any input because the sh shell took command instead of letting xf have control of the terminal. That's pretty excruciatingly messy. I'm not sure why it happens like that, but it feels to me like it shouldn't happen like that.

Related

How to get fully interactive bash terminal in C with shift + ctrl keys?

I want to know how to create a fully interactive terminal. I am creating an interactive bash terminal like this:
fds = open(ptsname(fdm), O_RDWR);
if (fork())
{
....
}
else
{
...
ioctl(0, TIOCSCTTY, 1);
// Execution of the program
{
char *child_av[] = {"/bin/sh", "-i", NULL};
rc = execvp(child_av[0], child_av);
}
// if Error...
return -1;
}
I can then go on to use read()/write() to send commands and receive output. My problem is how do I automate CTRL & SHIFT keys? Python's subprocess does this so it's definitely possible.
How to send ctrl-c to kill foreground process? In python it would be the follow and I want to know what it looks like in C:
process = subprocess.Popen(..) ...
process.send_signal(signal.SIGINT)
How to send shift, ctrl, esc keys? For example, how to open nano, then send ctrl-a-esc? In python it would be the following & I want to know what it looks like in C:
from subprocess import Popen, PIPE
shift_a_sequence = '''keydown Shift_L
key A
keyup Shift_L
'''
def keypress(sequence):
p = Popen(['xte'], stdin=PIPE)
p.communicate(input=sequence)`
There are four separate issues here:
1. How to trigger a Ctrl-C on the PTY
The sigint is triggered by the terminal's line discipline upon receiving Ctrl-C as input (in cooked mode). You can do this by sending 0x03, which is equivalent to uppercase ASCII 'C' with the 7th bit cleared:
write(fdm, "\x03", 1);
2. The C equivalent of process.send_signal
process = subprocess.Popen(..) ...
process.send_signal(signal.SIGINT)
This simply does a fork+kill, so it's unrelated to anything you'd do to accomplish #1. You can see this in strace:
$ cat foo.py
import signal
import subprocess
process = subprocess.Popen(["sleep", "10"])
process.send_signal(signal.SIGINT)
$ strace -f -eclone,kill,execve python foo.py
execve("/usr/bin/python", ["python", "foo.py"], 0x7ffe4d179458 /* 30 vars */) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fa0f6e14a10) = 12917
strace: Process 12917 attached
[pid 12917] execve("/usr/bin/sleep", ["sleep", "10"], 0x7ffe6a45efc0 /* 30 vars */) = -1 ENOENT (No such file or directory)
[pid 12917] execve("/bin/sleep", ["sleep", "10"], 0x7ffe6a45efc0 /* 30 vars */ <unfinished ...>
[pid 12916] kill(12917, SIGINT) = 0
[pid 12917] <... execve resumed> ) = 0
[pid 12917] --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=12916, si_uid=1000} ---
[pid 12917] +++ killed by SIGINT +++
3. How to send Shift, Ctrl and Esc
Esc is simple: you just send its ASCII value. Here's man ascii:
033 27 1B ESC (escape)
Shift and Control are modifiers, so you don't actually send them. You just modify the character you send. #1 already covered how to do this for Ctrl on simple ascii characters.
For shift, you should simply uppercase the character you're interested in using appropriate string functions. The traditional hardware logic of clearing/setting bit 6 doesn't work well for unicode characters, but 'c' == 'C'+0x20 if you feel like it.
Note that this does not apply to things like arrow keys, where you need to send a different ANSI escape code corresponding to the terminal you're trying to emulate.
For completeness, Alt/Meta has two forms: Traditionally setting bit 8 (mostly deprecated for Unicode reasons), or Meta-As-Escape where you simply send the two bytes ESC x for Alt+x.
4. The C equivalent of the piping to xte
from subprocess import Popen, PIPE
shift_a_sequence = '''keydown Shift_L
key A
keyup Shift_L
'''
def keypress(sequence):
p = Popen(['xte'], stdin=PIPE)
p.communicate(input=sequence)`
This opens an X11 utility that simulates an X11 key sequence. If you are running the program in an XTerm and don't switch focus, this will hopefully end up in the terminal where you started, but this is not at all a guarantee. It is definitely nothing like #3.
If you wanted to do this though, you can use popen from C in much the same way, but it won't help you do anything of what you describe textually.
You can find a complete example for sending Ctrl+C adapted from your code here:
#define _XOPEN_SOURCE 600
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#define __USE_BSD
#include <termios.h>
#define guard(x) do { if((x)<0) { perror(#x); exit(1); } } while(0);
int main(void)
{
int fdm, fds, rc;
char input[150];
fdm = posix_openpt(O_RDWR);
guard(grantpt(fdm));
guard(unlockpt(fdm));
if (fork())
{
char* output = "sleep 60\n";
// Start a long sleep
write(fdm, output, strlen(output));
// Wait and send Ctrl-C to abort it after 1 second
sleep(1); write(fdm, "\x03", 1);
// Make sure shell is still alive
output = "echo 'Shell is still alive'\n";
write(fdm, output, strlen(output));
// Wait and send Ctrl-D to exit
sleep(1); write(fdm, "\x04", 1);
while((rc = read(fdm, input, sizeof(input)-1)) > 0) {
input[rc] = 0;
printf("From PTY:\n%s\n", input);
}
close(fdm);
wait(NULL);
}
else
{
setsid();
guard(fds = open(ptsname(fdm), O_RDWR));
close(fdm);
dup2(fds, 0);
dup2(fds, 1);
dup2(fds, 2);
close(fds);
execlp("sh", "sh", "-i", NULL);
}
return 0;
} // main
Execution shows that sleep is interrupted, the shell continues, and is finally exited:
$ ./foo
From PTY:
sleep 60
$ ^C
echo 'Shell is still alive'
$ Shell is still alive
$
You seem to have most of the framework needed:
create a pseuso-terminal
fork
in the child, dup2 the slave pty to stdin/stdout/stderr
exec the shell
in the parent, interact with the shell via the master pty
For more detail on where you are going wrong, you'd need to provide a Minimal, Complete, and Verifiable example of what you are actually trying to do, not just a vague description.

How to get number of processes and threads in a C program?

I would like to get the number of ALL running processes and ALL running threads of my Linux system. I need this information IN a C application. From the terminal, I can get his info with
$ ps -A --no-headers | wc -l
for processes and
$ ps -AL --no-headers | wc -l
for processes including tasks.
I didn't find this info in /proc.
Any suggestions?
The ps examples in your question don't really give you the information you're asking about: the first lists all the processes on your system, not just those spawned by a particular program, and similarly the second lists the number of threads in every process.
If you want to find information about threads spawned by a particular process, you can look in /proc under /proc/<pid>/task. For example, here is a process with a single thread:
bash-4.4$ ls /proc/15355/task/
15355
And here's one that has three threads (in addition to the main thread):
bash-4.4$ ls /proc/15295/task/
15295 15296 15297 15298
The corresponding ps -L output for that process looks like:
bash-4.4$ ps -L -p 15295
PID LWP TTY TIME CMD
15295 15295 pts/4 00:00:00 python
15295 15296 pts/4 00:00:00 python
15295 15297 pts/4 00:00:00 python
15295 15298 pts/4 00:00:00 python
Getting the number of running processes from /proc takes a little more work, since Linux only maintains information about a process' parent, rather than it's children. That means you would need to scan through /proc and find every process for which the parent is your target process...and then repeat recursively for each of those processes.
You can use something like pstree to get this information, of course, but that output isn't really designed to be machine parseable.
Every running process has a corresponding directory /proc/<pid>. You can use that to count the number of running processes (by counting all sub-directories of /proc that are numeric).
Inside each of these directories you can check /proc/<pid>/status to get information about the process. Specifically, the line Threads: <cnt> gives you the number of threads for that process.
Refer to man proc for more info on the /proc (pseudo) file system.
The simplest is to parse the output of commands using popen.
The following:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void parse_output(char *buf, size_t bufsize, const char cmd[])
{
assert(buf != NULL);
assert(cmd != NULL);
FILE *fp;
// add dynamic allocation here
memset(buf, 0, bufsize);
if ((fp = popen(cmd, "r")) == NULL) {
printf("Error opening pipe!\n");
exit(-__LINE__);
}
// worst speed ever. And strlen is called twice...
while (fgets(&buf[strlen(buf)], bufsize - strlen(buf), fp) != NULL);
if(pclose(fp)) {
printf("Command not found or exited with error status\n");
exit(-__LINE__);
}
}
int main() {
char buf[256];
long num;
parse_output(buf, sizeof(buf), "ps -A --no-headers | wc -l");
if (sscanf(buf, "%ld", &num) != 1) {
exit(-__LINE__);
}
printf("Number of processes: %ld\n", num);
parse_output(buf, sizeof(buf), "ps -AL --no-headers | wc -l");
if (sscanf(buf, "%ld", &num) != 1) {
exit(-__LINE__);
}
printf("Number of processes including tasks: %ld\n", num);
}
will output on my system:
$ gcc 1.c && ./a.out
Number of processes: 241
Number of processes includeing tasks: 867

Two children of same parent are not communicating using pipe if parent do not call wait()

Please see the code below:
#include<stdio.h>
main(){
int pid, fds[2], pid1;
char buf[200];
pipe(fds);
pid = fork();
if(pid==0)
{
close(fds[0]);
scanf("%s", &buf);
write(fds[1], buf, sizeof(buf)+1);
}
else
{
pid1 = fork();
if(pid1==0)
{
close(fds[1]);
read(fds[0], buf, sizeof(buf)+1);
printf("%s\n", buf);
}
else
{
Line1: wait();
}
}
}
If I do not comment out Line1, it is working fine. Please see below:
hduser#pc4:~/codes/c/os$ ./a.out
hello //*Entry from keyboard*
hello //Output
hduser#pc4:~/codes/c/os$
But if I comment out Line1, two child processes are not communicating:
hduser#pc4:~/codes/c/os$ ./a.out
hduser#pc4:~/codes/c/os$
hi //*Entry from keyboard*
hi: command not found
hduser#pc4:~/codes/c/os$
I cannot understand significance of wait() here.
What's happening here is that the parent process completes execution before the child processes finish. Causing the children to lose access to the terminal.
Let us have a closer look at all this.
What does wait() do ?
The wait() system call suspends execution of the calling process until
one of its children terminates.
Your program is like this
Your main Process forks 2 child processes. The first one writes to a pipe while the other one reads from a pipe. All this happens while the main process continues to execute.
What happens when the main process has executed it's code ? It terminates. When it terminates, it gives up its control on the terminal. Which causes the children to lose access to the terminal.
This explains why you get command not found -- what you have typed is not on the stdin of your program but on the shell prompt itself.
There were a couple of other issues with your code too,
1) In this part of your code,
scanf("%s", &buf);
This is wrong. You were unlucky and didn't get a segmentation fault. Since buf is already an address, this should have been
scanf("%s", buf);
2) Notice this,
read(fds[0], buf, sizeof(buf)+1);
This is undefined behavior as was pointed out in the comments section. You are trying to read more data and store it in a lesser memory space. This
should have been,
read(fds[0], buf, sizeof(buf));
3) Calling wait(). You have created two child processes, you should wait for both of them to finish, so you should call wait() twice.
After fixing some infelicities in the code, I came up with a semi-instrumented version of your program like this:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
int pid, fds[2], pid1;
char buf[200];
pipe(fds);
pid = fork();
if (pid == 0)
{
close(fds[0]);
printf("Prompt: "); fflush(0);
if (scanf("%199s", buf) != 1)
fprintf(stderr, "scanf() failed\n");
else
write(fds[1], buf, strlen(buf) + 1);
}
else
{
pid1 = fork();
if (pid1 == 0)
{
close(fds[1]);
if (read(fds[0], buf, sizeof(buf)) > 0)
printf("%s\n", buf);
else
fprintf(stderr, "read() failed\n");
}
else
{
/*Line1: wait();*/
}
}
return 0;
}
That compiles cleanly under stringent options (GCC 5.1.0 on Mac OS X 10.10.5):
gcc -O3 -g -std=c11 -Wall -Wextra -Werror p11.c -o p11
When I run it, the output is:
$ ./p11
Prompt: scanf() failed
read() failed
$
The problem is clear; the scanf() fails. At issue: why?
The wait() version needs an extra header #include <sys/wait.h> and the correct calling sequence. I used the paragraph:
else
{
printf("Kids are %d and %d\n", pid, pid1);
int status;
int corpse = wait(&status);
printf("Parent gets PID %d status 0x%.4X\n", corpse, status);
}
When compiled and run, the output is now:
$ ./p11
Kids are 20461 and 20462
Prompt: Albatross
Albatross
Parent gets PID 20461 status 0x0000
$
So, the question becomes: how or why is the standard input of the child process closed when the parent doesn't wait? It is Bash doing some job control that wreaks havoc.
I upgraded the program once more, using int main(int argc, char **argv) and testing whether the command was passed any arguments:
else if (argc > 1 && argv != 0) // Avoid compilation warning for unused argv
{
printf("Kids are %d and %d\n", pid, pid1);
int status;
int corpse = wait(&status);
printf("Parent gets PID %d status 0x%.4X\n", corpse, status);
}
I've got an Heirloom Shell, which is close to an original Bourne shell. I ran the program under that, and it behaved as I would expect:
$ ./p11
Prompt: $ Albatross
Albatross
$ ./p11 1
Kids are 20483 and 20484
Prompt: Albatross
Albatross
Parent gets PID 20483 status 0x0000
$
Note the $ after the Prompt: in the first run; that's the shell prompt, but when I type Albatross, it is (fortunately) read by the child of the p11 process. That's not guaranteed; it could have been the shell that read the input. In the second run, we get to see the parent's output, then the children at work, then the parents exiting message.
So, under classic shells, your code would work as expected. Bash is somehow interfering with the normal operation of child processes. Korn shell behaves like Bash. So does C shell (tcsh). Attempting dash, I got interesting behaviour (3 runs):
$ ./p11
Prompt: $ Albatross
scanf() failed
read() failed
dash: 2: Albatross: not found
$ ./p11
Prompt: $ Albatross
scanf() failed
dash: 4: Albatross: not found
$ read() failed
$ ./p11
Prompt: scanf() failed
$ read() failed
$
Note that the first two runs shows dash reading the input, but the children did not detect problems until after I hit return after typing Albatross. The last time, the children detected problems before I typed anything.
And, back with Bash, redirecting standard input works 'sanely':
$ ./p11 <<< Albatross
Prompt: Albatross
$ ./p11 1 <<< Albatross
Kids are 20555 and 20556
Prompt: Albatross
Parent gets PID 20555 status 0x0000
$
The output Albatross comes from the second child, of course.
The answer is going to be lurking somewhere in behaviour of job control shells, but it's enough to make me want to go back to life before that.

How to kill zombie processes?

I have two problems: a program I made am glitching out and making nearly unkillable processes, if either subproblem is solved I believe both problems will be easily resolved. I am running an early 2008 Macbook on OSX 10.6.8.
/Problem #1, coding:/
I've been playing around with the iRobot Create using termios.h I/O. I compile without warnings or errors and can run the program without a hitch. I am connecting to the robot by usb which explains the input "/dev/tty.usbserial".
gcc simple.c -o simple
./simple /dev/tty.usbserial
The program starts by checking the arguments given, then tries to connect with the given argument (char * device is /dev/tty.usbserial) with the biscConnect() function. It fails on my mac.
//in file simple.c:
int main(int argc, char *argv[]){
if(argc!=2){
printf("Put port... like /dev/tty.usbserial or something\n");
exit(EXIT_FAILURE);
}
printf("Starting...\n");
biscConnect(argv[1]);
}
void biscConnect(char *device){
struct termios tty;
// Try to open the device from the input
if((fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK))==-1){
fprintf(stderr, "Serial port at %s could not be opened: %s\n", device, strerror(errno));
exit(EXIT_FAILURE);
}
tcflush (fd, TCIOFLUSH);
tcgetattr(fd, &tty);
tty.c_iflag = IGNBRK | IGNPAR;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cflag = CREAD | CS8 | CLOCAL;
cfsetispeed(&tty, B57600);
cfsetospeed(&tty, B57600);
tcsetattr(fd, TCSANOW, &tty);
//The code fails prior to this point
}
I would then send bytes to the robot to make it move if it didn't get stuck before then.
/Problem #2, unkillable processes:/
When I run the file, the terminal goes into a weird mode where the prompt is gone and I can type anything I want (usually signifying a process is running). I cannot exit using control-c. The only way I can seem to exit is closing the terminal window. This fails to kill the running process.
I can easily look up the pid but the Activity Monitor but the Force Quit fails to kill the process, kill -9 [pid] fails, killall [program name] fails etc. despite acknowledging the existence of the program. The only way to force terminate the process seems to be to physically close off the power from the computer and reboot it (ie shuting down doesn't work because it tries, and fails, to terminate the process(es)). I am wasting a terrible amount of time if to debug the program I need to power-cycle my laptop every run! I can continually create more process but am unable to delete them.
I think if I knew the parent process I might be able to kill these "zombie" processes but I don't know what the parent is.
Any ideas on how to get rid of these processes without power-cycling would be tremendous help, thanks!
On Mac OS X 10.8.4, I created a program zombie from zombie.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
if ((pid = fork()) >= 0)
{
if (pid == 0)
{
printf("%d: committing suicide (parent %d)\n", (int)getpid(), (int)getppid());
exit(0);
}
else
{
printf("%d: going to sleep for a while - child %d might die while I snooze\n",
(int)getpid(), (int)pid);
sleep(30);
printf("%d: awake\n", (int)getpid());
}
}
return 0;
}
When I ran it in one terminal window, the output was:
$ ./zombie
2443: going to sleep for a while - child 2444 might die while I snooze
2444: committing suicide (parent 2443)
2443: awake
$
In another terminal window, running ps -f produced:
UID PID PPID C STIME TTY TIME CMD
503 260 249 0 12:42PM ttys004 0:00.08 -bash
503 2443 260 0 5:11PM ttys004 0:00.00 ./zombie
503 2444 2443 0 5:11PM ttys004 0:00.00 (zombie)
The parenthesize (zombie) is the defunct process, and the name in parentheses is the original name of the process. When I copied the program to living-dead, the corresponding output was:
UID PID PPID C STIME TTY TIME CMD
503 260 249 0 12:42PM ttys004 0:00.09 -bash
503 2454 260 0 5:13PM ttys004 0:00.00 ./living-dead
503 2455 2454 0 5:13PM ttys004 0:00.00 (living-dead)
(On most systems, the defunct process is marked as <defunct> or something similar.)
Clearly, the value in the PPID column identifies the parent process of the zombie, and the various process IDs match the output from the programs themselves.
You can find out who birthed that undead process by running ps ef or using htop.
PID TTY STAT TIME COMMAND
21138 tty1 T 0:00 sudo /usr/bin/aura -Akax open_watcom openwatcom-extras-hg HOME=/home/hav3lock USER=hav3lock SHELL=/bin/zsh TERM=linux PATH=/usr/local/sbin:/u
21139 tty1 T 0:00 \_ /usr/bin/aura -Akax open_watcom openwatcom-extras-hg TERM=linux PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/
22111 tty1 Z 0:00 \_ [su] <defunct>`
In the above I have a zombie process who's momma is /usr/bin/aura, a process spawned by sudo /usr/bin/aura.
Running sudo kill -9 21138 will kill the parent, thereby subsequently killing its zombie spawn.
PID TTY STAT TIME COMMAND
23858 pts/9 S+ 0:00 sudo sudo ps ef LANG=en_US.utf8 DISPLAY=:0 SHLVL=3 LOGNAME=hav3lock XDG_VTNR=1 PWD=/home/hav3lock/sy.l/repos/pub_rel/slen/linux/src HG=/usr/b
23860 pts/9 S+ 0:00 \_ sudo ps ef LANG=en_US.utf8 DISPLAY=:0 LS_COLORS=no=0:fi=0:di=34:ln=00;96:or=91;4:mi=00;31;9:mh=01;37:pi=33;7:so=01;35:do=35:bd=01;33:cd=9
23861 pts/9 R+ 0:00 \_ ps ef LANG=en_US.utf8 DISPLAY=:0 LS_COLORS=no=0:fi=0:di=34:ln=00;96:or=91;4:mi=00;31;9:mh=01;37:pi=33;7:so=01;35:do=35:bd=01;33:cd=93
13405 tty2 S<sl+ 3:49 X :0 HOME=/home/hav3lock USER=hav3lock SHELL=/bin/zsh TERM=linux PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/ve`

How do I terminate backgrounded ssh instances

I have written C program that uses fork(2) and execl(3) to run ssh for port forwarding purposes.
The ssh's are run in the background the -f option.
When the C program exits, I want it to send SIGTERM to the ssh instances it spawned.
I have tried
// creating the ssh process
ssh_pid = fork();
if (ssh_pid == 0)
execl("/usr/bin/ssh", "/usr/bin/ssh", "-f", other options, NULL)
// ...
printf("Killing %d\n", ssh_pid); // <--- output the PID
kill(ssh_pid, 15);
sleep(1); // give it a chance to exit properly
if (kill(ssh_pid, 0) == 0)
kill(ssh_pid, 9); // the "shotgun" approach
However, this doesn't work (even with the SIGKILL).
If I run ps before the program exits
ps aux | grep ssh | grep -v sshd | grep -v grep
I see something like this:
user 27825 0.2 0.0 0 0 pts/0 Z+ 18:23 0:00 [ssh] <defunct>
user 27834 0.0 0.0 41452 1176 ? Ss 18:23 0:00 /usr/bin/ssh -f [other options]
When the program prints the PID it is killing, I see this:
Killing 27825
Subsequently repeating the ps gives me:
user 27834 0.0 0.0 41452 1176 ? Ss 18:23 0:00 /usr/bin/ssh -f [other options]
It seems that the original ssh has forked itself in order to become a background process.
So I changed my call to kill(2) to attempt to kill all processes spawned by the original ssh:
kill(-ssh_pid, 15);
But this appears to have no effect. I suspect it is because the original ssh is no longer the parent of the backgrounded ssh.
So, how do I safely kill the backgrounded ssh? Is it even possible?
The solution I have just found is not to use the -f option at all, and background the ssh myself.
ssh_pid = fork();
if (ssh_pid == 0)
{
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
execl("/usr/bin/ssh", "/usr/bin/ssh", other options, NULL);
}
Because when ssh gets the -f option it creates a child, and signals sent to the parent won't get passed to the child.

Resources