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);
}
Related
Referring to following code example, I want the main thread to supply the number num that the child thread is expecting using scanf.
I tried this way to write the wordcount (9) to stdin which is to be read by child thread, but it is not working.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
sleep(2);
char buffer[50];
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
int save_stdin = dup(fileno(stdin));
dup2(fileno(wfp), fileno(stdin));
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
dup2(save_stdin, fileno(stdin));
pclose(wfp);
}
pthread_join(tid, NULL);
}
Can someone suggest a correct way or any other alternative way to do this?
Thanks.
I don't think there is any good way for a process to write text to its own stdin; stdin is meant to be a way for the parent process (or the user, if the parent process is a Terminal window) to send data to your process, not for your process to send data to itself.
However, you could achieve a similar result by having your child thread use select() or similar to read input from both stdin and from the output end of a pipe; then your parent process can send data to the child process by writing to the input end of that same pipe.
Below is a modified version of your program demonstrating the technique. Note that the child thread will print out any text that you type into stdin; and also the main thread will send a line of text to the child thread once every 5 seconds, and the child thread will also print out that text. After the main thread has sent 5 messages to the child thread, the main thread will close its end of the pipe, causing the child thread to exit and then the process can exit cleanly as well.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/select.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
static int pipeReadFD = -1;
static int ReadTextFrom(const char * descriptionOfSender, int fd)
{
char buf[256];
const int numBytesRead = read(fd, buf, sizeof(buf)-1); // -1 so we always have room to place NUL terminator byte
if (numBytesRead > 0)
{
buf[numBytesRead] = '\0'; // make sure the string is NUL-terminated
printf("ReadTextFrom(): Read %i bytes from [%s]: [%s]\n", numBytesRead, descriptionOfSender, buf);
}
return numBytesRead;
}
void* init_on_sys_ready(void* terminalflag)
{
int num=0;
printf("Child thread: trying to read text from stdin\n");
while(1)
{
const int stdinFD = fileno(stdin);
const int maxFD = (pipeReadFD > stdinFD) ? pipeReadFD : stdinFD;
fd_set readFDSet;
FD_ZERO(&readFDSet);
FD_SET(stdinFD, &readFDSet);
FD_SET(pipeReadFD, &readFDSet);
const int selRet = select(maxFD+1, &readFDSet, NULL, NULL, NULL);
if (selRet >= 0)
{
if ((FD_ISSET(stdinFD, &readFDSet))&&(ReadTextFrom("stdin", stdinFD) <= 0)) break;
if ((FD_ISSET(pipeReadFD, &readFDSet))&&(ReadTextFrom("pipe", pipeReadFD) <= 0)) break;
}
else
{
perror("select");
break;
}
}
printf("Child thread exiting!\n");
return NULL;
}
int main(int argc, char ** argv)
{
int pipeFDs[2];
if (pipe(pipeFDs) < 0)
{
perror("pipe");
return -1;
}
pipeReadFD = pipeFDs[0];
int pipeWriteFD = pipeFDs[1];
pthread_t tid;
if (pthread_create(&tid, NULL, &init_on_sys_ready, NULL) != 0) {
printf("Failed to initialize CLI\n");
exit(1);
}
int count = 0;
for (int count=0; count < 5; count++)
{
char buf[512];
snprintf(buf, sizeof(buf), "Hello #%i from main thread", ++count);
const size_t slen = strlen(buf);
if (write(pipeWriteFD, buf, slen) == slen)
{
printf("main() sent [%s] to the child thread via the pipe.\n", buf);
}
else
{
perror("write");
break;
}
sleep(5);
}
close(pipeWriteFD); // this will cause the child thread to exit ASAP
pthread_join(tid, NULL);
return 0;
}
popen's man states:
[...] the command's standard output is the same as that of the process that called popen()
So you just need a way to redirect stdout to stdin.
Which is exactly what pipe is for. It links an output fd with an input fd.
As pipe creates new fds, we need to use dup2 to replace stdin and stdout, as you've already did in your example code. Threads share the same memory, so you don't have to worry about any child/parent differences in fds.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* child_thread_func(void* terminalflag)
{
int num=0;
printf("Trying to read num from stdin\n");
scanf("%d",&num);
/*expecting 9 to be printed here*/
printf("Entered number is %d\n", num);
}
int main () {
setbuf(stdin, NULL);
pthread_t tid;
if (pthread_create(&tid, NULL, &child_thread_func, NULL) != 0) {
printf("Failed to initialize thread\n");
exit(1);
}
int save_stdin = dup(STDIN_FILENO);
int save_stdout = dup(STDOUT_FILENO);
int tube[2];
pipe(tube);
dup2(tube[0], STDIN_FILENO);
dup2(tube[1], STDOUT_FILENO);
char buffer[50] = {0};
FILE *wfp = popen("wc -c", "w");
if (wfp != NULL) {
sprintf(buffer, "dummyword");
fwrite(buffer, sizeof(char), strlen(buffer), wfp);
pclose(wfp);
}
dup2(save_stdin, STDIN_FILENO);
dup2(save_stdout, STDOUT_FILENO);
pthread_join(tid, NULL);
}
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?
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;
}
}
I'm writing a program that reads input from stdin, manipulates the input, and writes output to stdout. However, many programs check whether stdin is a terminal or a pipe (by calling a function like isatty), and generate output differently. How do I have my program pretend to be a TTY?
The solution should work on both Linux and macOS. Any programming language that generates a standalone binary is acceptable, but Go is preferred.
Note that I'm asking a programming question, not asking for a tool. So, things like script or unbuffer is not something I'm looking for.
The following is fully working code for running a command in a pty and capturing its output. (Not as many lines as you may have thought.)
#include <signal.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <util.h>
pid_t child = 0;
void sighandler(int signum) {
if (child > 0) {
killpg(child, signum);
exit(signum);
}
}
// Run a command in a pty.
// Usage: /path/to/this/binary command to run
int main(int argc, char *argv[]) {
if (argc < 2) {
return EX_USAGE;
}
int master;
child = forkpty(&master, NULL, NULL, NULL);
if (child == -1) {
perror("failed to fork pty");
return EX_OSERR;
}
if (child == 0) {
// we're in the child process, so replace it with the command
execvp(argv[1], argv + 1);
perror("failed to execute command");
return EX_OSERR;
}
// trap kill signals and forward them to child process
signal(SIGHUP, sighandler);
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
const int buf_size = 1024;
char buf[buf_size];
fd_set fds;
ssize_t bytes_read;
// forward the output continuously
while (1) {
FD_ZERO(&fds);
FD_SET(master, &fds);
if (select(master + 1, &fds, NULL, NULL, NULL) > 0 && FD_ISSET(master, &fds)) {
bytes_read = read(master, buf, buf_size);
if (bytes_read <= 0) {
return EXIT_SUCCESS;
}
if (write(STDOUT_FILENO, buf, bytes_read) != bytes_read) {
perror("failed to write to stdout");
return EX_OSERR;
}
}
}
}
I had to write a program that uses 3 threads - one to read letters, second to count characters, and the third to output them. This is the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/stat.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
int first[2];
int second[2];
void *input(void *ptr)
{
char str[100];
int length;
while(1)
{
printf("Enter the message: ");
fflush(stdout);
length = read(STDIN_FILENO, str, sizeof(str));
if(str[0] == ';')
exit(2);
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[1]);
exit(2);
}
if(write(first[1], str, length) != length)
{
perror("write");
exit(2);
}
}
}
void *countChars(void *ptr)
{
char str[100];
int length, count = 0;
while(1)
{
length = read(first[0], str, sizeof(str));
if(length <= 0)
{
if(length == -1)
perror("read");
close(first[0]);
close(second[1]);
exit(2);
}
if(write(STDOUT_FILENO, str, length) != length)
{
perror("write");
exit(2);
}
while(str[count] != '\n') count++;
write(second[1], &count, sizeof(count));
count = 0;
}
}
void *output(void *ptr)
{
int length, count = 0;
while(1)
{
length = read(second[0], &count, sizeof(count));
if(length < sizeof(count))
{
close(second[0]);
exit(2);
}
printf("Number of characters: %d\n", count);
}
}
int main()
{
pthread_t t1, t2, t3;
if(pipe(first) == -1)
{
printf("First pipe error");
exit(1);
}
if(pipe(second) == -1)
{
printf("Second pipe error");
exit(1);
}
pthread_create(&t1, NULL, input, NULL);
pthread_create(&t2, NULL, countChars, NULL);
pthread_create(&t3, NULL, output, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
return 0;
}
It works, but right now I have to implement signals here. Sending SIGUSR1 signal should stop program execution until sending SIGUSR2 signal.
The problem is that when I send the signal, only one thread gets it. And thus I have to use FIFO to inform other threads which signal was executed and execute it in the rest of them.
How could I do this?
Signals are delivered to the process, not to the threads. Thus any thread that is able to handle a signal may be used to call a signal handler. What you need to do is figure out how to handle the signal and then decide how to communicate that to all the threads. You have not really described what you mean by "stop program execution", so I'll have to guess.
I would suggest using a combination of pthread_sigmask and sigwait. You can use pthread_sigmask to disable automatic handling of SIGUSR1 and SIGUSR2 in your worker threads. Then call sigwait in a fourth signal handler thread to explicitly handle those signals. When the signal handler thread receives a SIGUSR1 it sets a global flag. The worker threads check that flag periodically and go to sleep (on a condition variable maybe?) when it is set. The signal handler thread then loops around and calls sigwait again. When it receives a SIGUSR2, it wakes up the worker threads, then loops around and calls sigwait, once again.