Gracefully Terminating a process when its controlling shells gets killed - c

I have a command line application. The issue I am facing is that sometimes the users of that application close directly the terminal in which our application is running that also using the below command:
kill -9 pid_of_parent_console
I the above case our application should close gracefully by doing all the necessary cleanup. For that I wrote a signal handler for handling SIGHUP signal as when controlling terminal of process exits it sends SIGHUP to processes running under it, which we have handled to exit our process gracefully.But the thing is if user open a terminal suppose that is by default bash and then he again types bash command in it then run our application and if suppose kills that applications parent process that is bash executed manually after opening terminal then our application doesn't gets SIGHUP and does not exit gracefully.For simplicity I have written the below code which reproduces the issue:
#include <stdio.h>
#include <signal.h>
FILE *fp = NULL;
int flag = 1;
void handler(int signum)
{
flag = 0;
}
int main()
{
signal(SIGHUP, handler);
// just for testing
fp = fopen("file", "w");
// loop terminates only when HUP is generated
while (flag);
// if SIGHUP is generated then code should reach here
// and write the below in file.
fprintf(fp, "SIGHUP Generated");
fclose(fp);
return 0;
}
For simplicity I am not using sigaction for handling signals.
What I have observed is that when I open a terminal and press tty command and note the name of stdin file associated with it, and then when in the same terminal if I enter bash command and again if I note down the stdin file associated with it using tty command, what I found out is that both the bash shells, one that opens by default when I launch terminal and one I manually opened by typing bash command in bash console share the same stdin file name.
So, due to which when I kill that second bash which is parent of my process the stdin file associated with it doesn't gets closed and I think thats why I am not receiving SIGHUP signal.
Is there any other way I can use to kill my process too gracefully when its controlling console gets killed.
Terminal emulator used: GNOME Terminal 2.31.3
Default shell: bash

Regardless of the OS, SIGKILL (i.e. signal -9) is directly handled by the kernel and the process is killed before any userland handler had a chance to process anything. This is true for the Linux kernel just like the Solaris one.
There is then no way for the target process to protect itself against that signal (outside intercepting it on the sender side or in the kernel).
On the other hand, closing a terminal emulator window send an XEvent to it. Before exiting, the terminal emulator should probably send a SIGHUP signal to your process.
I would suggest to use dtrace on the Solaris 10 machine to investigate this process. Start with this script which show all signals sent: http://www.brendangregg.com/DTrace/kill.d

Related

