SIGCHLD causing EOF in parent process - c

A minimalistic example of a program that
reads char by char by getchar, until EOF
starts a child process when an "a" is encountered.
Curiously, installing a signal handler causes getchar to return an EOF whenever a child exits. A rather unwanted side-effect.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handler() {
//
}
void action() {
if (fork() == 0) {
printf("(action)\n");
sleep(1);
exit(0);
}
}
int main(int argc, char** argv) {
char c;
signal(SIGCHLD, handler); // signal
while (EOF != (c = getchar())) {
if (c == 'a') action();
}
printf("End Of Data\n");
}
The problem disappears when the signal ligne is commented out.
Can someone please explain why this happens?
Seems the same problem as Handling SIGCHLD will return EOF to the father, why?
which has no clear answer about why, and how to avoid this.
EDIT 1: Was not clear to me doesn't mean that much (friday evening...)
The cause is, the system call under getchar() was interrupted, causing it to return EOF (as written in the manual page).
(Ad hoc) Solution: check errno for EINTR.
On this example
while (EOF != (c = getchar()) || errno == EINTR) {
EDIT 2: checking errno works with getchar(), but doesn't make you out of the woods with getline().
The real solution is to forget about the obsolete signal(), and use sigaction() with the SA_RESTART flag.

Related

C Ctrl+D redirection to do smtg else

I am writting a program that copy the bash behaviour,
I use a while loop that stop if a variable "stop" is filled.
I want to get the ctrl-D signal when I press Ctrl-D only to fill the variable stop instead of stopping everything, is it possible
#include <signal.h>
void sigint_do(int sig) //handle the crtl c signal
{
signal(SIGINT, sigint_do);
sig = sig;
fflush(stdout);
}
int main()
{
signal(SIGINT, sigint_do);
int stop = -1;
while (stop < 0)
//do smg
if (stop >= 0)
{
free all allocation needed;
exit(stop);
}
return 0;
}
i already use a signal handling for ctrl-C with signal.h but i don't understand how to do modify the variable "stop" in the signal handler function if the Ctrl-D is seen.
could you give me any tip please?
CTRL+D is not a signal. It is simply translated by the system to the EOF symbol.
You can detect it if the read() command returns 0 for example.
You can use readline(). It returns NULL when ctrl + D is pressed. (For me read worked not really well).
something like:
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
int main ( void )
{
char *line;
line = readline(":");
while(line)
{
line = readline(":");
}
return (0);
}

Program won't end after catching SIGINT before pressing ENTER?

Why does my program not end until I press ENTER in terminal after pressing Ctrl+C?
Here is my code:
static volatile sig_atomic_t keepRunning = 1;
void intHandler(int sig)
{
keepRunning = 0;
}
int main(int argc, char *argv[])
{
signal(SIGINT, intHandler);
int ch;
while((ch = fgetc(stdin)) && keepRunning)
{
...
}
exit(EXIT_SUCCESS);
}
I have setup my while loop to read chars from stdin and to run until the SIGINT is caught. After that the keepRunning will be set to 0 and loop should end and terminate the program. However when I hit Ctrl+C my program doesn't accept any input anymore but it doesn't let me type any command in terminal until I press ENTER key. Why is that?
It is because fgetc() is blocking the execution, and the way you chose to handle SIGINT - fgetc() will NOT be interrupted with EINTR (see #AnttiHaapala's answer for further explanation). So only after you press enter, which releases fgetc(), keepRunning is being evaluated.
The terminal is also buffered, so only when you press enter it will send the chars to the FILE * buffer and will read by fgetc() one by one. This is why it exists only after pressing enter, and not other keys.
One of several options to "solve" it is to use nonblocking stdin, signalfd and epoll (if you use linux):
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>
int main(int argc, char *argv[])
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
/* Block signals so that they aren't handled
according to their default dispositions */
sigprocmask(SIG_BLOCK, &mask, NULL); // need check
// let's treat signal as fd, so we could add to epoll
int sfd = signalfd(-1, &mask, 0); // need check
int epfd = epoll_create(1); // need check
// add signal to epoll
struct epoll_event ev = { .events = EPOLLIN, .data.fd = sfd };
epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev); // need check
// Make STDIN non-blocking
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
// add STDIN to epoll
ev.data.fd = STDIN_FILENO;
epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev); // need check
char ch;
int keepRunning = 1; // no need to synchronize anymore
while(keepRunning) {
epoll_wait(epfd, &ev, 1, -1); // need check, must be always 1
if (ev.data.fd == sfd) {
printf("signal caught\n");
keepRunning = 0;
} else {
ssize_t r;
while(r = read(STDIN_FILENO, &ch, 1) > 0) {
printf("%c", ch);
}
if (r == 0 && errno == 0) {
/* non-blocking non-eof will return 0 AND EAGAIN errno */
printf("EOF reached\n");
keepRunning = 0;
} else if (errno != EAGAIN) {
perror("read");
keepRunning = 0;
}
}
}
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) & ~O_NONBLOCK);
exit(EXIT_SUCCESS);
}
Also note that I'm not using fgetc(). Because of buffering nature of FILE *, it will not work well with nonblocking IO.
The program above is intended for education purposes only and not for "production" use. There are several issue that need attention, for example:
All the libc / system calls need to tested for errors.
If output is slower than input (printf() may easily be slower), it may cause starvation and the signal will not get caught (the inner loop will exit only after input is over/slower).
Performance / reduction of system calls:
read() can fill much larger buffer.
epoll_wait can return multiple events instead of 1.
Usually system calls return with errno == EINTR if a signal was delivered when they're blocking, which would cause fgetc to return early with an error condition as soon as Control-C was hit. The problem is that the signal set by signal will be set to auto restarting mode, i.e. the underlying read system call would be restarted as soon as the signal handler completed.
The correct fix would be to remove the automatic restart but it does make it slightly trickier to use correctly. Here we see if the return value is EOF from fgetc and then if it is caused by EINTR and restart the loop if the boolean was not true.
struct sigaction action = {
.sa_flags = 0,
.sa_handler = intHandler
};
sigaction(SIGINT, &action, NULL);
int ch;
while (1) {
ch = fgetc(stdin);
if (ch == EOF) {
if (errno == EINTR) {
if (keepRunning) {
continue;
}
break;
}
break;
}
}

