I have a Client-Server application written in C language. This is not a professional poject, so everything I do is from command line (ubuntu terminal). Whenever the client code requires an input from the user, I use an fgets to take said input.
The problem is that if the user wants to stop the input process and exit the server by sending a CTRL+C signal (SIGINT), this won't work.
Specifically, what happens is this:
Client code asks for input and uses fgets to get one;
User presses CTRL+C while the client code is waiting for input;
Client code won't stop (if it stops, then the client code will send a specific kill signal to the server and shut the connection down);
Client will print on screen ^C;
If you try to press anything else, client code will just display it on the screen while you do the input, but if you press ENTER you won't send anything to the Server.
Therefore, the connection is stuck and the client is forever stopped in this input operation.
I don't know how to fix this. What i want is my client code to ask for input, if the user presses CTRL+C then the client code MUST stop and send such kill signal to the server. How can I do that?
I also tried with scanf like this:
while ((scanf("%s", buff)) == -1) EINTR_ABORT("Errore di scanf");
getchar();
(I know I should never use scanf, but for some reason my professor is fine with it -and I know is kinda sad but whatever-)
This is my fgets:
if(fgets(buff_ID, sizeof(buff_ID), stdin)== NULL)
EINTR_ABORT("Errore di fgets");
Where this is EINTR_ABORT:
#define ISTANT_ABORT(warning) ({ \
fprintf(stderr, "%s: %s.\n", warning, strerror(errno)); \
exit(EXIT_FAILURE);\
})
#define EINTR_ABORT(warning) ({ \
if(errno != EINTR) \
ISTANT_ABORT(warning); \
})
My professor never pointed this issue out, but he (obviously, because why not after all) expects us to solve it on our own in this project. I tried my best and never worked out anything that would actually fix the issue.
You can handle INT signal and set a flag signalling that user interrupted input.
#include <stdio.h>
#include <signal.h>
static sig_atomic_t interrupted = 0;
static void sigint_handler(int)
{
interrupted = 1;
}
int main(void)
{
struct sigaction act;
act.sa_handler = sigint_handler; // Set new SIGINT handler
sigemptyset(&act.sa_mask); // No need to mask signals in handler
act.sa_flags = 0; // Don't set SA_RESTART flag
if (sigaction(SIGINT, &act, NULL) != 0)
perror("sigaction");
char str[256];
char *ret = fgets(str, sizeof(str), stdin);
if (interrupted)
{
// Handle interrupt here
printf("Interrupted!\n");
} else if (ret)
{
// Handle fgets result here
}
return 0;
}
Note: don't use signal() function for setting up signal handler because on Linux it applies SA_RESTART flag.
Related
We have an assignment to create a game in C. We can't use any external headers, and it should run on console.
I thought it would be cool to have animations going on. We already have a walking character and everything (printing matrix, updating, clearing screen, reprinting...), but it would be nice to have things such as running water, for example. To do that, I'd need a way to keep the program running while waiting for an input. As it currently is, the animations would stop as the user is prompted for an input (and he'll constantly be asked for directions), so it wouldn't work.
I'm wondering if there'd be a way to keep the animation running while the input prompt is still going, maybe having a timeout for the input function and re-prompting every frame until the user starts typing. We're familiar with C, but not with any of the obscure functions and headers that could be used to work around this problem.
How can I keep the program running while waiting for input?
You can use the select to monitor a file or a group of file descriptors until they have input available. Depending on the structure of your program you could also use asynchronous input where a callback is called when I/O is signaled as ready for a given file descriptor.
The rough snippet below shows the necessary methodology to allow for the callback action_handler to be called when input becomes available on the target file description, which emittes SIGIO. This allows for the input to be processed when it arrives, if it arrives. Using a socket (lets assume UDP) you would have something similar to the following.
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
...
create and bind socket
...
/** Set sigaction to use sigaction callback and not handler */
act.sa_flags = SA_SIGINFO; // Enables sigaction instead of handler
act.sa_sigaction = action_handler; // Callback function
/** Set mask to ignore all signals except SIGIO during execution of handler */
sigfillset(&act.sa_mask); // Mask all
sigdelset(&act.sa_mask, SIGIO); // Clear SIGIO from mask
/** Set callback for SIGIO */
sigaction(SIGIO, &act, NULL)
/** Set socket io signals to async and make sure SIGIO is sent to current
* process when a TCP connection is made on the socket
* */
file_status = fcntl(socket_fd, F_GETFL); // Get current status
file_status |= O_ASYNC | O_NONBLOCK;
fcntl(socket_fd, F_SETFL, file_status); // Set modified status flags
fcntl(socket_fd, F_SETSIG, SIGIO); // Produce a SIGIO signal when i/o is possible
fcntl(socket_fd, F_SETOWN, getpid()); // Make sure SIGIO signal is sent to current process
The easiest thing to do is just to change stdin over to non blocking. This is done (on Linux and others) by using the fcntl() function to change the F_SETFL option on stdin.
That will make fgetc() (or any other read) no longer block returning right away. If there wasn't any thing to read (no input) it will return an error. If there is something it will return that character.
Here is the call you need to make to change stdin to non-blocking.
fcntl(0, F_SETFL, FNDELAY);
The first argument is the file handle. We pass in 0 which is stdin. The next argument is the option we want to change (in this case F_SETFL), and the 3rd argument is what to change it to (FNDELAY is non-blocking mode).
Here is a simple example program:
#include <stdio.h>
#include <fcntl.h>
void SetupIO(void);
void ShutDownIO(void);
int main(void)
{
long count;
char c;
SetupIO();
count=0;
for(;;)
{
printf("Counting %d\r",count);
count++;
c=fgetc(stdin);
if(c=='q')
break;
}
ShutDownIO();
return 0;
}
void SetupIO(void)
{
fcntl(0, F_SETFL, FNDELAY);
}
void ShutDownIO(void)
{
fcntl(0, F_SETFL, 0);
}
This is great. It no longer blocks, but it still echos the characters being typed and you need to press enter before you will get back any input from fgetc().
If we want to make it more game like we need to tell the terminal to stop messing with our input. We need to switch the terminal into RAW mode.
The following example turn the terminal to RAW mode and changes stdin to non-blocking.
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
void SetupIO(void);
void ShutDownIO(void);
struct termios orig_termios;
int main(void)
{
long count;
char c;
SetupIO();
count=0;
for(;;)
{
printf("Counting %d\r",count);
count++;
c=fgetc(stdin);
if(c=='q')
break;
}
ShutDownIO();
return 0;
}
void SetupIO(void)
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(ShutDownIO);
cfmakeraw(&new_termios);
new_termios.c_iflag|=INLCR; // CR=NL
new_termios.c_lflag|=ISIG; // We still want Ctrl-C
tcsetattr(0, TCSANOW, &new_termios);
/* Non-blocking */
fcntl(0, F_SETFL, FNDELAY);
}
void ShutDownIO(void)
{
tcsetattr(0, TCSANOW, &orig_termios);
fcntl(0, F_SETFL, 0);
}
The downside to this is that if you Ctrl-C or your program crashes it will leave your terminal in a messed up state that is not easy to get back out of. You can help with this by adding an atexit() and/or signal handling.
Tried my best to figure this out on my own, but I really do not want to continue tampering with things that I do not fully understand. So for a programming assignment I have to do in C, I need to terminate a program upon the user entering CTRL+D key stroke via a terminal. I tried to isolate that functionality in a smaller test function, but now my CTRL+D behaves as my CTRL+C and CTRL+C does not have any effect, even outside of the program when it finishes executing. This is the program that caused this change:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
printf("\n");
signal(SIGINT, SIG_DFL);
exit(0);
}
int main(){
signal(SIGINT, ctrlD);
while(1) {
printf("Hello\n");
sleep(5);
}
}
The line signal(SIGINT, SIG_DFL); was added afterward upon realizing my CTRL+C no longer worked. I thought it would return the keystrokes to their original functionalities, but to no avail. What do I do to get back the original functionalities while also making this program work with CTRL+D?
***EDIT: This question seems to have gone off the rails a bit. I get now that Ctrl+D is not a signal. Nonetheless, I no longer have the functionality of Ctrl+C anymore when attempting to use it in my MAC OS terminal, and instead Ctrl+D seems to have that exact functionality. HOW exactly can I return each to have the functionality that they had before I went on this haphazard journey?
If your intention is to restore signal's default behavior after executing handler then, pass SA_RESETHAND flag to sa_flags while registering signal action. For example.
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_flags = SA_RESETHAND;
act.sa_handler = some_handler;
sigaction(SIGINT, &act, NULL);
From sigaction() man
SA_RESETHAND
Restore the signal action to the default upon entry to the signal handler. This flag is meaningful only when
establishing a signal handler.
If you write a program to explore signals, it is much better to write it carefully, using proper POSIX interfaces (sigaction() instead of signal()), and avoiding undefined behaviour (using non-async-signal safe functions in a signal handler).
Consider, for example, the following program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
static volatile sig_atomic_t sigint_count = 0;
static void catch_sigint(int signum)
{
if (signum == SIGINT)
sigint_count++;
}
static int install_sigint(void)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sigint;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) == -1)
return errno;
return 0;
}
static int install_default(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
struct timespec duration;
int result;
if (install_sigint()) {
fprintf(stderr, "Cannot install SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
duration.tv_sec = 5;
duration.tv_nsec = 0; /* 1/1000000000ths of a second. Nine zeroes. */
printf("Sleeping for %d seconds.\n", (int)duration.tv_sec);
fflush(stdout);
while (1) {
result = nanosleep(&duration, &duration);
if (!result)
break;
if (errno != EINTR) {
fprintf(stderr, "nanosleep() failed: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* nanosleep was interrupted by a delivery of a signal. */
if (sigint_count >= 3) {
/* Ctrl+C pressed three or more times. */
if (install_default(SIGINT) == -1) {
fprintf(stderr, "Cannot revert SIGINT to the default handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("SIGINT has been reverted to the default handler.\n");
fflush(stderr);
}
}
if (sigint_count > 0)
printf("You pressed Ctrl+C %d time%s.\n", (int)sigint_count, (sigint_count > 1) ? "s" : "");
else
printf("You did not press Ctrl+C at all.\n");
return EXIT_SUCCESS;
}
The #define tells your C library (glibc in particular) that you want POSIX.1-2008 (and later) features from it.
The INT signal handler only increments a volatile sig_atomic_t counter. Note that this type may have a very small range it can represent; 0 to 127, inclusive, should be safe.
The main program waits using the POSIX nanosleep() function. On some systems, sleep() may be implemented via the SIGALRM function, so it is better avoided when using signals otherwise; nanosleep() does not interfere with signals like that at all. Plus, nanosleep() can return the amount of time remaining, if it is interrupted by a signal delivery.
In the main loop, nanosleep() will return 0, if it has slept the entire interval (but note that it may not update the remaining time to 0 in this case). If it is interrupted by the delivery of a signal, it will return -1 with errno == EINTR, and the remaining time updated. (The first pointer is to the duration of the sleep, and the second is to where the remaining time should be stored. You can use the same structure for both.)
Normally, the main loop does only one iteration. It can do more than one iteration, if it is interrupted by the delivery of a signal.
When the main loop detects that sigint_count is at least three, i.e. it has received at least three INT signals, it resets the signal handler back to default.
(Note that both the memset() and the sigemptyset() are important when clearing the struct sigaction structure. The memset() ensures that future code is backwards compatible with older code, by ensuring even padding fields are cleared. And sigemptyset() is the safe way to clear the signal mask (set of signals blocked while the handler runs).)
(In theory, memset() is not async-signal-safe, while both sigemptyset() and sigaction() are. This is why I reset the signal handler in the main program, and not in the signal handler.)
If you want to print from a signal handler, you need to use low-level I/O, because <stdio.h> functions are not async-signal safe. For example, you can use the following function to print strings to standard output:
static int wrerr(const char *p)
{
const int saved_errno = errno;
int retval = 0;
if (p) {
const char *q = p;
ssize_t n;
while (*q)
q++;
while (p < q) {
n = write(STDERR_FILENO, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n != -1) {
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
}
errno = saved_errno;
return retval;
}
The above wrerr() function is async-signal safe (because it only uses async-signal safe functions itself), and it even keeps errno unchanged. (Many guides forget to mention that it is quite important for a signal handler to keep errno unchanged. Otherwise, when a function is interrupted by a signal handler, and that signal handler modifies errno, the original function will return -1 to indicate an error, but then errno is no longer EINTR!)
You can just use wrerr("INT signal!\n") if you want. The return value from wrerr() is zero if the write was successful, and an errno error code otherwise. It ignores interrupts itself.
Do note that you should not mix stderr output via fprintf() or other <stdio.h> functions with the above (except perhaps for printing error messages when the program aborts). Mixing them is not undefined behaviour, it just may yield surprising results, like wrerr() output appearing in the midst of a fprintf(stderr,...) output.
Its because of exit(0) statement in the handler, when SIGINT is raised, handler strlD gets called and you might thinking why signal(SIGINT,SIG_DFL) didn't work ? Actually it works. But your main process a.out get terminated successfully there itself by calling exit(0). remove exit(0) if you want to restore the behavior of SIGINT.
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
//printf("CTRL+C pressed\n");/* just to observe I added one printf
statement, Ideally there shouldn't be any printf here */
signal(SIGINT, SIG_DFL);/*restoring back to original action */
}
int main(){
signal(SIGINT, ctrlD);/*1st time when CTRL+C pressed, handler ctrlD gets called */
while(1) {
printf("Hello\n");
sleep(5);
}
return 0;
}
Also its advisable to use sigaction() instead of signal() as told here What is the difference between sigaction and signal? . Read man 2 sigaction and man 2 exit to check what exit(0) means.
Also this How to avoid using printf in a signal handler?
Edit :
void ctrlD(int sig){
/* printf("CTRL+C pressed \n"); */
signal(SIGINT, SIG_DFL); /* only one time CTRL+C works
after that SIG_DFL will terminate whole process */
}
int main(){
signal(SIGINT, ctrlD); /* if you press CTRL+C then it will go to handler
and terminate */
int ch;
while( ((ch = getchar())!=EOF) ) { /* wait or read char until CTrl+D is not pressed */
printf("Hello : %d \n",ch);/* ASCII equivalent of char */
}
return 0;
}
Thank you everyone who contributed to this question. The resources provided/linked were tremendously helpful in learning more about signals (and that EOF isn't a signal), among the other wealth of information provided.
After some more research, I found out that somehow, either through some accidental bash command gone awry, or perhaps the program posted in my original question itself, I had altered the key mappings for my terminal's stty settings. If anyone finds themselves in this oddly specific situation in the future, I hope this can be of help, as it is what fixed my problem:
Enter the command $ stty -a to see all of your terminals settings, specifically the "cchars" section.
I then saw the reversal, and fixed it like so:
$ stty intr ^C
$ stty eof ^D
Then you can run $ stty -a once again to see that the changes have properly taken effect. Once again, thanks everyone.
I haven't found a post addressing a similar scenario so far: In a command-line unix program for sonification of certain floating point data sets, I have a main() which either reads from a file whose name is given as program argumant or stdin redirection ( < ) or data piped from another program ( | ), depending on argc.
For as long as argv[1] is a file name, I can manage gently stopping the execution by hitting 'enter' key, which programmatically sets a condition to fade out the signal, cleanup and regularly exit the audio subroutine and terminate the program. I achieve it by calling a function for non-blocking kbhit(). Definition:
int kbhit(void)
{
struct timeval tv;
fd_set read_fd;
tv.tv_sec = 0;
tv.tv_usec= 0;
FD_ZERO(&read_fd);
FD_SET(0, &read_fd);
if(select(1, &read_fd, NULL, NULL, &tv) == -1)
return 0;
if(FD_ISSET(0, &read_fd))
return 1;
return 0;
}
However, if input is being redirected or piped, kbhit() then prevents from execution by stopping the playback the moment it has started, since it returns 1. I don't know how to deal with it, so I am using SIGINT, which I don't find so elegant. Here are the code snippets:
//... ...
bool stop = false; //global variable
//... ...
if(argc == 1) stdr = true;//flag: reading from redirected or piped stdin (simplified)
//code for reading from file or stdin, using fscanf() ...
//code for initializing and starting audio process ...
//loop, during audio callback on a separate thread ...
while((player.totalFrames < player.totalSize) && !stop){
if(!stdr){ //( = reading from file)
if (kbhit()){
stop = true;
break;
}
}else{ //( = reading from redirected stdin)
if (signal(SIGINT, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGINT\n");
}
}
if(stop){ … }//code for gently fading the signal and stopping the audio process
//cleanup and termination...
//... ...
And here is the standard signal handler which sets stop condition to true:
void sig_handler(int s)
{
if (s == SIGINT){
if(debug)printf("\ncaught SIGINT\n");
stop = true;
}
}
Is there a more elegant way to:
send & catch a message from the keyboard while having input redirected or piped, to allow program cleaning after itself and exiting (other than using SIGINT as described above)?
how would a code allowing for such a message from coputer keyoard (during redirection or piping) look like? Could the arguments inside kbhit() be rearranged somehow, or one has to go in a completely different direction. I have no idea which?
If this is possible, I would kindly appreciate a suggestion. Thanks in advance!
If you want an interactive TTY user to be able to stop a program using an input character (not a signaling character which causes the TTY driver to deliver a signal), and that program must be redirected, then your only resort is to explicitly open a file descriptor to the TTY device (via /dev/tty) and monitor that for input.
Your kbhit function should probably take an int fd parameter, using which you give it the open TTY file descriptor that it should poll.
If you want a single character input from a TTY to be immediately available to the program (even if that character isn't a newline), then you have to put the TTY into "raw mode", or at least partially: at the very least, you have to disable "canonical input processing" via negating the ICANON flag in the c_iflag numeric field in the struct termios structure, and ensure that c_cc[VTIME] and c_cc[VMIN] are set to 0 and 1, respectively. Look up tcgetattr and tcsetattr. You probably want to disable the signaling characters, and disable echoing and other things. Some platforms have a cfmakeraw function which tweaks a struct termios object in the right ways to bring about a very raw mode without having to fiddle with any of the termios flags.
In canonical input processing mode, a TTY won't make input available to the process (and won't select positive under select for input) until a complete line of input is received.
Your problem is that, when reading data from redirection, you're trying to use both the file AND your keyboard. stdin isn't your keyboard anymore, it is the file, and thus you cannot read data from your keyboard (like the "enter" key pressed).
If I'm not mistaken, your only two options are signals and opening a new file descriptor to the keyboard input (the TTY).
I've a client/server program, now I want to handle signals. When the client closes the connection (if for example I close the terminal), the server has to handle a SIGPIPE, am I right? I'd like to implement something like this. Is it possible?
server.c:
void function(){
printf("...");
read(socket,buff,size);
//IF THE CLIENT CLOSES, THE SERVER RECEIVES A SIGPIPE
...the resting part of the scheduled code should be ignored if sigpipe is received, and the program should begin from where I wrote on the handler of the sigpipe...
printf("not working"); //this should be ignored, but it's printed 2 times immediatly, and when I've finished the actions indicated in the function by the handler, it prints it another time, because the program counter restarts from here...
}
void sigpipehandler(){
close(socket);
main(); //I'd like that the program restarts from the main when I've received a SIGPIPE. It restarts from the main, but only after having printed "not working" two times...
}
int main(){
sigPipe.sa_sigaction = &sigpipehandler;
sigPipe.sa_flags = SA_SIGINFO;
sigaction(SIGPIPE, &sigpipehandler, NULL);
...code...
}
Converting comments into an answer.
Note that you only get SIGPIPE when you write to a pipe where there is no process with the read end of the pipe open. You get EOF (zero bytes read) when you read from a pipe that has no process with the write end of the pipe open.
So, if I change the read() with a write() in the example. How can I handle the SIGPIPE?
Simplest is to ignore SIGPIPE (signal(SIGPIPE, SIG_IGN)) and then monitor the return value from write(). If it comes back with -1 and errno set to EINTR, you can assume you got interrupted by some signal, and most probably a SIGPIPE, especially if you don't have any other signal handling set. Of course, you should be looking at the return value from write() — and read() — anyway.
Alternatively, if you want an explicit SIGPIPE handler, then you definitely do not want to recursively call main() from your signal handler. You can write a loop in main(), and have the signal handler set a flag which you test in the loop. Per Standard C, about the only thing you can do in a signal handler is modify a variable or exit.
static volatile sigatomic_t sig_recvd = 0;
static int sock_fd = -1;
void sigpipehandler(int signum)
{
close(sock_fd);
sock_fd = -1;
sig_recvd = signum;
}
int main(void)
{
sigPipe.sa_sigaction = &sigpipehandler;
sigPipe.sa_flags = SA_SIGINFO;
sigemptyset(&sigPipe.sa_mask);
sigaction(SIGPIPE, &sigpipehandler, NULL);
int done = 0;
while (!done)
{
if (sock_fd == -1)
{
if (sig_recvd != 0)
{
...report signal received...
sig_recvd = 0;
}
...(re)open socket on sock_fd...
}
...code as before - sets done = 1 when loop should terminate...
}
return 0;
}
Note that naming a variable the same as a system call (socket in your code) is treading on thin ice; hence, I renamed it sock_fd. A global variable called socket would be a really bad idea.
I have a C program with two threads: the main thread continously reads data from the network and prints it out to the screen, while the secondary thread listens for and handles keypresses from standard input.
Currently my program catches SIGINT, SIGTERM, and SIGPIPE in order to terminate the program cleanly. My problem is that at the end of the main thread (once the main loop has terminated from the signal handler), it attempts to revert the terminal settings using tcsetattr, however this blocks until the current fgetc call on the other thread returns.
How can I interrupt the background thread so that the fgetc call returns and the main thread can restore the terminal settings and exit cleanly?
I have tried using pthread_kill(thread, SIGINT) but that just causes my existing signal handler to be called again.
Relevant code:
// If the program should still be running.
static sig_atomic_t running = 1;
// Background thread that reads keypresses.
pthread_t thread;
static void *get_keypresses();
static void receive_signal(int signal) {
(void)signal;
running = 0;
}
int main(int argc, char *argv[]) {
// Set up signal handling.
if(signal(SIGINT, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGINT.\n");
}
if(signal(SIGTERM, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGTERM.\n");
}
if(signal(SIGPIPE, receive_signal) == SIG_ERR) {
fprintf(stderr, "Error setting signal handler for SIGPIPE.\n");
}
// Set up thread attributes.
pthread_attr_t thread_attrs;
if(pthread_attr_init(&thread_attrs) != 0) {
perror("Unable to create thread attributes");
exit(2);
}
if(pthread_attr_setdetachstate(&thread_attrs, PTHREAD_CREATE_DETACHED) != 0) {
perror("Unable to set thread attributes");
exit(2);
}
// Set up terminal for reading keypresses.
struct termios orig_term_attr;
struct termios new_term_attr;
tcgetattr(fileno(stdin), &orig_term_attr);
memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
new_term_attr.c_lflag &= ~(ECHO|ICANON);
tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
// Start background thread to read keypresses.
if((pthread_create(&thread, &thread_attrs, &get_keypresses, NULL)) != 0) {
perror("Unable to create thread");
exit(2);
}
// Main loop.
while(running) {
// Read data from network and output to screen.
}
// Restore original terminal attributes. ***IT BLOCKS HERE***
tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
return 0;
}
// Get input from the keyboard.
static void *get_keypresses() {
int c;
while(running) {
// Get keypress. ***NEEDS TO BE INTERRUPTED HERE***
if((c = fgetc(stdin)) != - 1) {
// Handle keypress.
}
}
return NULL;
}
There are two ways to go:
you change your code not to block (using O_NONBLOCK, poll(), select(), etc.),
you force your blocked code to get kicked out of the blocking system call.
Forcing the end of a system call can basically be done in two ways: either you make the system call impossible to finish (for example, you close the file descriptor the blocking call waits on), or you send some signal to that thread, and make sure that the signal handling is properly set up. The proper signal handling means that the signal is not ignored, not masked, and that the signal flags are set up in such a way that the system call does not get restarted after the signal handling (see sigaction(), SA_RESTART flag).
Replace
if((c = fgetc(stdin)) != -1)
with
if (0 < read(fileno(stdin), &c, 1))
read() would be interupted if the thread received a signal.
I managed to find a solution that works well for me: I made reading from standard input non-blocking.
fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
This also requires some form of sleep (or usleep or nanosleep) in the background thread.
Thanks for HapKoM for getting me thinking in the right direction.
One way that I see is to read stdin by poll() or select() with some small timeout. And when it returns on timeout you can check "running" flag and perform clear termination if it is set to "false" value.
May be it is not the best solution, but it should work.