Why does vim crash when it becomes an orphan process?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int pid = fork();
if (pid) {
sleep(5);
// wait(NULL); // works fine when waited for it.
} else {
execlp("vim", "vim", (char *)NULL);
}
}
When I run this code, vim runs normally then crashes after the 5 seconds (i.e. when its parent exits). When I wait for it (i.e. not letting it become an orphan process), the code works totally fine.
Why does becoming an orphan process become a problem here? Is it something specific to vim?
Why is this even a thing that's visible to vim? I thought that only the parent knows when its children die. But here, I see that somehow, the child notices when it gets adopted, something happens and crashes somehow. Do the children processes get notified when their parent dies as well?
When I run this code, I get this output after the crash:
Vim: Error reading input, exiting...
Vim: preserving files...
Vim: Finished.
This actually happens because of the shell that is executing the binary that forks Vim!
When the shell runs a foreground command, it creates a new process group and makes it the foreground process group of the terminal attached to the shell. In bash 5.0, you can find the code that transfers this responsibility in give_terminal_to(), which uses tcsetpgrp() to set the foreground process group.
It is necessary to set the foreground process group of a terminal correctly, so that the program running in foreground can get signals from the terminal (for example, Ctrl+C sending an interrupt signal, Ctrl+Z sending a terminal stop signal to suspend the process) and also change terminal settings in ways that full-screen programs such as Vim typically do. (The subject of foreground process group is a bit out of scope for this question, just mentioning it here since it plays part in the response.)
When the process (more precisely, the pipeline) executed by the shell terminates, the shell will take back the foreground process group, using the same give_terminal_to() code by calling it with the shell's process group.
This is usually fine, because at the time the executed pipeline is finished, there's usually no process left on that process group, or if there are any, they typically don't hold on to the terminal (for example, if you're launching a background daemon from the shell, the daemon will typically close the stdin/stdout/stderr streams to relinquish access to the terminal.)
But that's not really the case with the setup you proposed, where Vim is still attached to the terminal and part of the foreground process group. When the parent process exits, the shell assumes the pipeline is finished and it will set the foreground process group back to itself, "stealing" it from the former foreground process group which is where Vim is. Consequently, the next time Vim tries to read from the terminal, the read will fail and Vim will exit with the message you reported.
One way to see by yourself that the parent processing exiting does not affect Vim by itself is running it through strace. For example, with the following command (assuming ./vim-launcher is your binary):
$ strace -f -o /tmp/vim-launcher.strace ./vim-launcher
Since strace is running with the -f option to follow forks, it will also start tracing Vim when it's launched. The shell will be executing strace (not vim-launcher), so its foreground pipeline will only end when strace stops running. And strace will not stop running until Vim exits. Vim will work just fine past the 5 seconds, even though it's been reparented to init.
There also used to be an fghack tool, part of daemontools, that accomplished the same task of blocking until all forked children would exit. It would accomplish that by creating a new pipe and have the pipe inherited by the process it spawned, in a way that would get automatically inherited by all other forked children. That way, it could block until all copies of that pipe file descriptor were closed, which typically only happens when all processes exit (unless a background process goes out of its way to close all inherited file descriptors, but that's essentially stating that they don't want to be tracked, and they would most probably have relinquished their access to the terminal by that point.)

Use C to exit ssh session to linux

I"m running a program that runs over ssh session. Meaning I connect to a linux using putty, the program starts, using the /etc/bash.bashrc file. At some point the program suppose to end and with it the ssh session via putty should disconnect.
What I"ve done untill now with a code below, and it doesn't work: the program exits, and stays in linux shell, meaning the putty is connected. What I expected is the command "exit" to end the putty ssh session.
char file_name[20];
pid_t pid = fork();
if (pid == 0)
{
printf("Starting vi\r\n");
char external_cmd[200] = "vi ";
strcat(external_cmd, file_name);
system(external_cmd);
} else
{
waitpid(pid,0,0);
printf("Exit..\r\n");
system("exit");
}
thanks for the help.
The easiest solution is to start your C program with exec so that it replaces the shell. The SSH session will naturally end when the program exits.
$ exec your_program
This code doesn't accomplish anything:
system("exit");
system() runs a child process under sh:
The system() function shall behave as if a child process were
created using fork(), and the child process invoked the sh utility
using execl() as follows:
execl(<shell path>, "sh", "-c", command, (char *)0);
So, your call to system() starts a sh process, which then executes the exit command you passed to it - and the child process exits, doing nothing to the parent processes.
You can kill the parent shell, though, by obtaining the PID of the parent process with getppid() and then calling kill() to kill it:
#include <unistd.h>
#include <signal.h>
...
pid_t parent = getppid();
kill( parent, SIGKILL );
SIGKILL is probably a bit extreme - it kills a process immediately, with no chance for the process to clean up after itself, but it will work (unless you're running a non-root setuid child process in this case - if you don't know what that is, don't worry about it.). SIGTERM is a less-extreme signal, but since it can be caught or blocked it isn't guaranteed to end a process. SIGINT is the equivalent of CTRL-C and may also work, but again the process can catch or ignore the signal. SIGQUIT can also be used, but it's purpose is to cause the process to quit and dump a core file, which you probably don't want.

c daemon runtime shell parameters

I'd develop a daemon that can be controlled by means of shell commands.
Just to clarify let's say that the daemon will have three functions (the invoke I'd like to have):
$ myDaemon start #do nothing, just daemonize. exit(0) is success, exit(1) otherwise
$ myDaemon stop #ask to the daemon to stop. exit(0) is success, exit(1) otherwise
$ myDaemon doSomething #ask to the daemon. exit(0) is success, exit(1) otherwise (let's say the daemon do int a = 0; exit(0); just to see the code, not interested in special stuffs)
may anyone kindly show me an example on how to produce this daemon (ok, start is really simple...)?
thanks all!
If you really want a daemon to do all the work, then one way is to write a terminal program to pass all the commands from the terminal to the daemon via some IPC technique.
All you have to do is:
Write a terminal program which forks and then execl the daemon along with command line arguments( like file descriptors if using pipes).
The terminal program then takes input in while loop from the terminal and passes them to the daemon through IPC mechanism in use.
EDIT
Algorithm for main process
main()
{
> fork the daemon with some initial arguments(if any)
while(1)
{
> take inputs from the shell
> parse the input and pass it to daemon(via preferred mechanism)
> if(exit condition) kill->daemon and break
}
}
For daemon
main() (or function_name() if no execl)
{
> initialize the arguments and IPC mechanism
while(1)
{
> read command(can use simple integer/character commands)
> perform requested action or break if exit command
}
> proper exit(closing file descriptors,etc.)
}

Writing a C program to move a process to background

I am trying to write a program , which does a fork and exec a child process and executes it in the back ground .
One approach I would see is to redirect the output to /dev/NULL file and come back to my main program . Any other ideas ?
After a process is started, shell has no more control on process file descriptors so you can not silence it by a shell command i.e. terminal has its stdin, stdout and stderr bound to the terminal and you cannot do anything about it without re-gaining control over that terminal.
There is a tool called retty how you can use it can be seen at this link retty this tool is used to attach processes running on terminals
Beside you can also use the built in disown command to disown the process which will prevent from sending a SIGHUP signal to the program when the shell exits
This link can be helpful Link to a similar problem

Terminal Access Control issues

I am attempting to write a shell. When a foreground process is run, the forked process pipeline is given its own process group id. The terminal is then given over to this process group id (using tcsetpgrp) and the shell waits for it to terminate before giving itself terminal control once again. This works perfectly fine.
The issue that arises is when I attempt to run a background process. Again, I give all of the processes in the pipeline a single process group id but this time I do not give terminal control to this group. Upon running, the output of a given background command is output to the terminal (before it is finished executing) and the terminal gives the user back the prompt at the same time. What should have happened is that the child process that attempts to write to the terminal should get a SIGTTOU and it should stop, but this clearly doesn't happen. I verified that the forked processes all have the same process group id and that this id is different from the shell's.
Upon exiting the shell (via ctrl-c) and returning to the standard bash shell that ran it, because I did not reap the background process upon shell termination, the background process continues running (which is excepted). What is weird though is that this process continues writing output to the bash shell even though it is not the foreground process. This leads me to conclude that either this background process is not getting any SIGTTOUs because of a POSIX bug (unlikely), it is handling them (causing the default action of stopping to be ignored), or the background process is ignoring SIGTTOUs.
Is there a way to, before exec'ing a forked process, ensure that it will stop upon receiving a SIGTTOU (assuming that the exec binary does not change anything)?
SIGTTOU is sent to a background process which tries to write to the terminal only if the termios flag TOSTOP is set for that terminal. By default, it is generally not set, in which case the background process can happily write to the terminal. (The TOSTOP flag does not affect read permissions. If the process tries to read, it will be sent a SIGTTIN.)
So, yes, there is something the foreground process can do: use tcsetattr to set TOSTOP
The solution was to make the forked process execute the following before calling exec:
struct termios term;
if (tcgetattr(STDIN_FILENO, &term) < 0)
printf("ERROR\n");
term.c_lflag = TOSTOP;
if (tcsetattr(STDIN_FILENO,TCSANOW,&term)<0)
printf("ERROR\n");

Resources