Parent gets EOF when child quits after getting an EOF?

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
int ch;
pid_t p = fork();
if (p == 0) {
do {
ch = getchar();
} while (ch >= 0);
return 0;
}
int s;
waitpid(p, &s, 0);
printf("A done\n");
p = 0;
do {
ch = getchar();
} while (ch >= 0 && (++p));
printf("chars: %d\n", p);
return 0;
}
Here's a minimal example code. Theoretically it should read some characters until EOF, and print A done, and read some more characters, and show you how many there are after A done.
However, on my Windows Subsystem for Linux (Ubuntu 18.04), when I hit Ctrl-D for the first time, both the child and parent processes quit (receives an EOF). The output I get is something like
asdfghjkl
^DA done
chars: 0
Why is that? And how do I fix this?
In a fork(2), file descriptors are dup(2)ed, so they share the same file pointer, and what one of the process reads, is not read by the other, as a consequence of this.

Can't seem to find an EOF character?

I have:
#include <stdio.h>
/* Copy input to output; 2nd version. */
main(void)
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
return 0;
}
I want to terminate the while loop by entering an end-of-line character.
I Have Tried Inputing:
"\t"
"\0"
%d
%f
%c
%x
%n
EOF
"EOF"
\nEOF
int
float
char
long
long long
array
1 => 10
all letters
all symbols on keyboard
.
.
.
Question: What is the magical EOF character that I'm looking for?
*I am Sorry if this is a really easy question for you,but please be nice I'm only a beginner trying to learn something.
On Windows, Ctrl+Z;
on Linux, Ctrl+D.
There is NO EOF character. "EOF" is a logical condition that represents "end of file" has been met.
On Linux machine, you can "signal" the standard input EOF condition by pressing Ctrl+D in the beginning of the line.
Windows systems reserve a character Ctrl+Z, which is 0x1A in hex, to indicate this "end of file" condition. You can input this character by pressing Ctrl+Z. It is still not a real EOF character though. Rather, it is a convention in Windows.
Here ya go #Andy. You just used an int by accident instead of char c.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char c;
while ((c = getchar()) != '\t') // while input != tab, remember to use single
putchar(c); // quotes for characters '\n' etc.
system("pause");
return 0;
}
If you are curious about the signals in UNIX/LINUX system this code might help, wrote it for one of my OS labs. Essentially, the program keep asking for a user input. However, when you try to quit during in the beginning with ctrl+z or ctrl+c it doesn't allow you to because the signal gets ignored by the parent and gets handled by the signal handlers for the child process. Note, the parent is sleeping in the beginning, but when it wakes up it kills the child process and ends the program.
#include <stdio.h>
#include <signal.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/wait.h>
#define maxLength 1024
//****************************************
// Signal Handlers For Child Process
//****************************************
void ctrlchandler(){
signal(SIGINT, SIG_IGN); //Ignore ctrl-c
write(1, "Don't even think about it!", 26);
}
void ctrlzhandler(){
signal(SIGTSTP, SIG_IGN); //Ignore ctrl-z
write(1, "Nice Try.", 9);
}
//****************************************
// Main Program
//****************************************
int main(int argc, char* argv[]){
pid_t pid;
int status;
//Dynamically allocate char array for input line
char *inputLine = (char*)malloc(maxLength*sizeof(char));
//Ignore Ctrl-z and Ctrl-c
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
//Fork Process
if((pid = fork())<0){
//If fork fails
printf("Fork Child Process Faild.\n");
}
//Parent Process
else if(pid != 0){
printf("Parent: My child %d has been spawned.\n",pid);
printf("My pid is %d\n",getpid());
sleep(30);
kill(pid, SIGKILL);
if(waitpid(pid, &status, WUNTRACED))
printf("Child %d has terminated abnormally.\n",pid);
}
//Child Process
else{
sleep(1); //Wait for parent to output first
while(1){
signal(SIGTSTP, ctrlzhandler);
signal(SIGINT, ctrlchandler);
printf("Enter Input:");
fgets(inputLine, maxLength, stdin);
}
}
//Free allocated char array
free(inputLine);
return 0;
}
main()
{
printf ("%d=%x sizeof=%d\n", EOF, EOF, sizeof(EOF));
}
The output is:
-1=ffffffff sizeof=4
EOF is not a char, it is an int
if you type the Control sequence signifying end of file - it will be translated to an int whose value is -1

