I have encountered problems on signal handling when writing a shell-like program on C.
Here is the simplified version of my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#define SIZE 255
void sig_handler(int sig){
if (sig == SIGINT)
printf("\n[shell]$\n");
}
int main()
{
char input[SIZE];
printf("[shell]$");
signal(SIGINT,sig_handler);
while( gets(input) != NULL ){
// code of the shell including command interpreter and command execution
printf("[shell]$");
}
return 0;
}
When I run the program and try out SIGINT with command - "cat", the output shows as the following:
[shell]$ ^C (ctrl+C pressed for the first time)
[shell]$
^C (the input position go to the next line, which is unwanted)
[shell]$
cat (I want it in the same line with [shell]$)
^C
[shell]$
[shell]$ (duplicate printing occurs)
I have tried to modify the function void sig_handler(int sig) by deleting the second \n. However, the situation becomes worse than before. The program doesn't automatically trigger the signal event on the first pressing of ctrl+C.
To clarify my problem, here are the two questions I ask:
1. How to make the input position on the same line with [shell]$ ?
2. How to solve the duplicate printing problem ?
What #zneak said is true, you can use fflush and delete the second \n in sig_handler,
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#define SIZE 255
void sig_handler(int sig){
if (sig == SIGINT)
printf("\n[shell]$");
fflush(stdout);
}
int main()
{
char input[SIZE];
printf("[shell]$");
fflush(stdout);
signal(SIGINT,sig_handler);
while( gets(input) != NULL ){
// code of the shell including command interpreter and command execution
printf("[shell]$");
}
return 0;
}
First and foremost, printing from signal handler is a bad idea. Signal handler is like an interrupt handler - it happens asynchronously, it could be raised while being inside your standard library code and calling another stdlib routine might mess up with non-reentrant internals of it (imagine catching SIGINT while inside of printf() in your loop).
If you really want to output something from within, you better use raw write() call to stderr file descriptor.
Related
I have the following code(there's a bunch of extra headers that were used in other parts of the code that I removed since they don't have anything to do with my issue). I have also removed the error checking for fork and pipe for brevity:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
void compile(char *fullname)
{
int pid = fork();
int pipe_send_output[2];
pipe(pipe_send_output);
if (pid == 0)
{
close(pipe_send_output[0]);
dup2(pipe_send_output[1], STDERR_FILENO); // redirect output of gcc to the pipe
execlp("gcc", "gcc", "-Wall", fullname, NULL);
printf("execlp error");
}
else
{
close(pipe_send_output[1]);
dup2(STDIN_FILENO, pipe_send_output[0]); // redirect from pipe to stdin
close(pipe_send_output[0]);
exit(0);
}
}
int main(){
compile("folder/small.c");
}
The compile function is supposed to create a new process which compiles some c file using gcc, and then sends the output(errors and warnings) to the parent process, to be printed. To do this I redirected STDERR to the write end of the pipe, and then in the parent process redirected the read end to stdin. I can't for the life of me figure out why it's not displaying anything. If I remove the redirection of stderr to the pipe, then plenty of stuff it outputed, so the issue isn't gcc not outputting anything. I tried to replace the redirection to stdin with reading from the pipe and then printing it, but that has the same result. A couple of time this did print one or 2 characters, which is even more confusing. This is the code that replaces the second call to dup2 for printing:
char buffer[1024];
while(read(pipe_send_output[0], buffer, 1024)) printf("%s", buffer);
I've looked everywhere on google and on the man page and still don't have a clue what's wrong.
Disclaimer:This is part of a project for a lab at university. The code that I wrote follows the blueprint from the materials my professor provided, and makes perfect sense to me. Any hint on what the issue may be is appreciated.
So I have an exercise to do, and one part of this exercise requires us to execute a command passed as an argument, be able to pass it some strings on stdin, and get its output on stdout and stderr.
How I did it, I need to redirect the stdout and stderr (of the child, which is gonna call an exec) to a couple of pipes (other end of the pipes is held open by the parent).
I managed to do it, when I ask it to execute bash and send it "ls", it gives me what i want, where i want it. Same with cat and others.
Problem is, when I try executing awk or sed, nothing is ever written on the pipe. Ever.
If i leave stdout untouched, it does print it how it should. But as soon as i redirect the stdout, nothing.
I tried everything, select(), wait(), sleep() (even though it's not allowed). Nothing seems to work.
I made a minimum working example of what i mean (clearly, it lacks of conventions and mindful writing, as free() and close(), but it does it's job) which Is the one I'm attaching. The code works when i call it like this:
./program $(which bash)
It prompts for something, i write "ls" and it gives me the result expected
but when i try
./program $(which awk) '{print $0;}'
I get nothing at all
Here's the code (minimum working example):
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
int main(int argc, char* argv[]){
int fdStdinP[2],fdStdoutP[2];
char *string,*array[3];
array[0]=argv[1];
array[1]=argv[2];
array[2]=0;
pipe(fdStdinP);
pipe(fdStdoutP);
int pid=fork();
if(pid==0){
close(fdStdinP[1]);
close(fdStdoutP[0]);
dup2(fdStdinP[0],0);
close(fdStdinP[0]);
dup2(fdStdoutP[1],1);
close(fdStdoutP[1]);
//as suggested, the file descriptors are now closed
execvp(argv[1],array);
perror("");
return 0;
}
close(fdStdinP[0]);
close(fdStdoutP[1];
string=calloc(1024,sizeof(char));
read(0,string,1024);
write(fdStdinP[1],string,1024);
free(string);
string=calloc(1024,sizeof(char));
read(fdStdoutP[0],string,1024);
printf("I have read:%s",string);
return 0;
}
Thank you for your time.
Awk continues to wait for input and buffers its output, so appears to hang. Closing the sending end will tell awk that it's input has ended so that it will end and flush its output.
write(fdStdinP[1],string,1024);
close(fdStdinP[1]); // just added this line.
I am trying something in C on hp-nonstop(tandem),
As part my task is to wait for sometime.
I try to use the
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int i;
for(i=0;i<10;i++)
{
printf("Something");
sleep(5);
printf("Something");
fflush(stdout);
}
}
It's compiling without any problem,
While running it is giving ABENDED: each time different no.
The result calling sleep() from guardian environment is undefined. That might be leading to ABEND that you mentioned. If you want to wait for some time in guardian hp-nonstop environment, you should call DELAY(). It takes centi-seconds as arguments. So if you want to add delay of 5 seconds, you should call it as DELAY (500). You also need to include the header #include<cextdecs(DELAY)>
I am trying to write a program, caller, which traps SIGINT with sigaction, and calls an external program, prog.
I understand how to use sigaction on a simple program, but I don't know how to use it to set signal handlers for other processes (called by execl, for example).
The following is a MWE:
caller.c is this:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
void got_sig(int sig) {
printf("SIGNAL caught: %d\n",sig);
}
int main () {
struct sigaction sa;
(void) sigfillset(&sa.sa_mask);
sa.sa_handler = got_sig;
sa.sa_flags=0;
sigaction(SIGINT,&sa,NULL);
printf("\n");
execl("./prog", "./prog", (char*) NULL);
}
prog.c is this:
#include <stdio.h>
#include <unistd.h>
int main() {
for(int i=0; ; i++) {
sleep(1);
printf("epoch: %d\n",i);
}
}
But when I run caller, I see the output of prog.c, and hitting ^C does stop the prorgam (the signal is not trapped).
I suppose this is related to how execl works (it creates a new process, which does not inherit the parents' signal handlers -- is this right?)
So, how can I accomplish what I am trying to do?
Thank you!
execl("./prog", "./prog", "./prog", (char*) NULL);
Or if it's not working, check out here: https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
I serached it but i can't find anything i am looking for. Can anyone give a simple example how to pause for a while (not for given time, like sleep()) and proceed programme ? I tried something but it just pause, and then the only thing i can do is to terminate the program before the second printf:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include<termios.h>
#include<signal.h>
#include <sys/wait.h>
struct sigaction act;
sigset_t to_start, to_stop;
int main(int argc, char *argv[])
{
int i=0;
printf("BEFORE SIGSUSPEND");
sigemptyset(&to_stop);
sigsuspend(&to_stop);
printf("AFTER SIGSUSPEND");
fflush(stdout)
return 0;
}
Your child process should start with the signal you want to send
blocked.
Then you need to make sure:
a delivery of the signal won't kill the process (=> you need to set up a signal handler for it (an empty one will do))
the signal can be delivered (=> sigsuspend will do that)
In code:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handler(int X) { }
int main(void)
{
sigset_t to_stop;
sigemptyset(&to_stop);
sigaction(SIGINT /*you'd probably use SIGUSR1 here*/,
&(struct sigaction){ .sa_handler = handler }, 0);
puts("BEFORE");
/*SIGINT will now wake up the process without killing it*/
sigsuspend(&to_stop);
puts("AFTER");
}
You should be able to try this out with Ctrl+C. In real code, you probably should be using SIGUSR1/SIGUSR1 or one of the realtime signals for this.