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;
}
}
Related
Background
I'm trying to build a wrapper for the shell. Running in a TTY, it spawns the regular shell in a child process via forkpty. The intent is for all user input to be forwarded to the child process as-is, but to intercept the child's output and do some processing on it before copying it to the parent process' stderr. The user should be able to forget that the shell is wrapped at all, apart from the augmented output.
Problem
I can't figure out how to transparently forward the input. Here's the gist of my code currently (error checks and minor details omitted). It should compile with gcc <filename> -pthread -lutil:
#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <pty.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUF_SIZE 512
#define EOT "\x04" // ASCII end-of-transmission (i.e. 'EOF').
void * tty_input_routine(void * arg);
void tty_output_routine();
int parent_term_fd;
volatile sig_atomic_t got_sigchld = 0;
volatile sig_atomic_t got_sigwinch = 0;
// Listens for the child to exit, and causes the parent to exit.
void handle_sigchld(int sig) {
got_sigchld = 1;
}
// Listens for the parent to be resized, and causes the child to be resized.
void handle_sigwinch(int sig) {
got_sigwinch = 1;
}
void main() {
/* Block SIGWINCH and SIGCHLD. They are later unblocked via pselect in the main loop. */
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGWINCH);
sigaddset(&sigmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigmask, NULL);
/* Establish signal handlers. */
struct sigaction sig_action;
sig_action.sa_flags = 0;
sig_action.sa_handler = &handle_sigchld;
sigemptyset(&sig_action.sa_mask);
sigaction(SIGCHLD, &sig_action, NULL);
sig_action.sa_handler = &handle_sigwinch;
sigaction(SIGWINCH, &sig_action, NULL);
/* Get the initial terminal size. */
struct winsize term_sz;
ioctl(STDERR_FILENO, TIOCGWINSZ, &term_sz);
/* Turn off input echo in the child terminal since the parent should do that. */
struct termios term_ios;
tcgetattr(STDERR_FILENO, &term_ios);
term_ios.c_lflag &= ~(ECHO);
/* Do the fork. */
pid_t child_pid = forkpty(&parent_term_fd, NULL, &term_ios, &term_sz);
if (child_pid == 0) {
/* This is the child process. Execute the shell. */
char *const argv[] = { NULL };
execvp("/bin/bash", argv);
}
/* This is the parent process.
* Spawn a dedicated thread to forward input to the child PTY.
* The main thread will be used to process the output. */
pthread_t input_thread;
pthread_create(&input_thread, NULL, &tty_input_routine, NULL);
tty_output_routine(parent_term_fd);
}
void * tty_input_routine(void * arg) {
struct termios tcattr;
tcgetattr(STDIN_FILENO, &tcattr);
// cfmakeraw(&tcattr); // This doesn't seem to help.
// tcattr.c_lflag &= ~ICANON; // Neither does this...
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tcattr);
char buf[BUF_SIZE];
fd_set fds;
FD_ZERO(&fds);
while (true) {
FD_SET(STDIN_FILENO, &fds);
if (select(STDIN_FILENO + 1, &fds, NULL, NULL, NULL) == -1) {
if (errno == EINTR) {
continue; // A signal was caught; just try again.
}
// Otherwise, some error...
puts("THIS IS UNEXPECTED");
break;
} else {
ssize_t bytes = read(STDIN_FILENO, buf, BUF_SIZE);
if (bytes > 0) {
write(parent_term_fd, buf, (size_t)bytes);
} else if (bytes == 0) {
/* End of transmission? */
write(parent_term_fd, EOT, 1);
break;
}
}
}
return NULL;
}
void tty_output_routine() {
fd_set fds;
FD_ZERO(&fds);
sigset_t empty_sigmask;
sigemptyset(&empty_sigmask);
char buf[BUF_SIZE];
while (true) {
FD_SET(parent_term_fd, &fds);
if (pselect(parent_term_fd + 1, &fds, NULL, NULL, NULL, &empty_sigmask) == -1) {
if (errno == EINTR) {
/* A signal was caught. */
if (got_sigwinch) {
got_sigwinch = 0;
struct winsize term_sz;
ioctl(STDERR_FILENO, TIOCGWINSZ, &term_sz);
/* This sends SIGWINCH to the child. */
ioctl(parent_term_fd, TIOCSWINSZ, &term_sz);
}
if (got_sigchld) {
// This should run when the user does CTRL+D, but it doesn't...
puts("THIS IS THE PROPER EXIT");
return;
}
} else {
// Otherwise, some error...
break;
}
} else {
ssize_t bytes = read(parent_term_fd, buf, BUF_SIZE);
// (Omitted) do some processing on the buffer.
write(STDERR_FILENO, buf, (size_t)bytes);
}
}
}
The idea is that when the user hits CTRL+D, the input routine will read an empty buffer, and send EOT to the child, which will exit, causing SIGCHLD to fire in the parent, which will also exit. However, SIGCHLD is never raised in the parent, even though bash definitely exits as shown by the fact that it prints exit to the screen. Confusingly, SIGWINCH appears to be handled just fine.
Furthermore, the parent has trouble forwarding CTRL+C to the child. Even if I add another signal handler for SIGTERM and simply forward that signal to the child via kill, the shell itself exits, as opposed to whatever's running in the shell, as bash does normally. I'm not sure what to do differently here.
I've tried cfmakeraw and turning off canonical mode (ICANON) but this makes the program even more broken. Perhaps there are some other terminal attributes I'm missing?
It feels like I'm over-engineering this, since all I want to do is essentially trick the child process into accepting input as though it had no wrapping parent process. Do I really have to handle everything explicitly in the parent and manually forward user input and signals to the child? How can I do this in a way that the user can't tell that the shell is wrapped, apart from the augmented output?
I got a Synthasizer yesterday as a gift, and was interested in writing data to it. I got this much working, here is a program that scales through some notes.
Then I thought it would be neat to have it catch the Ctrl+C singal, and close.
The problem with just closing the file descriptor is that the MIDI device still processes the last note it was given, so I wrote the mute function, which tells the midi device to mute. That works.
so then I tried to have the signal handler mute the device before exiting, and I have been struggling ever since. The signal(SIGINT, intHandler); function wont take additional arguments. So I thought I would be clever, and write a function mySig that calls the signal function and takes the device file descriptor, and data pointer, and would be able to do one last write, before exiting.
IDK, that might even work, but mySig function, seems to be called from the start, and scaling never happens.
How can I call my mute function, before exiting the program with the signal function?
This is my first signal handing program, Im running linux, and the program is in C.
#include <sys/soundcard.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
static volatile int keepRunning = 1;
char* device = "/dev/midi1";
//function headers:
void mute(int fd, char *data);
void intHandler(int dummy);
void mySig(void (*intHandler)(int dummy), int fd, char *data);
int main(void){
unsigned int note=50;
char data[3] = {0x90, note, 33}; //device address, note, volume
int fd = open(device, O_WRONLY, 0);
if( fd < 0 ){
printf("Error: cannot open Synth %s\n", device);
exit(1);
}
signal(SIGINT, intHandler);
// mySig(intHandler,fd,data);
while(keepRunning){
for( note=30; note < 95; note++ ){
data[1]=note;//change note
write( fd, data, sizeof(data) );
usleep(100000);
if(note>=89){
note =30;
}
}
mute(fd,data); //mutes the data stream.
close(fd); // close device
return 0;
}
}
//functions:
void mute(int fd, char *data){
data[2]=0;//setVolume to 0
write(fd, data, sizeof(data));
close(fd);
}
void mySig(void (*intHandler)(int dummy), int fd, char *data){
printf("my Sig has been called\n");
mute(fd,data);
signal(SIGINT, intHandler);
}
void intHandler(int dummy) {
printf("my Sig has been called\n");
keepRunning = 1;
printf("ctrl+c was pressed, exiting\n");
usleep(10000);
exit(1);
}
Use the signal handler to only clear your keepRunning flag.
Personally, I prefer the opposite flag, as in done:
static volatile sig_atomic_t done = 0;
static void done_handler(int signum)
{
done = 1; /* Or, in Linux, done = signum. */
}
static int install_done(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = done_handler;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
If the user runs the program in a terminal, and they close the terminal unexpectedly, the program will receive a SIGHUP signal; Ctrl+C causes a SIGINT signal; and SIGTERM is often used to ask a program to exit. So, I personally like to do
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
early in my main().
All you need to do, is to have your loop -- in my case,
while (!done) {
/* Play notes or whatever */
}
and after the loop, mute the last note played, then close the device.
Consider the signal just a request to exit, as soon as is convenient; not a demand to exit immediately. It is expected that programs do necessary cleanup when they receive a signal asking them to exit. If one wants a program to exit right then, one can always kill the process with SIGKILL.
I am trying to manually interrupt the main thread of a program when it is blocked on a read() system call. I do this in a second thread with a call to pthread_kill() however a segmentation fault occurs. However if I place the call to read() in the scond thread, i.e. NOT the main thread and call pthread_kill() from the main thread then all works as expected.
For example, the following code results in a segmentation fault, where I call pthread_kill() in the second thread, approximatelt 2 seconds after it is started. It uses the pthread_t of the main thread obtained by a call (in the main thread) to pthread_self():
Example 1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
static int fd = 0;
unsigned char buf[255];
static pthread_t s;
void sigHandler(int sig){
printf("Signal handler called.\n");
}
void * closeFD(void *arg){
printf("Second thread started.\n");
sleep(2);
int r = pthread_kill(s, SIGUSR1);
}
int main(char *argv[], int argc){
struct termios newtio;
pthread_t t1;
unsigned char buf[255];
void *res;
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("Process id is: %d.\n", getpid());
fd = open("/dev/ttyS0", O_RDONLY | O_NOCTTY);
if (fd != -1){
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = B2400 | CS7 | CLOCAL | CREAD ;
newtio.c_iflag = ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ~ICANON;
newtio.c_cc[VMIN] = 14;
tcsetattr(fd,TCSANOW,&newtio);
pthread_create(&t1, NULL, closeFD, NULL);
printf("Reading ..\n");
read(fd,buf,255);
close(fd);
}
return 0;
}
The following code is the same except I place the call to read() in the second thread (in closeFD()) and works as expected. The second thread unblocks and terminates while the main thread waits for it to exit then exits itself.
Example 2:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
static int fd = 0;
unsigned char buf[255];
static pthread_t s;
void sigHandler(int sig){
printf("Signal handler called.\n");
}
void * closeFD(void *arg){
printf("Second thread started.\n");
read(fd,buf,255);
printf("Read interrupted.\n");
}
int main(char *argv[], int argc){
struct termios newtio;
pthread_t t1;
unsigned char buf[255];
void *res;
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("Process id is: %d.\n", getpid());
fd = open("/dev/ttyS0", O_RDONLY | O_NOCTTY);
if (fd != -1){
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = B2400 | CS7 | CLOCAL | CREAD ;
newtio.c_iflag = ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ~ICANON;
newtio.c_cc[VMIN] = 14;
tcsetattr(fd,TCSANOW,&newtio);
pthread_create(&t1, NULL, closeFD, NULL);
sleep(2);
int r = pthread_kill(t1, SIGUSR1);
pthread_join(t1, &res);
close(fd);
}
return 0;
}
So far I have not been able to find a specific reference stating that terminating the main thread from a second (within the same process) is an illegal operation, so is there something I am doing wrong?
UPDATE #1
Thanks for all those that have replied, however I should make a few points clear:
I am aware that using printf in the signal handler is unsafe however this is an example and it's not the cause of the segmentation fault, though it is a valid point. Taking the printf() out of the signal handler still results in a segmentation fault. Example 2 works with printf() either in or out of the signal handler.
I know sending a SIGUSR will not terminate the program. However by using the pthread_kill(pthread_t thread, int signal) it WILL send a signal to the thread thread and it will unblock (if indeed it is blocked). This is the action I desire, this is what actually happens in Example 2 and this is what I understand should happen in either example, but does not in example 1.
When describing example 1, I used the term 'method' when I meant 'thread', where I mention the call to pthread_kill().
Further, quoting from 'Programming with POSIX Threads', David R. Butenhof, section 6.6.3 p217 'pthread_kill':
Within a process, one thread can send a signal to a specific thread
(including itself) by calling pthread_kill.
With that said, the following example ALSO gives a segmentation fault:
Example 3
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <signal.h>
static pthread_t s;
int value = 0;
void sigHandler(int sig){
value = 1;
}
int main(char *argv[], int argc){
struct sigaction int_handler = {.sa_handler=sigHandler};
sigaction(SIGUSR1,&int_handler,0);
s = pthread_self();
printf("The value of 'value' is %d.\n", value);
printf("Process id is: %d.\n", getpid());
int r = pthread_kill(s, SIGUSR1);
printf("The value of 'value' is %d.\n", value);
return 0;
}
This also fails if instead of a call to sigaction() is replaced by the (non-portable) call to signal(). With the third example in mind, which is very simple, I am not able to find any documentation that expressly states it is an illegal action. In fact the quoted reference indicates it's allowable!
You forgot to #include <pthread.h>. That fixes your segfault for me in example #3 on a recent Linux system.
--- pthread_kill-self.c.orig 2015-01-06 14:08:54.949000690 -0600
+++ pthread_kill-self.c 2015-01-06 14:08:59.820998965 -0600
## -1,6 +1,6 ##
#include <stdio.h>
#include <string.h>
-#include <string.h>
+#include <pthread.h>
#include <signal.h>
and then...
$:- gcc -o pthread_kill-self pthread_kill-self.c -pthread
$:- ./pthread_kill-self
The value of 'value' is 0.
Process id is: 3152.
The value of 'value' is 1.
You're using printf(), which is not async-signal safe, and you're not initializing your struct sigaction properly (in particular, the signal mask is left undefined).
Third, sending a SIGUSR1 signal, with a handler installed, does not and should not terminate the main thread. You're just sending it a signal, that's all.
As Jens Gustedt mentioned in his comment to the original question, both of the programs have undefined behaviour. Therefore, I'm not going to try and guess exactly what part of the undefined behaviour causes the segmentation fault (in the first program).
Instead, I'll show you a working example.
For debugging/testing purposes, I like to start with async-signal safe output functions, based on write(2):
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#define MYSIGNAL SIGUSR1
#define SECONDS 10
static int wrstr(const int descriptor, const char *p, const char *const q)
{
while (p < q) {
ssize_t n;
n = write(descriptor, p, (size_t)(q - p));
if (n > (ssize_t)0)
p += n;
else
if (n != (ssize_t)-1)
return EIO;
else
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
return errno;
}
return 0;
}
static const char *ends(const char *s)
{
if (s)
while (*s != '\0')
s++;
return s;
}
static int wrout(const char *const p)
{
if (p != NULL && *p != '\0') {
int saved_errno, result;
saved_errno = errno;
result = wrstr(STDOUT_FILENO, p, ends(p));
errno = saved_errno;
return result;
} else
return 0;
}
static int wrouti(const int value)
{
char buffer[32];
char *p = buffer + sizeof buffer;
unsigned int u;
if (value < 0)
u = -(long)value;
else
u = value;
do {
*(--p) = '0' + (u % 10U);
u /= 10U;
} while (u > 0U);
if (value < 0)
*(--p) = '-';
return wrstr(STDOUT_FILENO, p, buffer + sizeof buffer);
}
static int wrerr(const char *const p)
{
if (p != NULL && *p != '\0') {
int saved_errno, result;
saved_errno = errno;
result = wrstr(STDERR_FILENO, p, ends(p));
errno = saved_errno;
return result;
} else
return 0;
}
The above functions are async-signal safe, and therefore okay to use in a signal handler. wrout() and wrerr() also retain errno unchanged, which is useful. Saving and restoring errno in a signal handler is usually omitted, by the way, although I do believe there are some odd corner cases it might matter. The wrouti() is just a crude decimal signed integer printer, also async-signal-safe, but it does not retain errno unchanged.
Next, let's define the signal handler itself, and an installer function for it. (I like to do it this way, to make the main() simpler.)
static volatile sig_atomic_t handled = 0;
static void handler(int signum)
{
wrerr("Signal received.\n");
handled = signum;
}
static int install_handler(const int signum)
{
struct sigaction act;
/* memset(&act, 0, sizeof act); */
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
The commented-out memset is recommended, but not required for proper operation. The sigemptyset(), however, is required, to clear the set of blocked signals.
Next, let's look at the thread function. You shouldn't use sleep(), as that interacts with signals; use POSIX.1-2001 nanosleep() instead.
static void *worker(void *target)
{
struct timespec duration, left;
int retval;
wrout("Worker started. Sleeping ");
wrouti((int)SECONDS);
wrout(" seconds...\n");
duration.tv_sec = SECONDS;
duration.tv_nsec = 0;
left.tv_sec = 0;
left.tv_nsec = 0;
while (1) {
retval = nanosleep(&duration, &left);
if (retval == 0)
break;
if (left.tv_sec <= 0 ||
(left.tv_sec == 0 && left.tv_nsec <= 0))
break;
duration = left;
left.tv_sec = 0;
left.tv_nsec = 0;
}
wrout("Sleep complete.\n");
if (target) {
wrout("Sending signal...\n");
retval = pthread_kill(*(pthread_t *)target, MYSIGNAL);
if (retval == 0)
wrout("Signal sent successfully.\n");
else {
const char *const errmsg = strerror(retval);
wrout("Failed to send signal: ");
wrout(errmsg);
wrout(".\n");
}
}
wrout("Thread done.\n");
return NULL;
}
The pointer given to the thread function should point to the thread identifier (pthread_t) the signal is directed to.
Note that above, nanosleep() can be interrupted by a signal delivery, if the signal is delivered to or caught by this particular thread. If that occurs, nanosleep() tells us how much time was left to sleep. The loop above shows how to make sure you sleep at least the specified time, even if interrupted by signal delivery.
Finally, the main(). Instead of opening a specific device, I use standard input. To reproduce OP's program, redirect standard input from /dev/ttyUSB0, i.e. ./program < /dev/ttyUSB0, when executing it.
int main(void)
{
pthread_t main_thread, worker_thread;
pthread_attr_t attrs;
struct termios original, settings;
int result;
if (!isatty(STDIN_FILENO)) {
wrerr("Standard input is not a terminal.\n");
return EXIT_FAILURE;
}
if (tcgetattr(STDIN_FILENO, &original) != 0 ||
tcgetattr(STDIN_FILENO, &settings) != 0) {
const char *const errmsg = strerror(errno);
wrerr("Cannot get terminal settings: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
settings.c_lflag = ~ICANON;
settings.c_cc[VMIN] = 14;
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings) != 0) {
const char *const errmsg = strerror(errno);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
wrerr("Cannot set terminal settings: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
wrout("Terminal is now in raw mode.\n");
if (install_handler(MYSIGNAL)) {
const char *const errmsg = strerror(errno);
wrerr("Cannot install signal handler: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
main_thread = pthread_self();
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 65536);
result = pthread_create(&worker_thread, &attrs, worker, &main_thread);
if (result != 0) {
const char *const errmsg = strerror(errno);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
wrerr("Cannot create a worker thread: ");
wrerr(errmsg);
wrerr(".\n");
return EXIT_FAILURE;
}
pthread_attr_destroy(&attrs);
wrout("Waiting for input...\n");
while (1) {
char buffer[256];
ssize_t n;
if (handled) {
wrout("Because signal was received, no more input is read.\n");
break;
}
n = read(STDIN_FILENO, buffer, sizeof buffer);
if (n > (ssize_t)0) {
wrout("Read ");
wrouti((int)n);
wrout(" bytes.\n");
continue;
} else
if (n == (ssize_t)0) {
wrout("End of input.\n");
break;
} else
if (n != (ssize_t)-1) {
wrout("read() returned an invalid value.\n");
break;
} else {
result = errno;
wrout("read() == -1, errno == ");
wrouti(result);
wrout(": ");
wrout(strerror(result));
wrout(".\n");
break;
}
}
wrout("Reaping the worker thread..\n");
result = pthread_join(worker_thread, NULL);
if (result != 0) {
wrout("Failed to reap worker thread: ");
wrout(strerror(result));
wrout(".\n");
} else
wrout("Worker thread reaped successfully.\n");
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
wrout("Terminal reverted back to original mode.\n");
return EXIT_SUCCESS;
}
Because it's much more fun to test using the terminal, the above tries hard to restore the terminal to its original state before returning.
Note that since the VMIN field in the termios structure is set to 14, the read() blocks until at least 14 bytes are available in the buffer. If a signal is delivered, a short count is returned if there is at least one byte in the buffer. Therefore, you cannot expect the read() to always return 14 bytes, and you cannot expect it to return -1 with errno == EINTR whenever a signal is delivered! Experimenting with this program is very useful, to clarify these in your mind.
I don't remember whether the USB serial drivers in Linux ever produce EPIPE or raise SIGPIPE, but that can definitely occur when using pipes. When using pipes, the most common reason is trying to read after read has already returned zero (end of input). Unless ignored or caught with a signal handler, the process dies much like in a segmentation fault, except that the cause is SIGPIPE signal instead of SIGSEGV. With terminal-like character devices, it depends on the driver, I seem to recall.
Finally, I wrote the above code under the weather (flu), so there might be bugs in tharrr. It should be POSIX.1 C99 code, and gcc -Wall -pedantic does not complain, but having a stuffed head, I'm not making any promises here. Fixes are more than welcome!
Questions? Comments?
I am trying to implement a basic event loop with pselect, so I have blocked some signals, saved the signal mask and used it with pselect so that the signals will only be delivered during that call.
If a signal is sent outside of the pselect call, it is blocked until pselect as it should, however it does not interrupt the pselect call. If a signal is sent while pselect is blocking, it will be handled AND pselect will be interrupted. This behaviour is only present in OSX, in linux it seems to function correctly.
Here is a code example:
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
int shouldQuit = 0;
void signalHandler(int signal)
{
printf("Handled signal %d\n", signal);
shouldQuit = 1;
}
int main(int argc, char** argv)
{
sigset_t originalSignals;
sigset_t blockedSignals;
sigemptyset(&blockedSignals);
sigaddset(&blockedSignals, SIGINT);
if(sigprocmask(SIG_BLOCK, &blockedSignals, &originalSignals) != 0)
{
perror("Failed to block signals");
return -1;
}
struct sigaction signalAction;
memset(&signalAction, 0, sizeof(struct sigaction));
signalAction.sa_mask = blockedSignals;
signalAction.sa_handler = signalHandler;
if(sigaction(SIGINT, &signalAction, NULL) == -1)
{
perror("Could not set signal handler");
return -1;
}
while(!shouldQuit)
{
fd_set set;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
printf("Starting pselect\n");
int result = pselect(STDIN_FILENO + 1, &set, NULL, NULL, NULL, &originalSignals);
printf("Done pselect\n");
if(result == -1)
{
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
perror("pselect failed");
}
}
else
{
printf("Start Sleeping\n");
sleep(5);
printf("Done Sleeping\n");
}
}
return 0;
}
The program waits until you input something on stdin, then sleeps for 5 seconds. To create the problem, "a" is typed to create data on stdin. Then, while the program is sleeping, an INT signal is sent with Crtl-C.
On Linux:
Starting pselect
a
Done pselect
Start Sleeping
^CDone Sleeping
Starting pselect
Handled signal 2
Done pselect
On OSX:
Starting pselect
a
Done pselect
Start Sleeping
^CDone Sleeping
Starting pselect
Handled signal 2
^CHandled signal 2
Done pselect
Confirmed that it acts that way on OSX, and if you look at the source for pselect (http://www.opensource.apple.com/source/Libc/Libc-320.1.3/gen/FreeBSD/pselect.c), you'll see why.
After sigprocmask() restores the signal mask, the kernel delivers the signal to the process, and your handler gets invoked. The problem here is, that the signal can be delivered before select() gets invoked, so select() won't return with an error.
There's some more discussion about the issue at http://lwn.net/Articles/176911/ - linux used to use a similar userspace implementation that had the same problem.
If you want to make that pattern safe on all platforms, you'll have to either use something like libev or libevent and let them handle the messiness, or use sigprocmask() and select() yourself.
e.g.
sigset_t omask;
if (sigprocmask(SIG_SETMASK, &originalSignals, &omask) < 0) {
perror("sigprocmask");
break;
}
/* Must re-check the flag here with signals re-enabled */
if (shouldQuit)
break;
printf("Starting select\n");
int result = select(STDIN_FILENO + 1, &set, NULL, NULL, NULL);
int save_errno = errno;
if (sigprocmask(SIG_SETMASK, &omask, NULL) < 0) {
perror("sigprocmask");
break;
}
/* Recheck again after the signal is blocked */
if (shouldQuit)
break;
printf("Done pselect\n");
if(result == -1)
{
errno = save_errno;
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
{
perror("pselect failed");
}
}
There are a couple of other things you should do with your code:
declare your 'shouldQuit' variable as volatile sig_atomic_t
volatile sig_atomic_t shouldQuit = 0;
always save errno before calling any other function (such as printf()), since that function may cause errno to be overwritten with another value. Thats why the code above aves errno immediately after the select() call.
Really, I strongly recommend using an existing event loop handling library like libev or libevent - I do, even though I can write my own, because it is so easy to get wrong.
fgetc() and other input functions can return when there's no data on the file descriptor. This can be simulated for console applications reading from stdin typing Ctrl-D on keyboard (at least on unix). But how to do it programmatically? For example, how to return from the fgetc() in the reader thread in the following code (NB: ignore the possible race condition)?
#include <pthread.h>
#include <stdio.h>
void* reader()
{
char read_char;
while((read_char = fgetc(stdin)) != EOF) {
;
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t thread;
pthread_create(&thread, NULL, reader, NULL);
// Do something so the fgetc in the reader thread will return
pthread_exit(NULL);
}
Thanks!
It seems you want a threads to stop blocking on fgetc(stdin) when some event occurs to handle that event instead. If that's the case you could select() on both stdin and some other message pipe so that the thread can handle input from both:
fd_set descriptor_set
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(pipefd, &descriptor_set);
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0)
{
// select() error
}
if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
// read byte from stdin
read(STDIN_FILENO, &c, 1);
}
if (FD_ISSET(pipefd, &descriptor_set))
// Special event. Do something else
Also note that only one thread in your process should be reading from stdin.
You can either 'close' standard input, or connect standard input to '/dev/null' ('NUL:' on Windows) with freopen(), or you can connect standard input to '/dev/zero'.
If you close it, every function call will fail because the file stream is not valid. If you connect it to the null data source, all reads will fail and return EOF immediately. If you connect it to the zero data source, every read will succeed and return a corresponding number of zero bytes.
It is possible one of those will suit your needs sufficiently. If not, then you probably need to give us a more detailed explanation of what you actually need.
With POSIX you can signal the thread whose primitives (e.g. "read") are blocking, and if you have set up a do-nothing signal handler with the SA_RESTART bit cleared then the primitives will fail with EINTR errors. Here's a working version of the original:
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static void SignalHandler(int signum)
{
}
void* reader(void *arg)
{
char read_char;
while((read_char = fgetc(stdin)) != EOF) {
;
}
printf("leaving reader\n");
return NULL;
}
int main(int argc, const char * argv[])
{
struct sigaction action;
memset(&action, 0, sizeof(action)); // SA_RESTART bit not set
action.sa_handler = SignalHandler;
sigaction(SIGUSR1, &action, NULL);
pthread_t thread;
pthread_create(&thread, NULL, reader, NULL);
sleep(1); // time to start reader thread
// Do something so the fgetc in the reader thread will return
pthread_kill(thread, SIGUSR1);
sleep(1); // time to exit reader thread; could join it if set up
return 0;
}
Alexandre posted the correct solution. His answer respond precisely to the question I asked. It follows simple self compiling code based on his hints:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
static int pipe_fds[2];
void* user_interaction()
{
char read_char;
fd_set descriptor_set;
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(pipe_fds[0], &descriptor_set);
while(1)
{
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) {
// select() error
}
if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
// read byte from stdin
read(STDIN_FILENO, &read_char, 1);
// Re-set the selected file descriptor so it can
// be signaled again
FD_SET(STDIN_FILENO, &descriptor_set);
}
if (FD_ISSET(pipe_fds[0], &descriptor_set))
// Special event. break
break;
}
pthread_exit(NULL);
}
int main(void)
{
pipe(pipe_fds);
pthread_t thread;
pthread_create(&thread, NULL, user_interaction, NULL);
// Before closing write pipe endpoint you are supposed
// to do something useful
sleep(5);
close(pipe_fds[1]);
pthread_join(thread, NULL);
pthread_exit(NULL);
}
I tried to use the code from this answer in a slightly modified form:
void *user_interaction()
{
char ch;
int rv;
fd_set set;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(pipe_fds[0], &set);
while (1)
{
rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
if (rv < 0)
{
printf(">>> select(): error occurred, %d\n", rv);
break;
}
if (FD_ISSET(pipe_fds[0], &set))
{
printf(">>> pipe_fds[0]: is ready\n");
break;
}
if (FD_ISSET(STDIN_FILENO, &set))
{
read(STDIN_FILENO, &ch, 1);
write(STDOUT_FILENO, &ch, 1);
}
}
pthread_exit(NULL);
}
but wasn't getting the expected behaviour. When executed like in the below:
$ echo -n 1 | ./a.out
my terminal was being rendered with 1's in the infinite loop and the pipe was never reported by select() to be ready (i.e. even after close()ing it in the main thread).
With some experimentation, I figured that you need to move FD_ZERO/FD_SET inside the loop, to get select() to work as desired:
void *user_interaction()
{
char ch;
int rv;
fd_set set;
while (1)
{
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(pipe_fds[0], &set);
rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
if (rv < 0)
{
printf(">>> select(): error occurred, %d\n", rv);
break;
}
if (FD_ISSET(pipe_fds[0], &set))
{
printf(">>> pipe_fds[0]: is ready\n");
break;
}
if (FD_ISSET(STDIN_FILENO, &set))
{
read(STDIN_FILENO, &ch, 1);
write(STDOUT_FILENO, &ch, 1);
}
}
pthread_exit(NULL);
}