Stop some commands used in the system() function

I have a problem with system() function.
I need to implement a simple bash, one of the modules of the my project is to permit user types some bash command to execute it.
Above what i'm doing actually:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// similar to gets
int reads(char* str)
{
#ifndef WIN32
fflush(stdout);
strcpy(str,GetServiceLine());
#else
gets(str);
#endif
return 1;
}
int main(void) {
char str[100];
while(strcmp(str, "exit")) {
printf("\nNote: type \"exit\" to return menu\n");
printf("MyBash$ ");
reads(str);
system(str);
}
return 0;
}
My problem is with commands like ping.
When i run this code on my PC and i try execute ping command for a legal IP it works fine, i can stop the ping process using CTRL+C, but when i run it on my target on the same way i can't use CTRL+C and my process keep always at system() call.
Does somebody can help me?
Note: i read this post about how to use CTRL+C to break a system function. I tried the suggestion but didn't work.
Thanks.
Since you hadn't tried it yet I'll throw it up here as a suggestion. You can always install a signal handler to catch signals that you are interested in.
Here's a quick example using (mostly) your code which demonstrates how it's done:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void intHandler(int dummy)
{
exit(1); // Do whatever you want here to handle it...
}
int main(void)
{
char str[100];
signal(SIGINT, intHandler);
signal(SIGKILL, intHandler);
while(strcmp(str, "exit")) {
printf("\nNote: type \"exit\" to return menu\n");
printf("MyBash$ ");
gets(str);
system(str);
}
return 0;
}
I can catch a ctrl+C using this, but I'm not sure if it's what you're looking for.
After the comments above i just thought of explaining why exactly you can't control this in a graceful manner (some hacks are suggested in comments though).
system command is going to behave exactly if you forked a child process and then called exec on the child for executing the binary passed to exec as an argument.
The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate. If this might cause the application to miss a signal that would have killed it, then the application should examine the return value from system() and take whatever action is appropriate.
Remember, this is very much OS specific behavior and there is no standard as such.
system() function call in Linux
Internally ping utility would run on icmp and waits until a response is received from the other node.
You might write a signal handler as suggested in another answer and call a killpid() but it would be blocked until the call to system() returns. This is stated in the specs of the function. So you might be able to terminate but only AFTER the call has returned. :)
Below the code used to fix my problem. I don't know if is the better solution, but solved my problem in this case.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// similar to gets
int reads(char* str)
{
#ifndef WIN32
fflush(stdout);
strcpy(str,GetServiceLine());
#else
gets(str);
#endif
return 1;
}
void getCommandName(char input[], char output[])
{
int count=0;
while (input[count] != NULL && input[count] != ' ' && input[count] != '\0') {
output[count] = input[count];
count++;
}
}
int killLastCommand(int pid)
{
char commandKill[30];
memset(commandKill, 0, 30);
sprintf(commandKill, "kill -9 %d", pid);
return(!system(commandKill));
}
int main(void) {
FILE *fp; //Will be used with popen()
char str[100];
char lastCommandName[50];
char pidofCommand[50];
char strLastPIDCommand[10];
int lastPIDCommand=0;
memset (str, 0, 100);
while(strcmp(str, "exit")) {
if (lastPIDCommand == 0) {
memset (lastCommandName, 0, 50); //Clean lastCommandName array
memset (pidofCommand, 0, 50); //Clean pidofCommand array
memset (strLastPIDCommand, 0, 10); //Clean strLastPIDCommand array
printf("\n\nNote: type \"exit\" to return menu\n");
printf("MyBash$ ");
reads(str);
if (strcmp(str, "exit")) {
sprintf(str, "%s &", str);
}
getCommandName(str, lastCommandName);
system(str);
sleep(1); //Sleep to guarantee than command will end
sprintf(pidofCommand, "pidof %s", lastCommandName);
//Saving PID
fp = popen(pidofCommand, "r");
if (fp) {
fgets(strLastPIDCommand, 10, fp);
lastPIDCommand = atoi(strLastPIDCommand);
} else {
//Handle error
}
pclose(fp);
printf("commandName = %s\r\n", lastCommandName);
printf("pid = %d\r\n", lastPIDCommand);
} else {
printf("\n\nYou have a command running, press 'kill' to stop it before to type another command\n");
printf("EITVBash$ \n\n");
reads(str);
// if (str[0] == 0x03) { //CTRL+C hexa code
if (!strcmp(str, "kill")) {
if (killLastCommand(lastPIDCommand)) {
lastPIDCommand = 0;
}
}
}
}
return 0;
}
My implementation probably isn't clean, but i don't have much experience with c.
Thanks everybody.

Resources