I created a daemon that creates a fifo file with a default name and then is being blocked waiting for messages on that fifo. When an interactive process (even from the shell an echo "..." > file_filo) write data to that fifo, the daemon wakes up and writes the received data to a journal file along with the time when the writing was done. The code (daemon.fifo file) creates a daemon.fifo file in the current directory and can be written anywhere in that file.
code:
#include "ourhdr.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
// gcc -Wall -O tema_daemon_fifo.c liblab.a -o tema_daemon_fifo
int main()
{
// 1 = keep the current directory (will contain the log and the fifo)
// 0 = close the standard input/output streams for the daemon
int daemonized = daemon(1, 0);
if (daemonized != 0)
err_sys("daemon error");
char* fifo_file = "daemon.fifo";
int r = mkfifo(fifo_file, 0700);
if (r != 0)
{
if (errno != EEXIST)
{
err_sys("mkfifo error");
}
}
int fifo_fd = open(fifo_file, O_RDONLY);
if (fifo_fd < 0)
{
err_sys("open-fifo error");
}
int stop_loop = 0;
char buff[1024];
do
{
int c = read(fifo_fd, buff, 1024);
if (c >= 0)
{
if (c >= 4 && strncmp("exit", buff, 4) == 0)
{
stop_loop = 1;
}
if (c > 0)
{
int file_fd = open("daemon.txt", O_WRONLY | O_CREAT | O_APPEND, 0700);
if (file_fd >= 0)
{
time_t t = time(0);
char* timestamp = ctime(&t);
r = write(file_fd, timestamp, strlen(timestamp));
if (r < 0)
err_sys("write timestamp error");
r = write(file_fd, buff, c);
if (r < 0)
err_sys("write buff error");
close(file_fd);
}
else
{
err_sys("open-log error");
}
}
}
else
{
err_sys("read error");
}
} while (stop_loop == 0);
close(fifo_fd);
return 0;
}
problem (bug): Normally the daemon.fifo file through which you can write to the daemon should only exist if there is also a daemon to write to. How can I fix the bug?
You need to call unlink() after closing the file to remove the file from the folder.
Also, you should consider catching the SIGTERM and allowing the signal to also break the loop so the daemon can terminate in a normal way and clean the files when kill <PID> is executed.
So this is the modificaiton I did:
...
...
#include <signal.h>
int stop_loop = 0;
void terminate(int signum)
{
stop_loop = 1;
}
int main()
{
// 1 = keep the current directory (will contain the log and the fifo)
// 0 = close the standard input/output streams for the daemon
int daemonized = daemon(1, 0);
if (daemonized != 0)
err_sys("daemon error");
char* fifo_file = "daemon.fifo";
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = terminate;
sigaction(SIGTERM, &action, NULL);
...
...
and
...
...
} while (stop_loop == 0);
close(fifo_fd);
unlink(fifo_file);
return 0;
}
The file will remain on the system only if the daemon is killed with kill -9 <PID>.
Related
I'm writing a very simple bash-like shell in C and am currently implementing pipes between commands (i.e. command1 | command2, which should run both commands at the same time with the stdout of the first one connected through a pipe with the stdin of the second one).
I've gotten to the point where something like
shell> echo test | cat | cat
correctly prints "test" to the string, but anything more complicated than that doesn't make it. For example:
shell> ls -1 / | sort | rev
It's (as far as I can tell) equivalent to the previous one in terms of piping, yet this one fails and the other one succeeds.
I'm at a complete loss as to why this is because I've debugged both the main process and the children exhaustively and verified that the processes get launched with the correct connections both in the working and in the not working command.
Here's a simplified version of the code:
// Uncomment to use hardcoded input
// #define USE_HARDCODED_INPUT
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // NULL
#include <errno.h> // ENOENT
#include <stdio.h> // setbuf, printf
#include <unistd.h> // exec, fork
#include <fcntl.h> // open
#include <sys/types.h> // wait
#include <sys/wait.h>
void set_process_FDs(int input, int output, int error)
{
if (input)
{
dup2(input, STDIN_FILENO);
close(input);
}
if (output)
{
dup2(output, STDOUT_FILENO);
close(output);
}
if (error)
{
dup2(error, STDERR_FILENO);
close(error);
}
}
void child_setup(char **argv, int input, int output, int error)
{
if (input || output || error)
set_process_FDs(input, output, error);
execvp(argv[0], argv);
perror("exec()");
exit(1);
}
int launch_process(char **argv, int is_last,
int input, int output, int error)
{
int status;
pid_t pid = fork();
switch(pid)
{
case -1:
perror("fork()");
return 0;
case 0:
child_setup(argv, input, output, error);
return 0;
default:
break;
}
if (is_last)
wait(&status);
return 1;
}
int run_commands(char ***argvv)
{
int no_commands_ran = 0;
int argc;
char **argv = argvv[0];
int in_pipe[2];
int out_pipe[2];
for (int i=0; (argv = argvv[i]); ++i)
{
pipe(out_pipe);
if (i == 0)
in_pipe[0] = 0;
if (!argvv[i+1])
{
close(out_pipe[0]);
close(out_pipe[1]);
out_pipe[1] = 0;
}
for (argc=0; argv[argc]; ++argc);
if (!launch_process(argv, !argvv[i+1],
in_pipe[0], out_pipe[1], 0))
break;
if (i != 0)
{
close(in_pipe[0]);
close(in_pipe[1]);
}
in_pipe[0] = out_pipe[0];
in_pipe[1] = out_pipe[1];
no_commands_ran = i + 1;
}
return no_commands_ran;
}
extern int obtain_order(); // Obtains an order from stdin
int main(void)
{
char ***argvv = NULL;
int argvc;
char *filev[3] = {NULL, NULL, NULL};
int bg;
int ret;
setbuf(stdout, NULL); // Unbuffered
setbuf(stdin, NULL);
while (1)
{
#ifndef USE_HARDCODED_INPUT
printf("%s", "shell> "); // Prompt
ret = obtain_order(&argvv, filev, &bg);
if (ret == 0) // EOF
{
fprintf(stderr, "EOF\n");
break;
}
if (ret == -1)
continue; // Syntax error
argvc = ret - 1; // Line
if (argvc == 0)
continue; // Empty line
if (!run_commands(argvv))
continue; // Error executing command
#else
argvc = 3;
char ***argvv1 = calloc(4, sizeof(char*));
argvv1[0] = calloc(3, sizeof(char*));
argvv1[0][0] = strdup("echo");
argvv1[0][1] = strdup("test");
argvv1[1] = calloc(2, sizeof(char*));
argvv1[1][0] = strdup("cat");
argvv1[2] = calloc(2, sizeof(char*));
argvv1[2][0] = strdup("cat");
char ***argvv2 = calloc(4, sizeof(char*));
argvv2[0] = calloc(4, sizeof(char*));
argvv2[0][0] = strdup("ls");
argvv2[0][1] = strdup("-1");
argvv2[0][2] = strdup("/");
argvv2[1] = calloc(4, sizeof(char*));
argvv2[1][0] = strdup("sort");
argvv2[2] = calloc(4, sizeof(char*));
argvv2[2][0] = strdup("rev");
printf("%s", "shell> echo test | cat | cat\n");
if (!run_commands(argvv1))
continue; // Error executing command
usleep(500);
printf("%s", "shell> ls -1 / | sort | rev\n");
if (!run_commands(argvv2))
continue; // Error executing command
printf("%s", "\nNo more hardcoded commands to run\n");
break;
#endif
}
return 0;
}
obtain_order() is a function located in the parser, which is a simple Yacc parser. It just fills the vector of argvs called argvv with whatever was input in the shell. In case anyone wants to try the code and see the problem, simply uncomment the #define at the beginning to see the behaviour you'd get from typing the problematic commands manually.
To start, your parent process does not wait for all of its child processes to complete their execution.
This call to wait does occur after the last child process has been spawned
if (is_last)
wait(&status);
but it does not necessarily wait for the last child process. That is to say, it will return when any one child process has completed execution (or an error occurs).
Properly waiting for all child processes to complete, at the end of run_commands,
/* ... */
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
return no_commands_ran;
exposes the fact that children after the first are hanging, as wait blocks execution of the parent program.
(After placing a few fprintf statements. █ here indicates program is blocking.)
shell> echo test | cat | cat
LOG: Child<30607> (echo)
LOG: Child<30608> (cat)
LOG: Child<30609> (cat)
LOG: Child<30607> process exited with status <0>
█
Without waiting for all child processes, you are creating orphan processes.
As for why these processes fail to terminate, this is due to the fact that certain file descriptors are not being closed.
The call to launch_process
launch_process(argv, !argvv[i+1], in_pipe[0], out_pipe[1], 0)
ensures that in_pipe[0] and out_pipe[1] are closed in the child process, but leaks any valid file descriptors in_pipe[1] or out_pipe[0]. With those leaked file descriptors still open in the child processes, the associated pipes remain valid, and thus the processes will continue to block while they wait for more data to arrive.
The quickest fix is to change launch_process to accept both pipes
int launch_process(char **argv, int is_last,
int input[2], int output[2], int error);
pass both pipes
if (!launch_process(argv, !argvv[i+1], in_pipe, out_pipe, 0))
close the excess file descriptors
case 0:
close(input[1]);
close(output[0]);
child_setup(argv, input[0], output[1], error);
return 0;
remove
if (is_last)
wait(&status);
and add the previously shown wait loop to the end of run_commands.
Here is a complete example of a working version of your program, with minimal refactoring.
Compile with -DDEBUG for some additional sleep time, in order to discover file descriptor leaks (there should not be any). Please read the extended comment in main.
#define _POSIX_C_SOURCE 200809L
#define USE_HARDCODED_INPUT
#define DEBUG_SLEEP_TIME 20
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
void set_process_FDs(int input, int output, int error)
{
if (input) {
dup2(input, STDIN_FILENO);
close(input);
}
if (output) {
dup2(output, STDOUT_FILENO);
close(output);
}
if (error) {
dup2(error, STDERR_FILENO);
close(error);
}
}
void child_setup(char **argv, int input, int output, int error)
{
if (input || output || error)
set_process_FDs(input, output, error);
#ifdef DEBUG
/* a sleep here should allow time to inspect
* `/proc/$PID/fd` for FD leaks, see `main` for details
* if the child process hangs you will have ample time, regardless
*/
sleep(DEBUG_SLEEP_TIME);
#endif
execvp(argv[0], argv);
perror("exec()");
exit(EXIT_FAILURE);
}
int launch_process(char **argv, int is_last,
int input[2], int output[2], int error)
{
pid_t pid = fork();
(void) is_last;
switch(pid) {
case -1:
perror("fork()");
return 0;
case 0:
fprintf(stderr, "LOG: Child<%ld> (%s)\n", (long) getpid(), *argv);
close(input[1]);
close(output[0]);
child_setup(argv, input[0], output[1], error);
return 0;
default:
break;
}
return 1;
}
int run_commands(char ***argvv)
{
int no_commands_ran = 0;
int in_pipe[2];
int out_pipe[2];
char **argv;
for (int i = 0; (argv = argvv[i]); ++i) {
pipe(out_pipe);
if (i == 0)
in_pipe[0] = 0;
if (!argvv[i+1]) {
close(out_pipe[0]);
close(out_pipe[1]);
out_pipe[1] = 0;
}
if (!launch_process(argv, !argvv[i+1], in_pipe, out_pipe, 0))
break;
if (i != 0) {
close(in_pipe[0]);
close(in_pipe[1]);
}
in_pipe[0] = out_pipe[0];
in_pipe[1] = out_pipe[1];
no_commands_ran = i + 1;
}
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
return no_commands_ran;
}
int main(void)
{
fprintf(stderr, "LOG: Parent ID: <%ld>\n", (long) getpid());
#ifdef USE_HARDCODED_INPUT
char ***argvv1 = calloc(4, sizeof(char*));
argvv1[0] = calloc(3, sizeof(char*));
argvv1[0][0] = "echo";
argvv1[0][1] = "test";
argvv1[1] = calloc(2, sizeof(char*));
argvv1[1][0] = "cat";
argvv1[2] = calloc(2, sizeof(char*));
argvv1[2][0] = "cat";
char ***argvv2 = calloc(4, sizeof(char*));
argvv2[0] = calloc(4, sizeof(char*));
argvv2[0][0] = "ls";
argvv2[0][1] = "-1";
argvv2[0][2] = "/";
argvv2[1] = calloc(2, sizeof(char*));
argvv2[1][0] = "sort";
argvv2[2] = calloc(2, sizeof(char*));
argvv2[2][0] = "rev";
puts("shell> echo test | cat | cat");
if (!run_commands(argvv1))
return EXIT_FAILURE;
/* usleep is deprecated */
nanosleep(&(struct timespec) { .tv_nsec = 5e5 }, NULL);
puts("shell> ls -1 / | sort | rev");
if (!run_commands(argvv2))
return EXIT_FAILURE;
puts("No more hardcoded commands to run");
#endif
#ifdef DEBUG
/* compile with -DDEBUG
* placing a sleep here to provide time to discover
* any file descriptor leaks
* inspect `ls -l /proc/$PID/fd`
* only the standard stream fds should exist (0, 1, 2) at
* either debug sleep
* see child_setup as well
*/
sleep(DEBUG_SLEEP_TIME);
#endif
}
Here is a cursory, annotated example of establishing a series of pipes and processes. It works similarly to your example, and might help to further showcase the order in which file descriptors must be opened, duplicated, and closed.
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int valid(int fd)
{
return fd >= 0;
}
/* these safe_* functions are a non-operation when passed negative values */
void safe_close(int fd)
{
if (valid(fd) && !valid(close(fd)))
perror("close");
}
void safe_dup2(int old, int new)
{
if (valid(old) && valid(new) && !valid(dup2(old, new)))
perror("dup2");
}
void execute(char *args[][8], size_t length)
{
int channel[2] = { -1, -1 };
for (size_t i = 0; i < length; i++) {
/* get previous reader in parent */
int from = channel[0];
/* close previous writer in parent */
safe_close(channel[1]);
/* create current-writer-to-next-reader pipe */
if (!valid(pipe(channel)))
perror("pipe");
int to = (i < length - 1) ? channel[1] : -1;
if (0 == fork()) {
/* duplicate previous reader to stdin in child */
safe_dup2(from, fileno(stdin));
/* close previous reader in child */
safe_close(from);
/* close next reader in current child */
safe_close(channel[0]);
/* duplicate current writer to stdout in child */
safe_dup2(to, fileno(stdout));
/* close current writer in child */
safe_close(channel[1]);
execvp(args[i][0], args[i]);
perror("exec");
exit(EXIT_FAILURE);
}
/* close previous reader in parent */
safe_close(from);
}
/* close final pipe in parent */
safe_close(channel[0]);
safe_close(channel[1]);
/* reap children */
pid_t pid;
int status;
while ((pid = wait(&status)) > 0)
if (WIFEXITED(status))
fprintf(stderr, "LOG: Child<%ld> process exited with status<%d>\n",
(long) pid,
WEXITSTATUS(status));
}
int main(void)
{
char *argv[][8] = {
{ "echo", "test" },
{ "cat" },
{ "cat", "-n" }
};
execute(argv, 3);
char *argv2[][8] = {
{ "ls", "-1", "/" },
{ "sort" },
{ "rev" }
};
execute(argv2, 3);
}
Aside: As an edge case, 0 is a valid file descriptor. set_process_FDs is flawed in that if STDIN_FILENO is closed, and a new file descriptor is acquired, it may be zero. if (output) or if (error) may not behave as expected.
I am trying to use FIFO for interprocessing. But when trying to create a FIFO and then open it, my program hangs (cannot exit).
if (mkfifo("./fifo.txt", S_IRUSR | S_IWUSE) < 0) {
fprint("Can not create fifo");
return 1;
}
if ((readfd = open("./fifo.txt", O_RDONLY)) < 0) {
return 1;
}
What am I doing wrong here?
Thank you very much.
Read fifo(7), notably:
Normally, opening the FIFO blocks until the other end is opened also.
So I guess that your call to open(2) is blocked. Perhaps you want to pass the O_NONBLOCK flag.
You should use strace(1) to debug your program (and perhaps also strace the other program on the other end of the fifo). And call perror(3) on error.
Perhaps using unix(7) sockets could be more relevant in your case. You can then poll(2) before accept(2)
You should read Advanced Linux Programming.
Here is an example code:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void child(void)
{
int fd = 0;
if ((fd = open("./fifo.txt", O_WRONLY)) < 0) {
return;
}
write(fd, "hello world!", 12);
}
void parent(void)
{
int fd = 0;
if ((fd = open("./fifo.txt", O_RDONLY)) < 0) {
return;
}
char buf[36] = {0};
read(fd, buf, 36);
printf("%s\n", buf);
}
int main(void)
{
pid_t pid = 0;
if (mkfifo("./fifo.txt", S_IRUSR | S_IWUSR) < 0) {
printf("Can not create fifo\n");
return 1;
}
pid = fork();
if (pid == 0) {
printf("child process\n");
child();
} else if (pid < 0) {
printf("fork error\n");
return -1;
}
parent();
}
Basically I have a parent process that forks a child and feeds it it's stdin through a pipe. The child process can terminate in one of two cases:
the write end of the pipe is closed by the parent, meaning it reached the end of stdin thus receiving an EOF,
or it receives a certain input through the pipe(-1 in this case) and exits
My parent code looks roughly like this:
close(pi[0]); // close input end
signal(SIGPIPE, SIG_IGN); // do not handle SIGPIPE
char buffer;
int ok = 1;
while(ok && read(STDIN_FILENO, &buffer, 1) > 0) {
int b_written = write(pi[1], &buffer, 1);
if(b_written == -1) {
if(errno == EPIPE) ok = 0;
else perror("pipe write"); // some other error
}
}
As you can see, I check whether the read end of a pipe is closed by checking for errno == EPIPE. However this means that the read loop does one extra iteration before closing. How could I possibly poll to see if the pipe is closed without necessarily writing something to it?
This snippet will check if the other end of a writable pipe is closed using poll(2). This works on Linux -- I'm not sure about other OSes or what POSIX says.
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
bool is_pipe_closed(int fd) {
struct pollfd pfd = {
.fd = fd,
.events = POLLOUT,
};
if (poll(&pfd, 1, 1) < 0) {
return false;
}
return pfd.revents & POLLERR;
}
The child could send a signal, such as SIGUSR1 when it detects it has finished. Parent could set a flag to when it receives SIGUSR1 signal, and check this flag before trying to read input. But I am not absolutely sure SIGUSR1 could not be received after checking the flag ans before reading input from stdin). So I prefer to use a control pipe, each time child know it will be able to read one more data it write a 1 in this control pipe. The result could be something like that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#define STOP_VALUE 100
#define SIZE_STDIN_BUFFER 1024
static char can_read_more = 1;
static int handle_child(int *p_child_input_stream, int *p_control_stream)
{
int pipefd[2][2];
pid_t fk;
if (pipe(pipefd[0]) < 0) // Pipe to read input from
{
perror("pipe");
return -1;
}
if (pipe(pipefd[1]) < 0) // Pipe to notifiate parent input can be processed
{
perror("pipe");
close(pipefd[0][0]);
close(pipefd[0][1]);
return -1;
}
if ((fk = fork()) < 0)
{
perror("fork");
close(pipefd[0][0]);
close(pipefd[0][1]);
close(pipefd[1][0]);
close(pipefd[1][1]);
return -1;
}
if (fk == 0)
{
close(pipefd[0][1]);
close(pipefd[1][0]);
write(pipefd[1][1], &can_read_more, sizeof(char)); // sizeof(char) == 1
ssize_t nb_read = 0;
char buffer;
while (nb_read >= 0)
{
nb_read = read(pipefd[0][0], &buffer, sizeof(char));
if (nb_read > 0)
{
printf("0x%02x\n", (unsigned int) buffer);
if (buffer == STOP_VALUE)
{
nb_read = -1;
}
else
{
write(pipefd[1][1], &can_read_more, sizeof(char));
}
}
}
close(pipefd[0][0]);
close(pipefd[1][1]);
exit(0);
}
close(pipefd[0][0]);
close(pipefd[1][1]);
*p_child_input_stream = pipefd[0][1];
*p_control_stream = pipefd[1][0];
return 0;
}
int main()
{
int child_input_stream;
int control_stream;
if (handle_child(&child_input_stream, &control_stream) < 0)
{
return 1;
}
char stdin_buffer[SIZE_STDIN_BUFFER];
char buffer;
int ok = 1;
int child_available_input = 0;
while(ok)
{
while (child_available_input <= 0 && ok)
{
ssize_t nb_control = read(control_stream, &buffer, sizeof(char));
if (nb_control > 0)
{
child_available_input += buffer;
}
else
{
fprintf(stderr, "End of child reading its input detected.\n");
ok = 0;
}
}
if (ok)
{
if (fgets(stdin_buffer, SIZE_STDIN_BUFFER, stdin) == NULL)
{
ok = 0;
}
else
{
if (stdin_buffer[strlen(stdin_buffer) - 1] == '\n')
{
stdin_buffer[strlen(stdin_buffer) - 1] = '\0';
}
char dummy;
int input;
if (sscanf(stdin_buffer, "%d%c", &input, &dummy) == 1)
{
buffer = (char) input;
write(child_input_stream, &buffer, sizeof(char));
child_available_input--;
}
}
}
}
return 0;
}
I am writing a simple code to implement the indirect input function for a unix/linux shell.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
extern void error(char* message);
void
cisshRedirectedInput(char* command[], char* inputFile)
{
//Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
//For the child process
if ((pid=fork())==0)
{
//Try to input files, failing on an error
fd=open(inputFile,O_RDONLY);//To read input file
if(fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
//use dup() to copy file
close(1);
if(dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
//Close file and exec()
close(fd);
execvp(command[0], command);
//If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if(wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if(status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
However, after compilation (no error reported), but when I try (fileList created already)
sort -r <fileList
The shell just stuck there without giving me answer, what is the problem please?
The standard input file descriptor is 0 (or STDIN_FILENO), not 1 (or STDOUT_FILENO).
Either use:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
close(0);
if (dup(fd) < 0) …
close(fd);
Or:
int fd = open(inputFile, O_RDONLY);
if (fd < 0) …
if (dup2(fd, 0) < 0) …
close(fd);
It is good that your code does the close(fd) after duplicating to a standard I/O descriptor — that is almost always correct. It's also good that you are checking that the key system calls succeed. (There isn't much you can do if close() fails.)
This simple modification of your code (key change: use close(0); instead of close(1);) works for me. Did you null terminate your argument list?
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static inline void error(char *message)
{
fprintf(stderr, "%s\n", message);
}
void
cisshRedirectedInput(char *command[], char *inputFile);
void
cisshRedirectedInput(char *command[], char *inputFile)
{
// Try to implement the RedirectInput from here
pid_t pid;
int status;
int fd;
// For the child process
if ((pid = fork()) == 0)
{
// Try to input files, failing on an error
fd = open(inputFile, O_RDONLY); // To read input file
if (fd < 0)
{
error("sampleSh: error opening standard input file");
exit(1);
}
// use dup() to copy file
close(0);
if (dup(fd) < 0)
{
error("sampleSh: error duplicating standard input");
perror("dup()");
exit(1);
}
// Close file and exec()
close(fd);
execvp(command[0], command);
// If failure in any case
error("sampleSh: failure to execute command");
exit(1);
}
else
{
/* This is the parent process.
* Wait for the child to terminate.
*/
if (wait(&status) < 0)
{
error("sampleSh: error waiting for child.");
perror("wait");
}
if (status != 0)
error("sampleSh: command exited with nonzero error status.");
}
}
int main(void)
{
char *args[] = { "sort", "-r", 0 };
cisshRedirectedInput(args, "fileList");
return 0;
}
Input file:
bash-assoc-arrays.sh
cissh.c
fileList
kwargs.py
makefile
posixver.h
rangeinc.c
select.c
spc.py
testcsv.py
uncrustify.bug
yield.py
Output:
yield.py
uncrustify.bug
testcsv.py
spc.py
select.c
rangeinc.c
posixver.h
makefile
kwargs.py
fileList
cissh.c
bash-assoc-arrays.sh
I have to implement a testing program(quiz), which besides displaying the question and reading the answer, it has to display the time left at each one minute past. After finishing the examination time, by finishing the questions or by running out of time,the program has to get back from the beginning, when before the start, we enter the name of the candidate. This implementation has to be done using processes. Below is the code that i have written so far. The problem is that i am not sure that i am making a good communication between the process and the subprocesses, especially because i am not using a pipe. Some opinions?
#include<stdio.h>
#include<sys/types.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
#define T 180
void firstChildAction(){
static const char filename[] = "/home/osystems01/laura/text";
char question[100];
char answer[100];
FILE *file = fopen(filename,"r");
if(file != NULL){
while(fgets(question,sizeof question,file) != NULL){
fputs(question, stdout);
scanf("%s",&answer);
}
fclose(file);
}
else{
perror(filename);
}
}
void secondChildAction(){
int i;
for(i = T; i >= 0; i-=60){
if( i/60 != 0){
printf("You have %d %s left.\n", i/60,(i/60 > 1)?"minutes":"minute");
sleep(60);
}
else{
printf("The time is over\n");
break;
}
}
}
int main() {
pid_t pidA;
pid_t pidB;
pid_t wPid;
char name[20];
while(1){
printf("Enter the candidate name or Quit to exit: \n");
scanf("%s",&name);
if(strcmp(name,"Quit") == 0 || strcmp(name,"quit") == 0){
printf("The program is terminating.....\n");
break;
}
else{
pidA = fork();
if(pidA == 0){
firstChildAction();
exit(0);
}
else{
pidB = fork();
if(pidB == 0){
secondChildAction();
exit(0);
}
}
int status;
while(wPid = wait(&status)) > 0 ){
if(WIFEXITED(status)){
int result = WEXITSTATUS(status);
printf("Exit status of %d is %d\n", wPid, result);
if(wPid == pidA){
kill(pidB,SIGTERM);
kill(pidA,SIGTERM);
}
else if(wPid == pidB){
kill(pidA,SIGTERM);
kill(pidB,SIGTERM);
}
}
}
}
}
return 0;
}
Pipes as such don't require you to provide a regular file, but they can have a unique, globally visible name, which is provided by a (unused) filename you have to specify. The contents of the file, if any, is handled by the library.
There are (simple) pipes for communication among related processes (such as a child and a parent process in the same process hierarchy) where the pipe handle can easily be passed to other processes.
The other flavor is called 'named pipes' for processes with any relation, where one can lookup the pipe handle using the global name (as explained in the answer of the question I linked). You can think of a pipe as of a directly connected speaking tube, allowing two processes to chitchat about whatever they like, using read and write functions. On Linux, a pipe is a simplex (at a time, one talks, the other one listens). One would nee two pipes for bidirectional async IO in this case (https://unix.stackexchange.com/questions/53641/how-to-make-bidirectional-pipe-between-two-programs). The immediate buffer for input and output is abstracted. Its just like with network sockets.
I'd suggest to compile this nice example in the accepted answer to play around with: https://stackoverflow.com/a/2789967/1175253
Edit
Example code with error handling. Treat pipe.h & pipe.c as a library (link NamedPipeReader and NamedPipeWriter against it).
This code would need further testing, however, the code is able to (re)open named pipes in any order.
pipe.h
#ifndef PIPE_H_
#define PIPE_H_
//C headers
#include <errno.h>
#include <assert.h>
//Linux headers
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C"
{
#endif
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created);
#ifdef __cplusplus
}
#endif
#endif /* PIPE_H_ */
pipe.c
#include "pipe.h"
#include <stdio.h>
int open_named_pipe(const char* const name, const int permissions, const int mode, int* pipe_created)
{
int fd;
assert(name);
assert(permissions);
assert(pipe_created);
//Create or use an existing pipe special file
if (0 == mkfifo(name, permissions))
{
*pipe_created = 1;
printf("Successfully created named pipe '%s'\n", name);
}
else
{
switch (errno)
{
case EEXIST:
//this is OK, as the other process might already has created the special file
printf("Opened existing named pipe '%s'\n", name);
break;
default:
fprintf(stderr, "Failed to create or access named pipe '%s'\n", name);
perror(" ");
return -1;
};
}
fd = open(name, mode);
if (fd < 0)
{
perror("Could not open pipe for writing");
if (*pipe_created)
{
if (0 == unlink(name))
{
*pipe_created = 0;
}
else
{
perror("Failed to unlink named pipe");
}
}
}
return fd;
}
NamedPipeReader.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t read_buffer_size = 1024; //[bytes]
const size_t read_retry_delay = 25000; //[us]
int fd = -1;
int pipe_created = 0;
char* read_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (read_buffer)
free(read_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
int main()
{
//Locals
int run = 1;
int received = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
read_buffer = (char*) malloc(read_buffer_size);
if (!read_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_RDONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
assert(fd >= 0);
assert(read_buffer_size > 1);
received = read(fd, read_buffer, read_buffer_size - 1);
if (received > 0)
{
//add a NUL char for string termination
read_buffer[received] = '0';
printf("local process %llu received: %s\n", (unsigned long long) getpid(), read_buffer);
}
else if (received == 0)
{
//EOF reached, this happens in case the writer has closed its handle.
//Perform a delayed restart and recreate the named pipe
usleep(read_retry_delay);
printf("Restarting...\n");
goto restart;
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is empty,
//happens when opened with the O_NONBLOCK flag
usleep(read_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
return EXIT_SUCCESS;
}
NamedPipeWriter.c
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "pipe.h"
//Globals
const char* const pipe_name = "/tmp/myfifo";
const int pipe_permissions = 0600;
const size_t write_buffer_size = 1024; //[bytes]
const size_t write_retry_delay = 25000; //[us]
const size_t write_interval = 1000000;
int fd = -1;
int pipe_created = 0;
char* write_buffer = NULL;
//Handles EPIPE signal
void signal_handler(int signal)
{
fprintf(stderr, "cought signal %d\n", signal);
}
//Handles cleanup on exit
void exit_handler(void)
{
if (write_buffer)
free(write_buffer);
if (fd >= 0)
close(fd);
//if this process created the FIFO, we unlink it
if (pipe_created == 0)
unlink(pipe_name);
}
//Main Function
int main()
{
//Locals
int run = 1;
int sent = 0;
int msg_len = 0;
//Install the exit handler
atexit(&exit_handler);
signal(EPIPE, signal_handler);
signal(EACCES, signal_handler);
//Allocate the buffer
write_buffer = (char*) malloc(write_buffer_size);
if (!write_buffer)
{
perror("Failed to allocate buffer");
return EXIT_FAILURE;
}
restart: ;
//Close if already open
if(fd >= 0)
close(fd);
//Create or use an existing pipe special file
fd = open_named_pipe(pipe_name, pipe_permissions, O_WRONLY, &pipe_created);
if (fd < 0)
{
return EXIT_FAILURE;
}
while (run)
{
//Print message into the buffer
msg_len = snprintf(write_buffer, write_buffer_size, "Greetings from process %llu\n", (unsigned long long) getpid());
{
char* msg_ptr = write_buffer;
char* msg_end = write_buffer + msg_len;
while (msg_ptr != msg_end)
{
assert(fd >= 0);
assert(msg_ptr < msg_end);
sent = write(fd, msg_ptr, msg_end - msg_ptr);
if (sent > 0)
{
msg_ptr += sent;
}
else if (sent == 0)
{
//retry delay for nonblocking writes
usleep(write_retry_delay);
}
else
{
switch (errno)
{
case EAGAIN:
//Wait, if the pipe is full,
//happens when opened with the O_NONBLOCK flag
usleep(write_retry_delay);
break;
case EPIPE:
case EBADF:
case EBADFD:
perror("Pipe error");
printf("Restarting...\n");
goto restart;
default:
perror("Pipe error");
return EXIT_FAILURE;
};
}
}
printf("Written: %s\n", write_buffer);
usleep(write_interval);
}
}
return EXIT_SUCCESS;
}