I am used to gdb. Now, as I had switched to FreeBSD, I try to use lldb.
The program uses kqueue to wait on SIGINT, SIGTERM, SIGQUIT signals. Now if lldb attaches to the process, or runs target with command line arg, the debugger sends SIGSTOP signal. One thing is that SIGSTOP can't be ignored and wait on.
The problem is, that kevent returns -1 on process resume and proceeds to quit. I can not see no output to stderr, that is another question being asked in different post.
What is a practice to deal with signals when lldb is a debugger?
Update
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/event.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <yma/yerror.h>
#include <yma/ybsd_sock.h>
#define TERMINATE_SET(s, v, l) { EXPAND(s) = v; PRINT_ERROR() goto EXPAND(l); }
#define NSIGNALS (5)
#define NSOCKETS (2)
#define SIGNALS {SIGINT, SIGQUIT, SIGTERM, SIGCONT, SIGPIPE}
#define NKEVBACKLOG (NSIGNALS + (NSOCKETS * 4)) /* signals + socket reads/writes + socket error + eof */
#define BUFSIZE (1024)
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
/* program status */
int status = 0;
/* restore signals at program exit ? */
bool b_restore_procmask = false;
/* signal masks */
sigset_t mask_block_sigs, mask_save_sigs, mask_kqueue_sigs;
struct kevent chlist[10]; /* events to monitor */
struct kevent evlist[NKEVBACKLOG]; /* events that were triggered */
int kq = -1, i, n;
/* signals being monitored by kqueue */
int signals[] = SIGNALS;
/* create kqueue descriptor */
if((kq = kqueue()) < 0){
TERMINATE_SET(status, -1, l_end)
}
/* set up signals */
sigemptyset(&mask_block_sigs);
/* these signals will be blocked. the signals will arrive */
/* to kqueue and not to a default signal handler. */
for(i = 0; i < NSIGNALS; ++i){
sigaddset(&mask_block_sigs, signals[i]);
}
/* save old sigmask, replace it with new sigmask */
status = sigprocmask(SIG_BLOCK, &mask_block_sigs, &mask_save_sigs);
TERMINATE_IF(status < 0, l_end)
b_restore_procmask = true;
/* set signal event */
for(i = 0; i < NSIGNALS; i++){
EV_SET(&chlist[i], signals[i], EVFILT_SIGNAL, EV_ADD, 0, 0, 0);
}
/* end of signal setup */
/* add signals change list to kqueue */
n = kevent(kq, chlist, NSIGNALS, NULL, 0, NULL);
if(n < 0){
TERMINATE_SET(status, -1, l_end)
}
/* event loop */
while( 1 ) {
/* see event list */
n = kevent(kq, NULL, 0, evlist, NKEVBACKLOG, NULL);
switch(n) { /* switch on kevent return */
/* timeout */
case 0:
break;
/* error */
case -1:
TERMINATE_SET(status, -1, l_end);
/* some events */
default: {
int ev_id;
/* process events */
for(i = 0; i < n; ++i){
ev_id = evlist[i].ident;
/* check if signal has arrived */
switch(ev_id){
case SIGINT:
printf(" ! got SIGINT\n");
goto l_end;
case SIGQUIT:
printf(" ! got SIGQUIT\n");
goto l_end;
case SIGTERM:
printf(" ! got SIGTERM\n");
goto l_end;
case SIGPIPE:
printf(" ! got SIGPIPE\n");
break;
default: break;
}
} break; /* end process events */
} /* end kevent return switch */
} /* end event loop */
l_end:
/* restore procmask */
if(b_restore_procmask && (sigprocmask(SIG_SETMASK, &mask_save_sigs, NULL) < 0)){
PRINT_ERROR()
}
/* close kqueue */
CLOSE(kq);
return status;
}
The above is a complete source of a program that waits on POSIX signals.
I do not want to include my makefile, as it is irrelevant here. Something like clang -std=c2x -Wall -Wpedantic -Wextra -Wfatal-errors -O0 -g3 -glldb -fpic -c kevent.c -o outp should go.
Update
The program is indeed exits with "Interrupted system call"
Update
As I read again through the code, I've found an error. The error was in misplaced ev_id initialization. The initialization were before for event loop. So the above code reacts to SIGINT as expected.
Update
But again, when I attach to process, and not run it as an argument to lldb, I am being confused again. If I send SIGINT to the process, lldb is ignoring the fact that process handle SIGINT -s false -p true and quits with "Interrupted system call."
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'm trying to make a program that simulates the command nohup. The program gets as a first parameter, the name of a command that is gonna be executed.
The program executed by my program must not be notified when the terminal is closed, it will have to ignore the SIGHUP.
If I test my program with with the following command:
./mynohup sleep 120 &
And then I try to send a SIGHUP from another terminal, sleep terminates when it should be immune to it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "utils.h"
#define NOHUP_OUT_FILE "nohup.out"
static void handle_signal(int signum)
{
if(signum == SIGHUP)
{
printf("This is ignored\n");
}
else
{
printf("Not ignored\n");
}
fflush(stdout);
}
/* configure handlers */
static void set_signals(void)
{
struct sigaction sa;
int rc;
/* TODO - ignore SIGHUP */
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_signal;
rc = sigaction(SIGHUP, &sa, NULL);
DIE(rc == -1, "sigaction");
}
/* execute a new program */
static void exec_func(int argc, char **argv)
{
int rc;
int i;
char **exec_args;
int fd;
set_signals(); /* ignore SIGHUP */
if(isatty(STDOUT_FILENO))
{
fd = open(NOHUP_OUT_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
DIE(fd < 0, "open");
dup2(fd, STDOUT_FILENO);
close(fd);
}
/* exec a new process */
exec_args = malloc(argc * sizeof(*exec_args));
DIE(exec_args == NULL, "malloc");
for (i = 0; i < argc-1; i++)
exec_args[i] = argv[i+1];
exec_args[argc-1] = NULL;
execvp(exec_args[0], exec_args);
DIE(1, "execvp");
}
int main(int argc, char **argv)
{
if (argc <= 1) {
fprintf(stderr, "Usage: %s command_and_arguments\n", argv[0]);
exit(EXIT_FAILURE);
}
exec_func(argc, argv);
return 0;
}
I tried to skip creating a new process and the signal handler works great.
If the signal handler is in the following form the program works
static void set_signals(void)
{
struct sigaction sa;
int rc;
/* ignore SIGHUP */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
rc = sigaction(SIGHUP, &sa, NULL);
DIE(rc == -1, "sigaction");
}
I don't understand why when I create the first version of the signal handler the program doesn't works and with the second one it works.
Thanks in advance!
All exec functions reset the dispositions of caught signals to their default dispositions.
When you exec, your process image is destroyed and replaced by the process image of the new program. In it, the pointer to the handle_function you passed to sigaction no longer has meaning, or the old meaning at least. The only sensible thing the OS can do with handled signals upon execve is to reset them.
The meaning of SIG_IGN is universal and independent of the current program and that's why SIG_IGN can be, and is, inherited.
execvp() is a front end for the execve() syscall.
From its linux manpage:
All process attributes are preserved during an execve(), except the following:
* The dispositions of any signals that are being caught are reset to
the default (signal(7)).
So the signal handler you installed is reset.
CORRECTION: (see history of changes for original text)
The nohup(1) program just shifts the progran name (nohup) and the options to it, from the argc/argv parameters to main, redirects stdout/stderr to a file (nohup.out) in case one or both are directed to a tty device, and then just ignores SIGHUP and execvp(*argv, argv); for the original program to execute. It even does no fork(2) at all.
The source code of FreeBSD nohup is available here.
Hi I'm trying to learn more about signals and I wrote a simple code that supposed to just print "bye" everything the alarm signal is send. I am using sigaction to set this. However, I keep returning NULL in my error checking could someone tell me what I'm doing wrong. Thanks in Advance!
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/time.h> /* for setitimer */
#include <unistd.h> /* for pause */
#include <signal.h> /* for signal */
#define INTERVAL 500 /* number of milliseconds to go off */
/* function prototype */
void DoStuff();
int main(int argc, char *argv[]) {
struct itimerval it_val; /* for setting itimer */
struct sigaction sa;
sa.sa_handler = &DoStuff;
/* Upon SIGALRM, call DoStuff().
* Set interval timer. We want frequency in ms,
* but the setitimer call needs seconds and useconds. */
if (sigaction(SIGALRM,&sa,NULL) < 0) { /*set the signal to be enabled if this action occurs*/
perror("Unable to catch SIGALRM");
exit(1);
}
it_val.it_interval = it_val.it_value;
it_val.it_value.tv_sec = INTERVAL/1000;
it_val.it_value.tv_usec = (INTERVAL*1000) % 1000000;
it_val.it_interval = it_val.it_value;
if (setitimer(ITIMER_REAL, &it_val, NULL) == -1) { /*set the timer to send the alarm command*/
perror("error calling setitimer()");
exit(1);
}
while(1)
{
pause();
}
}
void DoStuff() {
printf("bye\n");
}
sigaction cannot return null, since it returns an integer. I'm assuming it's returning -1. You're not correctly initializing the sigaction structure. It has many fields, but you're allowing them to be undefined. Fix the structure definition and try again. See:
http://man7.org/linux/man-pages/man2/sigaction.2.html
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?
in a single main() function,so need signal handling. Use Posix Message Queue IPC mechanism , can ignore the priority and other linked list message,to implement the scenario:
client:Knock Knock
server:who's there
client: pilcrow
Server:pilcrow,thanks a lot.
client:exit
all process terminated
stdin->POSIX MsgQ client send "knock knock" to server->Server compares string and send "who's there" back to client
What I got is :
client:knock knock
Server:Who's there?
client:pilcrow
pilcrow
client:Exit
Exit
1st round successfully give me the right result.From 2nd round, the client output the same typing on console.
Please help. Remember to use gcc -lrt to link mq_function.
Below is my code,
#include <mqueue.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#define MSG_SIZE 100 //max size of msg
#define MAX_MSG 1 //max # of msg
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
volatile sig_atomic_t mqflag; /* set nonzero by signal handler */
static void sig_usr1(int);
sigset_t zeromask, newmask, oldmask;
int main(int argc, char **argv) {
int c,flags;/* for getopt() */
pid_t child_pid;
mqd_t msgq_id;
struct mq_attr attr;
struct sigevent sigev;
char *buff_forward,*buff_backward;
flags=O_RDWR | O_CREAT;
attr.mq_msgsize=MSG_SIZE;
attr.mq_maxmsg=MAX_MSG;
buff_forward=malloc(attr.mq_msgsize);
buff_backward=malloc(attr.mq_msgsize);
while ((c= getopt(argc, argv, "e")) != -1) {
switch (c) {
case 'e': /* create the queue exclusive */
flags|= O_EXCL;
break;
}
}
if (optind!=argc-1){
printf("usage: [-e] <name>");
exit(1);
}
msgq_id = mq_open(argv[optind],flags,FILE_MODE,&attr);
/* producing the message */
mq_getattr(msgq_id, &attr) ;
printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- "
"large at most %ld bytes each\n\t- currently holds %ld messages\n",
argv[optind], attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
sigemptyset(&zeromask); /* no signals blocked */
sigemptyset(&newmask);
sigemptyset(&oldmask);
sigaddset(&newmask, SIGUSR1);
/* establish signal handler, enable notification */
signal(SIGUSR1, sig_usr1);
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = SIGUSR1;
sigprocmask(SIG_BLOCK, &newmask, &oldmask);/* block SIGUSR1 */
if ((child_pid=fork())==0){
for (; ; ) {
while (mqflag == 0)
sigsuspend(&zeromask);
mqflag =0; /* reset flag */
msgq_id=mq_open(argv[optind],O_RDONLY);
mq_receive(msgq_id, buff_forward, attr.mq_msgsize, NULL);
mq_close(msgq_id);
if (strcasecmp ("Knock Knock",buff_forward)==0){
strcpy(buff_backward,"Server:Who's there?");
}
else if(strcasecmp ("pilcrow", buff_forward)==0){
strcpy(buff_backward,"Server:Pilcrow,thanks a lot!");
}
else if(strcasecmp ("Exit",buff_forward)==0){
kill(getppid(),SIGTERM);
exit(0);
}
msgq_id=mq_open(argv[optind],O_WRONLY);
mq_send(msgq_id,buff_backward,MSG_SIZE,NULL);
mq_close(msgq_id);
mq_notify(msgq_id, &sigev); /* reregister */
}
sigprocmask(SIG_UNBLOCK, &newmask, NULL); /* unblock SIGUSR1 */
exit(0);
}
else if(child_pid>0){
for(;;){
printf("client:");
gets(buff_forward);
msgq_id=mq_open(argv[optind],O_WRONLY);
mq_send(msgq_id,buff_forward,MSG_SIZE,NULL);
mq_close(msgq_id);
mq_notify(msgq_id, &sigev);
while(mqflag==0)
sigsuspend(&zeromask);
mqflag==0;
msgq_id=mq_open(argv[optind],O_RDONLY);
mq_receive(msgq_id, buff_backward, attr.mq_msgsize, NULL);
printf("%s\n",buff_backward);
mq_close(msgq_id);
}
sigprocmask(SIG_UNBLOCK, &newmask, NULL); /* unblock SIGUSR1 */
exit(0);
}
return (EXIT_SUCCESS);
}
static void sig_usr1(int signo) {
mqflag = 1;
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
return;
}
The child calls sigsuspend before calling mq_notify (because mq_flag is implicitly initialized to zero and the check for interruption is improperly performed before notification). It can never be woken up as intended.
Initialize mq_flag to 1 to see the difference. Then refactor.
UPDATED
The OP changed the code substantially, so I changed this answer, too.