I would like to stuff an 'A' character back into stdin using ungetc on receipt of SIGUSR1. Imagine that I have a good reason for doing this.
When calling foo(), the blocking read in stdin is not interrupted by the ungetc call on receipt of the signal. While I didn't expect this to work as is, I wonder if there is a way to achieve this - does anyone have suggestions?
void handler (int sig)
{
ungetc ('A', stdin);
}
void foo ()
{
signal (SIGUSR1, handler);
while ((key = fgetc (stdin)) != EOF)
{
...
}
}
Rather than try to get ungetc() to unblock a blocking fgetc() call via a signal, perhaps you could try not having fgetc() block to begin with and wait for activity on stdin using select().
By default, the line discipline for a terminal device may work in canonical mode. In this mode, the terminal driver doesn't present the buffer to userspace until the newline is seen (Enter key is pressed).
To accomplish what you want, you can set the terminal into raw (non-canonical) mode by using tcsetattr() to manipulate the termios structure. This should case the blocking call to fgetc() to immediately return the character inserted with ungetc().
void handler(int sig) {
/* I know I shouldn't do this in a signal handler,
* but this is modeled after the OP's code.
*/
ungetc('A', stdin);
}
void wait_for_stdin() {
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fileno(stdin),&fdset);
select(1, &fdset, NULL, NULL, NULL);
}
void foo () {
int key;
struct termios terminal_settings;
signal(SIGUSR1, handler);
/* set the terminal to raw mode */
tcgetattr(fileno(stdin), &terminal_settings);
terminal_settings.c_lflag &= ~(ECHO|ICANON);
terminal_settings.c_cc[VTIME] = 0;
terminal_settings.c_cc[VMIN] = 0;
tcsetattr(fileno(stdin), TCSANOW, &terminal_settings);
for (;;) {
wait_for_stdin();
key = fgetc(stdin);
/* terminate loop on Ctrl-D */
if (key == 0x04) {
break;
}
if (key != EOF) {
printf("%c\n", key);
}
}
}
NOTE: This code omits error checking for simplicity.
Clearing the ECHO and ICANON flags respectively disables echoing of characters as they are typed and causes read requests to be satisfied directly from the input queue. Setting the values of VTIME and VMIN to zero in the c_cc array causes the read request (fgetc()) to return immediately rather than block; effectively polling stdin. This causes key to get set to EOF so another method for terminating the loop is necessary. Unnecessary polling of stdin is reduced by waiting for activity on stdin using select().
Executing the program, sending a SIGUSR1 signal, and typing
t e s t results in the following output1:
A
t
e
s
t
1) tested on Linux
It is not entirely clear what your goal is, but is this what you are looking for?
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
int handle = 0;
void handler (int sig) {
handle = 1;
}
void foo () {
int key;
signal (SIGUSR1, handler);
while ((key = fgetc (stdin)) != EOF) {
printf("%c\n",key);
if (handle) {
handle = 0;
ungetc('A',stdin);
}
}
}
int main(void) {
printf("PID: %d\n",getpid());
foo();
}
It produces this output:
PID: 8902
test (typed on stdin)
t
A
e
s
t
FILE*s are not async safe.
You cannot operate on a FILE* in a signal handler while someone else also uses that same FILE*. functions you can all in a signal handler is stated here:
http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html . (It might be
different on a windows machine, but still any FILE* are not safe there either.
This is essentially the same as #Jamie's answer, slightly changed to support your desire to process the A before the t, but it's too hard to type code into a comment box, so I've posted this answer separately.
int insert_an_A = 0;
void handler(int sig) { insert_an_A = 1; }
void process_char(char c) { ... }
int main(int argc, char **argv) {
int c;
/* skip signal setup */
while ((c = fgetc(stdin)) != EOF) {
if (insert_an_A) {
process_char('A');
insert_an_A = 0;
}
process_char(c);
}
}
If you want to process an handler received during the call to fgetc that returns EOF, you should also check insert_an_A after exiting the while loop.
Note also that in general the best practice for signal handlers is to set a global variable and return from the handler. Elsewhere in your program, look for that variable changing and react appropriately.
Related
I am writing my own shell and I want to stop the user from exiting the shell with CTRL+D. Below is how I am checking for EOF and I am able to catch the CTRL+D:
However, my loop goes into infinite and does prints please use _exit over and over again.
How could I stop this from doing so?
Thanks
OK, this seems to work:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
void disable_veof(void) {
struct termios t;
int r;
r = tcgetattr(STDIN_FILENO, &t);
if(r)
exit(EXIT_FAILURE);
t.c_cc[VEOF] = _POSIX_VDISABLE;
r = tcsetattr(STDIN_FILENO, TCSANOW, &t);
if(r)
exit(EXIT_FAILURE);
}
void echo_lines(void) {
char buffer[4096];
const size_t buffer_len = sizeof buffer;
ssize_t bytes;
while( 0 != (bytes = read(STDIN_FILENO, buffer, buffer_len)) ) {
bytes = write(STDOUT_FILENO, buffer, bytes);
if(bytes <= 0)
exit(EXIT_FAILURE);
}
}
int main() {
disable_veof();
echo_lines();
return EXIT_SUCCESS;
}
If the input is a terminal in canonical mode, EOF in raw io (as opposed to stdio where it is) is not sticky.
https://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap11.html#tag_11_01_09
Special character on input, which is recognized if the ICANON flag is
set. When received, all the bytes waiting to be read are immediately
passed to the process without waiting for a newline, and the EOF is
discarded. Thus, if there are no bytes waiting (that is, the EOF
occurred at the beginning of a line), a byte count of zero shall be
returned from the read(), representing an end-of-file indication. If
ICANON is set, the EOF character shall be discarded when processed.
The next read() should return a positive value unless the user keeps pressing Ctrl+D.
I am writing a simple shell program in c, and need to handle ctrl-c.
If a foreground process is running, I need to terminate it and continue the main shell loop. If not, I need to do nothing but print that the signal was caught.
Below is my code, based on this thread: Catch Ctrl-C in C
void inthandler(int dummy){
signal(dummy, SIG_IGN);
printf("ctrl-c caught\n");
}
and I call signal() right before entering my main loop
int main(int argc, char*argv[]){
signal(SIGINT, inthandler)
while(true){
//main loop
}
}
As of now, I am able to intercept ctrl-c and print my intended message, but any further input results in a segfault.
How can I return to execution of my main loop after I enter inthandler?
Use sigaction(), not signal(), except when setting the disposition to SIG_DFL or SIG_IGN. While signal() is specified in C, and sigaction() POSIX.1, you'll need POSIX to do anything meaningful with signals anyway.
Only use async-signal safe functions in signal handlers. Instead of standard I/O (as declared in <stdio.h>), you can use POSIX low-level I/O (read(), write()) to the underlying file descriptors. You do need to avoid using standard I/O to streams that use those same underlying descriptors, though, or the output may be garbled due to buffering in standard I/O.
If you change the signal disposition in the signal handler to ignored, only the first signal (of each caught type) will be caught.
Consider the following example program:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
/* Helper function: Write a string to a descriptor, keeping errno unchanged.
Returns 0 if success, errno error code if an error occurs. */
static inline int wrs(const int fd, const char *s)
{
/* Invalid descriptor? */
if (fd == -1)
return EBADF;
/* Anything to actually write? */
if (s && *s) {
const int saved_errno = errno;
const char *z = s;
ssize_t n;
int err = 0;
/* Use a loop to find end of s, since strlen() is not async-signal safe. */
while (*z)
z++;
/* Write the string, ignoring EINTR, and allowing short writes. */
while (s < z) {
n = write(fd, s, (size_t)(z - s));
if (n > 0)
s += n;
else
if (n != -1) {
/* This should never occur, so it's an I/O error. */
err = EIO;
break;
} else
if (errno != EINTR) {
/* An actual error occurred. */
err = errno;
break;
}
}
errno = saved_errno;
return err;
} else {
/* Nothing to write. NULL s is silently ignored without error. */
return 0;
}
}
/* Signal handler. Just outputs a line to standard error. */
void catcher(int signum)
{
switch (signum) {
case SIGINT:
wrs(STDERR_FILENO, "Caught INT signal.\n");
return;
default:
wrs(STDERR_FILENO, "Caught a signal.\n");
return;
}
}
/* Helper function to install the signal handler. */
int install_catcher(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = catcher;
act.sa_flags = SA_RESTART; /* Do not interrupt "slow" functions */
if (sigaction(signum, &act, NULL) == -1)
return -1; /* Failed */
return 0;
}
/* Helper function to remove embedded NUL characters and CRs and LFs,
as well as leading and trailing whitespace. Assumes data[size] is writable. */
size_t clean(char *data, size_t size)
{
char *const end = data + size;
char *src = data;
char *dst = data;
/* Skip leading ASCII whitespace. */
while (src < end && (*src == '\t' || *src == '\n' || *src == '\v' ||
*src == '\f' || *src == '\r' || *src == ' '))
src++;
/* Copy all but NUL, CR, and LF chars. */
while (src < end)
if (*src != '\0' && *src != '\n' && *src != '\r')
*(dst++) = *(src++);
else
src++;
/* Backtrack trailing ASCII whitespace. */
while (dst > data && (dst[-1] == '\t' || dst[-1] == '\n' || dst[-1] == '\v' ||
dst[-1] == '\n' || dst[-1] == '\r' || dst[-1] == ' '))
dst--;
/* Mark end of string. */
*dst = '\0';
/* Return new length. */
return (size_t)(dst - data);
}
int main(void)
{
char *line = NULL;
size_t size = 0;
ssize_t len;
if (install_catcher(SIGINT)) {
fprintf(stderr, "Cannot install signal handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("Type Ctrl+D, 'exit', or 'quit' on an empty line to exit.\n");
while (1) {
len = getline(&line, &size, stdin);
if (len < 0)
break;
clean(line, len);
printf("Read %zd chars: %s\n", len, line);
if (!strcmp(line, "exit") || !strcmp(line, "quit"))
break;
}
return EXIT_SUCCESS;
}
On most POSIXy systems, Ctrl+C in a pseudoterminal also clears the current line buffer, so pressing it in the middle of interactively supplying a line will discard the line (the data not sent to the process).
Note that the ^C you normally see in pseudoterminals when you press Ctrl+C is a terminal feature, controlled by the ECHO termios setting. That setting also controls whether keypresses in general are echoed on the terminal.
If you add signal(SIGINT, SIG_IGN) to catcher(), just after the wrs() line, only the first Ctrl+C will print "Caught INT signal"; any following Ctrl+C will just discard the the incomplete line.
So, if you type, say, FooCtrl+CBarEnter, you'll see either
Foo^CCaught INT signal.
Bar
Read 4 chars: Bar
or
Foo^CBar
Read 4 chars: Bar
depending on whether the INT signal generated by Ctrl+C is caught by the handler, or ignored, at that point.
To exit, type exit or quit at the start of a line, or immediately after a Ctrl+C.
There are no segmentation faults here, so if your code does generate one, it must be due to a bug in your program.
How can I return to execution of my main loop after I enter inthandler?
Signal delivery interrupts the execution for the duration of executing the signal handler; then, the interrupted code continues executing. So, the strictly correct answer to that question is by returning from the signal handler.
If the signal handler is installed with the SA_RESTART flag set, then the interrupted code should continue as if nothing had happened. If the signal handler is installed without that flag, then interrupting "slow" system calls may return an EINTR error.
The reason errno must be kept unchanged in a signal handler -- and this is a bug many, many programmers overlook -- is that if an operation in the signal handler changes errno, and the signal handler gets invoked right after a system or C library call failed with an error code, the errno seen by the code will be the wrong one! Granted, this is a rare case (the time window where this can occur is tiny for each system or C library call), but it is a real bug that can occur. When it does occur, it is the kind of Heisenbug that causes developers to rip out their hair, run naked in circles, and generally go more crazy than they already are.
Also note that stderr is only used in the code path where installing the signal handler fails, because I wanted to be sure not to mix I/O and POSIX low-level I/O to standard error.
I want to output the ASCII Code of the last key I pressed, every x second.
As example:
If i press a(97), the terminal should show the 97 every x second. When I now press the w(119), the program now should print the 119 instead of the 97.
So far my program just prints the first key I've pressed.
Here are the main and the other method:
int main(int argc, char const *argv[]){
printf("Hello World!");
while(1){
movePlayer();
fflush(stdout);
sleep(1);
}
return 0;
}
void movePlayer(){
system("/bin/stty raw");
int input = getchar(); //support_readkey(1000);
//fprintf(stdout, "\033[2J");
//fprintf(stdout, "\033[1;1H");
printf("\b%d",input);
system("/bin/stty cooked");
}
EDIT:
With a little bit of testing i've now a method which solves my problem
int read_the_key(int timeout_ms) {
struct timeval tv = { 0L, timeout_ms * 1000L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
int r = select(1, &fds, NULL, NULL, &tv);
if (!r) return 0;
return getchar();
}
getchar() waits for only one character, so this:
while(1){
movePlayer(); // getchar() and printf() here
fflush(stdout);
sleep(1);
}
causes this behavior. You read one character, you print it in movePlayer(). Then you flush the output buffer and go to sleep. Then you just repeat, which means that you have to input again.
Store the input and print it again, if you wish. However, your function will always wait for new input to arrive.
Here is an attempt with read() as suggested, but it will have similar behavior to your code as it is now:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int old_c = -1;
char c[1] = {0};
void movePlayer();
int main(int argc, char const *argv[]){
while(1) {
movePlayer();
fflush(stdout);
sleep(1);
}
return 0;
}
void movePlayer(){
system("/bin/stty raw");
if(read(STDIN_FILENO, c, sizeof(c)) > 0)
old_c = (int)c[0];
if(old_c == -1)
old_c = (int)c[0];
printf("\b%d", old_c);
system("/bin/stty cooked");
}
Please read read() from stdin to go on. You can tell read() for how many characters to wait and then return, but how will you know if the user intends to input a new character to command read() to wait for user's input?
As a result, I would say that you can not do what you wish, at least as far as I can tell, with a simple approach. You could have your program feed the stale input to stdin, so that your program has the impression it reads user's input. However, in case the user actually inputs fresh input, your program should handle that case carefully.
You can setup SIGALARM handler, setup alarm after x seconds and display what getchar returns in handler
I have a user defined shell project where I'm trying to implement the cat command but allow the user to click CTRL-/ to display the next x-lines. I'm new to signals so I think I have some syntax wrong somewhere...
in main...
while (fgets(buf, sizeof(buf), file) != NULL){ //CITE
//print out first four lines
if (j == 0){
for (i = 0; i < 4; i++){
printf("%s", buf);
fgets(buf, sizeof(buf), file);
}
j++;
}
signal(SIGQUIT, sig_int);
//now check for user input for the next x lines
if (keepRunning){
for (i = 0; i < 4; i++){
printf("%s", buf);
if (i < 3){
if (fgets(buf, sizeof(buf), file) == NULL){
fclose(file);
return 0;
}
}
}
keepRunning = 0;
}
Then I have the following sig_int function defined...
static volatile int keepRunning = 0;
void sig_int(int sig){
keepRunning = 1;
}
The main problem is that you don't slow down or stop anywhere when the signal is not received. You might use pause(), for example; it only returns when a signal is received. You'd then use that in place of the if (keepRunning) block (including the condition).
Alternatively, you might modify the terminal modes so that characters are sent as they're typed ('raw' mode), and attempt to read a character before continuing. However, if you do that, the onus is on you to ensure that the terminal mode is reset before the program exits under as many circumstances as possible. The good news is that there won't be any signals generated from the keyboard in raw mode, but you have to worry about signals from elsewhere.
You might want to let other signals wake the program and exit the loop, but if you don't set a signal handler for them, that'll happen automatically. That is, after pause() returns (it will always return with an error status and errno set to EINTR), you might check whether keepRunning is now 1 and exit the loop if not.
I am working on some example code in a C text called "Headfirst C". I wrote a practice application that demonstrates signal handling and after finishing the chapter decided to play around with it a bit. I'm an engineer used to working in LabVIEW (high degree of concurrency and intuitive event handling functionality) so I am interested in using alarms and signal handlers to generate periodic events. My question is this:
In the following sample code, what is the proper method or best practices for discarding a variable amount of user input that sits in stdin? I have written this little app as a demo and a 3 second alarm triggers an annoying "howdy!" message to interrupt an fgets call. The thing I have noticed, however, is that if the user is interrupted in the middle of typing, when he eventually does press enter any text entered (having been interrupted or not) will be echoed back. I want to discard anything that was interrupted before the user pressed enter.
//Sample Program - Signal Handling & Alarms
//Header Includes
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
//Function Declarations
//Handler Functions
void diediedie(int sig);
void howdy(int sig);
//Handler Register Function
int catchSignal(int signum, void(*handler)(int));
//Variable declarations
//Declare interrupted flags
static int interrupted = 0;
//Program entrypoint
int main() {
//Register interrupt hander, catch errors
if(catchSignal(SIGINT, diediedie) == -1) {
fprintf(stderr, "Could not register interrupt handler");
exit(2);
}
//Register alarm handler, catch errors
if(catchSignal(SIGALRM, howdy) == -1) {
fprintf(stderr, "Could not register alarm handler");
exit(2);
}
//Create initial alarm trigger
alarm(3);
//Do something stupid while waiting for signals
char name[30];
printf("Enter your name: ");
//Keep waiting for user input even if interrupted by alarm signal
while(1) {
fgets(name, 30, stdin);
if(interrupted) {
// reset interrupted flag
interrupted = 0;
// ***** ADD CODE TO DISCARD INTERRUPTED INPUT HERE ******
}
else {
//echo user input and break out
printf("Hello %s\n", name);
break;
}
}
//end program
return 0;
}
//interrupt handler definition
void diediedie(int sig) {
//write some stuff, exit program
puts("Goodbye world!");
exit(1);
}
//alarm handler definition
void howdy(int sig) {
//set interrupted flag
interrupted = 1;
//write some annoying message
puts("howdy!");
//set another alarm trigger
alarm(3);
//**** COULD STDIN BE FLUSHED HERE? ****
}
//signal handler registration function definition
int catchSignal(int signum, void(*handler)(int)) {
//register handler
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(signum, &action, NULL);
}
Is the proper place to do this purge within the alarm handler? Note the comments indicating my thoughts for proper code location.
I have considered the following:
while(getchar() != EOF) {}
I am also wondering, what happens while fgets is waiting for user input and SIGALRM is raised? Is that function terminated? I observe that if I don't include the while loop to check the interrupted flag and respond appropriately, the program will finish the fgets, dump some garbage on the screen (I assume the current state of stdin?) and end the program.
Thanks for any advice!
In Unix, a signal handler occurs out of band with your code. If the signal occurs in the middle of a blocking system call, the system call will exit with errno set to EINTR. But I believe that fgets() is handling this interruption for you and continuing on without returning control to you.
If you are using a Unix based OS and typing in input from the command line, what's really going on here is that you are reading data from your terminal in cooked mode. Your program doesn't get any data from the TTY until return is pressed. You need to set the terminal into 'raw' mode. Here is an example of how to do that integrated with your code:
//Sample Program - Signal Handling & Alarms
//Header Includes
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
//Function Declarations
//Handler Functions
void diediedie(int sig);
void howdy(int sig);
//Handler Register Function
int catchSignal(int signum, void(*handler)(int));
//Variable declarations
//Declare interrupted sa_flags
static int interrupted = 0;
static struct termios save_termios;
//Program entrypoint
int main() {
struct termios buf;
int fd = 1;
// This is derived from from Stevens, "Advanced Programming in the UNIX Environment"
if (tcgetattr(fd, &save_termios) < 0) /* get the original state */
return -1;
buf = save_termios;
buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* echo off, canonical mode off, extended input
processing off, signal chars off */
buf.c_iflag |= BRKINT | ICRNL;
/* SIGINT on BREAK, CR-toNL on */
buf.c_cflag &= ~(CSIZE | PARENB);
/* clear size bits, parity checking off */
buf.c_cflag |= CS8;
/* set 8 bits/char */
buf.c_oflag &= ~(OPOST);
/* output processing off */
buf.c_cc[VMIN] = 1; /* 1 byte at a time */
buf.c_cc[VTIME] = 0; /* no timer on input */
if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
return -1;
//Register interrupt hander, catch errors
if(catchSignal(SIGINT, diediedie) == -1) {
fprintf(stderr, "Could not register interrupt handler");
exit(2);
}
//Register alarm handler, catch errors
if(catchSignal(SIGALRM, howdy) == -1) {
fprintf(stderr, "Could not register alarm handler");
exit(2);
}
//Create initial alarm trigger
alarm(3);
//Do something stupid while waiting for signals
char name[30];
printf("Enter your name: ");
//Keep waiting for user input even if interrupted by alarm signal
char nextchar = 0;
char *p;
p = name;
while(nextchar != '\n') {
nextchar = fgetc(stdin);
if (interrupted) {
// reset interrupted flag
interrupted = 0;
//Discard interrupted input by reseting 'p' to the start of the buffer
p = name;
*p = 0;
continue;
}
if (nextchar == '\n') {
*p = 0;
fputc('\r', stdout);
fputc('\n', stdout);
break;
}
// You'll have to handle some characters manually to emulate what the
// terminal does, or you could filter them out using a function like isprint()
//
if (nextchar == 127) {
// *** handle backspace
if (p > name) {
p--;
}
// TODO: To handle this right you'll have to backup the cursor on the screen
} else {
*p = nextchar;
p++;
}
fputc(nextchar, stdout);
// Handle buffer overflow
if (p-name == sizeof(name) - 1) {
*p = 0;
break;
}
}
// echo user input
printf("Input is: %s\r\n", name);
tcsetattr(1, TCSAFLUSH, &save_termios);
}
//interrupt handler definition
void diediedie(int sig) {
//write some stuff, exit program
puts("Goodbye world!");
tcsetattr(1, TCSAFLUSH, &save_termios);
exit(1);
}
//alarm handler definition
void howdy(int sig) {
//set interrupted flag
interrupted = 1;
//write some annoying message
puts("howdy!");
//set another alarm trigger
alarm(3);
}
// signal handler registration function definition
int catchSignal(int signum, void(*handler)(int)) {
//register handler
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(signum, &action, NULL);
}
Note that you need to save the original terminal settings and restore them before the program exits! If you get into trouble, you might break the terminal settings. Use reset or stty sane from the command line to restore sane terminal settings. For more information on the termios datastructure, see the man pages.
You can also use libraries like ncurses to handle raw input.
To do this right, you'd need to put the terminal into "raw" mode, in which every keystroke is returned immediately to the application, instead of being accumulated with interpretation of line-editing characters ("cooked" mode, which is the default).
Of course, if you don't let the kernel handle line-editing (backspace, for example), then you need to do it yourself, which is quite a lot of work. Interpreting backspace and whatever other editing commands you're interested in isn't that hard, but maintaining the correct appearance on the terminal is a pain.
See man termios for a lot more information.