I was trying to build an interactive shell which runs within the ZSH shell. Any command would execute in it's own process group (thus I use tcsetpgrp call to make it the foreground process group, and once the command is executed the the interactive shell would become the foreground process group). However I observe that during the execution of my code I get a SIGTTOU signal, which to the best of my knowledge is delivered when a background process attempts to write to the terminal. With respect to the below example if someone could explain the reason for the aforementioned signal it would be really helpful.
Minimal reproducible example
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
for(;;){
printf(">Network shell\n");
int par = getpid();
int child = fork();
if(child == 0){
int c2 = fork();
if(c2 == 0){
char * args[2];
args[0]="/bin/ls";
args[1] = NULL;
execv(args[0], args);
}
else{
wait(NULL);
tcsetpgrp(STDIN_FILENO, par); //done to bring previous process group back to foreground
tcsetpgrp(STDOUT_FILENO, par);
}
}
else{
setpgid(child, child);
tcsetpgrp(STDIN_FILENO, child);
tcsetpgrp(STDOUT_FILENO, child);
wait(NULL);
}
}
}
Here is the output I see on my terminal:
Your /bin/ls writes to stdout, and since its stdout is not redirected, it writes to the terminal.
Related
If the subprocess does not call the system call, will the signal sent by kill still take effect?
It worked. But I want to know when did the subprocess enter the kernel mode.
Code as follows.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int pid;
if ((pid = fork()) == 0)
{
while (1)
continue;
exit(0);
}
sleep(3);
kill(pid, SIGINT);
int wid, status;
while ((wid = wait(&status)) != -1)
{
printf("child %d: exited with ", wid);
if (WIFEXITED(status))
printf("%d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status))
printf("%d\n", WTERMSIG(status));
}
return 0;
}
Yes, processes that do not make system calls can still receive signals. You can easily test this by writing an infinite loop program, running it, then pressing Ctrl-C, or using the kill command from another terminal window.
Let's take this snippet as an example:
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char const *argv[]) {
int pid = fork();
if (pid == 0) { // Child
execlp(argv[1], argv[1], NULL);
} else if (pid > 0) { // Parent
int n = atoi(argv[2]);
sleep(n);
kill(pid, SIGKILL);
int status;
wait(&status);
...
exit(0);
}
return 0;
}
After being run with main [command] [seconds], the program should create a subprocess to execute the given command and terminate it after n seconds.
Problem is, if I call it with args top 3 it correctly starts top and terminates after 3 seconds, but i'm left with a working shell with invisible text: i can type and execute commands, but the typed text doesn't show on the screen until a reset is run.
Any insight on what's happening?
top sets the terminal to a mode where it doesn't echo input (see man noecho or man tcsetattr). If top exits cleanly, like when you press "q" interactively, it resets it to a normal mode. But your program kills it, so the terminal is left in no-echo mode.
I am forking a child process and execl a BinaryApp.exe in that child process. However, I want a separate terminal window for newly forked BinaryApp.exe and control transferred to that terminal window so that it can detect any key strokes. Also by separating terminal windows, I can show interprocess communication more straightforwardly as parent and child process will output in their respective terminal windows. I am using cygwinb20 on Windows.
I have written a code for forking BinaryApp.exe but it shows its output in the same window as its parent. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
int main() {
pid_t Pid;
int ret;
printf("Main Program\n Initializing processe...\n");
Pid = fork();
if (Pid >= 0) /* fork succeeded */
{
if (Pid == 0) /* fork() returns 0 for the child process */
{
ret = execl("../BinaryApp.exe","BinaryApp.exe",(char*)NULL);
if(ret == -1) {
perror("execv - BinaryApp.exe");
}
}
}
else /* failure */
{
perror("fork - BinaryApp.exe");
exit(0);
}
// Doing other things
}
In the manual for GNU libc about orphaned process groups, it mentioned :
“process groups that continue running even after the session leader
has terminated are marked as orphaned process groups.
When a process group becomes an orphan, its processes are sent a SIGHUP
signal. Ordinarily, this causes the processes to terminate. However,
if a program ignores this signal or establishes a handler for it
(see Signal Handling), it can continue running as in the orphan process
group even after its controlling process terminates; but it still
cannot access the terminal any more. ”
I write a test program, but when the process group becomes an orphan, its process didn't receive the SIGHUP signal. I am wondering why?
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static void
sig_hup(int signo) //**never get called ???**
{
printf("SIGHUP received, pid = %ld\n", (long)getpid());
}
static void
pr_ids(char *name)
{
printf("%s: pid = %ld, ppid = %ld, pgrp = %ld, tpgrp = %ld\n",
name, (long)getpid(), (long)getppid(), (long)getpgrp(),
(long)tcgetpgrp(STDIN_FILENO));
fflush(stdout);
}
int
main(void)
{
char c;
pid_t pid;
pr_ids("parent");
pid = fork();
if (pid > 0) { // parent
sleep(5);
exit(0); // parent exit;
} else {
pr_ids("child");
setsid(); //create new session, and "child" becomes the session leader
pid = fork();
if(pid>0) {
sleep(20);
exit(0); // "child" exit
// so the process group become an orphan process group
}
else{
pr_ids("grandson");
signal(SIGHUP, sig_hup); // establish signal handler
sleep(60); // now becoming orphan process group
printf("end\n");
}
}
exit(0);
}
Orphaned process groups get SIGHUP followed by SIGCONT if they're stopped when they become orphaned.
Sleep is not enough, you need:
kill(getpid(), SIGSTOP); //or raise(SIGSTOP);
In addition to that, POSIX doesn't require that SIGHUP and SIGCONT be sent if the orphaning was caused by setsid() or setprgrp() because then it wasn't caused by an exiting process innocently unaware of job control (see http://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ).
However, with kill(getpid(), SIGSTOP) instead of that sleep(60) in the child, you will get a stopped orphan with your program even if you don't call setsid().
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
static void
sig_hup(int signo) //**never get called ???**
{
printf("SIGHUP received, pid = %ld\n", (long)getpid());
}
static void
pr_ids(char *name)
{
printf("%s: pid = %ld, ppid = %ld, pgrp = %ld, tpgrp = %ld\n",
name, (long)getpid(), (long)getppid(), (long)getpgrp(),
(long)tcgetpgrp(STDIN_FILENO));
fflush(stdout);
}
int
main(void)
{
pid_t pid;
pr_ids("parent");
pid = fork();
if (pid > 0) { // parent
sleep(5);
_exit(0); // parent exit;
} else {
pr_ids("child");
/*setsid(); //create new session, and "child" becomes the session leader*/
pid = fork();
if(pid>0) {
sleep(2);
exit(0); // "child" exit
// so the process group become an orphan process group
}
else{
pr_ids("grandson");
signal(SIGHUP, sig_hup); // establish signal handler
kill(getpid(), SIGSTOP);
printf("end\n");
}
}
exit(0);
}
should get you a SIGHUP in the child after the parent dies (5s).
That document section is talking specifically about the loss of a controlling terminal of a process that had one—usually by a modem hangup, or the virtual equivalent (ending an ssh session, etc). (I think the phrasing in the document could be improved here). When you use setsid() here, you give up access to the controlling terminal by the time setsid() returns, so there is no controlling terminal to lose from there forward.
You could open() a tty device (such as a pty slave) to gain a controlling terminal (note that you may have to do some additional operation as well—FreeBSD requires a TIOCSCTTY ioctl), then lose it again, and then you should get the SIGHUP signal.
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void sig_handler(int signal);
int pid, forkFlag = 0;
int main( int argc, char **argv, char **envp )
{
sigset(SIGINT, sig_handler); //handle ctrl + c
sigignore(SIGTSTP);
sigignore(SIGSTOP);
int ex, rv, status;
forkFlag = 1; //fork is being called
pid = fork();
if(pid == -1){
perror("fork");
exit(2);
}
else if (pid == 0){ //if child process
ex = access(argv[0], X_OK); //check if file is executable
if(ex){
perror("access");
exit(1);
}
else{
rv = execve(argv[0], argv, envp); //run program in child process
if(rv == -1){
perror("execve");
exit(1);
}
}
exit(0); //end child process
}
else{
rv = waitpid(pid, &status, 0); //wait for child
if(rv == -1){
perror("waitpid");
}
if(WEXITSTATUS(status)){ //check status of child if it did ot return 0
printf("The return status of the child was %d\n", WEXITSTATUS(status));
}
}
forkFlag=0;
}
void sig_handler(int signal)
{
if(signal == SIGINT && (pid && forkFlag)){
kill(pid,signal); //send kill to child
}
}
I'm trying to make my program ignore ctrl + C, except when there is a child process running, then it sends the the SIGINT to the child process. However, when I press ctrl + c when the child process is running, waitpid() returns -1 with the error "Interrupted System Call." This makes the child process stop running, but if I use ps, the child process is still there, but now labeled as defunct. I know from printf statements that kill is being calle din the function sig_handler, and that pid and forkFlag are their correct values. Is waitpid() making my program ignore the kill? How do I fix this? I know this code does next to nothing, but it's a small portion of my code (the only part involving fork)
Thanks for any help.
The problem is that the child processes get the same overridden handler for SIGINT. You probably want to reset the signal handler in the child process after the fork, or you might want to install the signal handler in the parent after you've already forked the child, so it doesn't inherit the overriden handler.