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.
Related
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.
I'm doing this assignment in which a number of child processes are generated and if they're aborted (ctrl-\ or ctrl-4) the parent process should run instead. This is the code:
int main(int argc, char *argv[])
{
/* The rest of the code is omitted. "times", "arg1"
and "cmd1" are parameters passed when running the program */
for(cont = 0; cont < times; cont++)
{
pid = fork();
if(pid == 0)
execvp(arg1,cmd1);
else if (pid >0) {
wait(&status);
if (WCOREDUMP(status) !=0)
printf("Core dump generado\n");
}
}
return 0;
}
The program runs a Linux command a number of times, with argv[1] being the number of times, and arg[2], argv[3] and so on the Linux command itself.
It runs fine if it's not cancelled. But when I try to abort it (for instance using a sleep command and then typing ctrl-4):
./ntimes 2 sleep 10
^\Quit (Core dumped)
it generates a dump. I want the parent process to print a message instead. I've tried with signal handling functions and many other things, but I can't seem to make it work.
SIGQUIT targets the whole foreground process group of your terminal—it kills the parent along with the child.
To prevent it from killing the parent, you need to either:
ignore it or block it in the parent
catch it in the parent
With approach 1., you'll need to unblock it/unignore it in the child before execing.
With approach 2., the signal disposition will be automatically defaulted upon execing so it won't affect the child, however, having at least one signal handler will open your application up to the possibility of EINTR errors on long-blocking syscalls such as wait, so you'll need to account for that.
Here's an example of approach 2. You can try it on e.g., ./a.out sleep 10 #press Ctrl+\ shortly after this.
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
void h(int Sig){}
int main(int argc, char *argv[])
{
sigaction(SIGQUIT, &(struct sigaction){.sa_handler=h}, 0);
int cont, status, times=1;
pid_t pid;
for(cont = 0; cont < times; cont++)
{
pid = fork();
if(pid == 0)
execvp(argv[1],argv+1);
else if (pid >0) {
// retry on EINTR
int rc; do{ rc=wait(&status); }while(0>rc && EINTR==errno);
if (0>rc) return perror("wait"),1;
if (WIFSIGNALED(status)){
if (WCOREDUMP(status))
printf("Core dump generado\n");
}
}
}
return 0;
}
I have a code as below
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("a\n");
fork();
printf("b\n");
if(fork() == 0) {
printf("c\n");
exit(0);
}
printf("d\n");
return 0;
}
Output :
a
b
d
a
b
c
a
b
d
a
b
c
I don't know why the output duplicated many times.
I don't know why the output duplicated many times
Because printf() is buffered.
When a process calls fork(), the resulting child process obtains a copy of the parent's output buffer.
You can empty this output buffer by placing fflush(stdout) just before each call to fork(). In that case output should be:
a
b
b
d
c
d
c
Note that if the output refered to a terminal, it would be line-buffered by default, i.e.: the buffer would be dumped every time a \n is sent to the output. Not so if you are redirecting the output to a file.
When you call fork() it gets a copy of output buffer of the calling process. Buffering is enabled by default, so you get this behavior.
You can use
fflush(stdout);
before a call to fork(). Or, you can also disable buffering using
setbuf(stdout, NULL);
You can read more about fork here. Let me know if you need any more help.
The answer is already in the comments. You are calling fork() twice. So the solution is to just call it once and save the result in a variable like this int pid = fork(). Also, you should check if the fork-call failed (if it does, it returns a negative value).
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("a\n");
int pid = fork();
if (pid < 0)
{
fprintf(stderr, "Can't fork!");
exit(1);
}
printf("b\n");
if(pid == 0)
{
printf("c\n");
}
else
{
printf("d\n");
}
return 0;
}
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
}
this is a simple example to use the tcsetpgrp function:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
static void judge(void){
pid_t pid;
pid = tcgetpgrp(STDIN_FILENO);
if(pid == -1){
perror("tcgetpgrp");
return;
}else if(pid == getpgrp()){
printf("foreground\n");
}else{
printf("background\n");
}
}
int main(void){
signal(SIGTTOU,SIG_IGN);
judge();
int result;
result = tcsetpgrp(STDIN_FILENO,getpgrp());
if(result == -1){
perror("tcsetpgrp");
return -1;
}
judge();
return 0;
}
i will run it in the background,as my expection,the output like:
todd911#virtual-machine:./a.out &
[1] 15894
todd911#virtual-machine:~$ background
foreground
but in fact,it act like:
todd911#virtual-machine:./a.out &
[1] 15894
todd911#virtual-machine:~$ background
foreground
exit
at last,the terminal exit automatically,is anybody know the reason?
It is possible that if the process group that controls the terminal exits, then the shell or terminal will decide the session has ended and kill it off. I tried your program on gnome-terminal with ksh93 and bash and it did not exit.