Program with mkfifo() and open() cannot exit - c

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();
}

Related

bug: the fifo file remains present even after the daemon dies

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>.

Named pipe, read fail with bad file descriptor

I'm beginning with pipes under Linux and I have a problem with my code.
I wanted to test sending an integer through the fifo so I code a small program to test it.
First I open the read only descriptor and then the write only one as precised in the documentation.
I send the number to the pipe and close the write descriptor.
However, when I'm trying to read from the pipe, it says that I have a bad file descriptor, I don't understand why it's not working because as the descriptor is the good one and is oppened with good option (O_RDONLY | O_NONBLOCK).
Here is my code :
void main(void)
{
int modePipeWrite, modePipeRead;
if(mkfifo("test.fifo", 0777) == -1)
{
perror("mkfifo");
exit(EXIT_FAILURE);
}
if(modePipeRead = open("test.fifo", O_RDONLY | O_NONBLOCK) == -1)
{
perror("openRead");
exit(EXIT_FAILURE);
}
if(modePipeWrite = open("test.fifo", O_WRONLY | O_NONBLOCK) == -1)
{
perror("openWrite");
exit(EXIT_FAILURE);
}
int n = 0;
if(write(modePipeWrite, &n, sizeof(n)) == -1)
{
perror("write");
}
printf("Send value: %d\n", n);
close(modePipeWrite);
int mode;
while(1)
{
if(read(modePipeRead, &mode, sizeof(mode)) == -1)
{
perror("read");
}
printf("Received value: %d\n", getpid(), mode);
sleep(1);
}
}
And the output :
./a.out
Send value: 0
read: Bad file descriptor
Received value: -1877110288
read: Bad file descriptor
Received value: -1877110288
read: Bad file descriptor
Received value: -1877110288
I don't understand what could be wrong here. If someone have some advices I would be glad to hear it.
Your code has a couple of issues, which you would've found if you compiled it with all warnings enabled. If you use gcc, the following options are nice/mandatory:
-Wall -Wextra -Werror -pedantic
One issue is that you don't assign the return value of open() to the file descriptors. You need to add braces around the assignment, or move it out of the if-statements.
Here's a working example, using a thread to read:
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <pthread.h>
static void *reader(void *arg)
{
int fd, val;
if ((fd = open("test.fifo", O_RDONLY)) == -1) {
perror("openRead");
exit(EXIT_FAILURE);
}
if (read(fd, &val, sizeof val) == -1)
perror("read");
else
printf("Received value: %d\n", val);
close(fd);
return NULL;
}
int main(void)
{
int modePipeWrite, modePipeRead;
pthread_t readerid;
if (mkfifo("test.fifo", 0777) == -1) {
perror("mkfifo");
exit(EXIT_FAILURE);
}
pthread_create(&readerid, NULL, reader, NULL);
sleep(1);
if ((modePipeWrite = open("test.fifo", O_WRONLY)) == -1) {
perror("openWrite");
exit(EXIT_FAILURE);
}
int n = 1234;
if (write(modePipeWrite, &n, sizeof n) == -1)
perror("write");
else
printf("Sent value: %d\n", n);
sleep(1);
close(modePipeWrite);
return 0;
}

Simple shell with indirect input

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

daemonize of a process?

I'm learning apue and I try to daemonize a process according to the code sample in apue. The code is as follows:
#include <syslog.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/resource.h>
int damonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa;
umask(0);
if(getrlimit(RLIMIT_NOFILE, &rl) < 0)
{
return 1;
}
if((pid = fork()) < 0)
{
return 2;
}
else if(pid != 0)
{
exit(0);
}
setsid();
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGHUP, &sa, NULL) < 0)
{
return 3;
}
if((pid = fork()) < 0)
{
return 4;
}
else if(pid > 0)
{
exit(0);
}
if(chdir("/") < 0)
{
return 5;
}
if(rl.rlim_max == RLIM_INFINITY)
{
rl.rlim_max = 1024;
}
for(i = 0; i < rl.rlim_max; i++)
{
close(i);
}
fd0 = open("/dev/null", O_RDWR);
fd1 = dup(0);
fd2 = dup(0);
openlog(cmd, LOG_CONS, LOG_DAEMON);
if(fd0 != 0 || fd1 != 1 || fd2 != 2)
{
syslog(LOG_ERR, "unexpected file descriptors %d %d %d\n", fd0, fd1, fd2);
return 6;
}
}
int main(void)
{
FILE *fp;
int id;
fp = fopen("test.txt", "w+");
id = damonize("ls");
fprintf(fp, "%d", id);
fclose(fp);
exit(0);
}
I run the above program and use ps -axj, but there's no daemon process created by the program, and threre's no output in the file test.txt. My question is
What's wrong in my code? What causes the above two problems?
You won't see the daemonized process because it doesn't stick around. After it tries writing to the file, it exits.
But it won't write to the file, because your daemonize routine closes every file handle (and that's what fopen() uses under the hood). Try opening the file in main() after daemonize(), or in the loop closing all file descriptors, exclude the one associated with the file using fileno().
I am afraid that your program is oversophisticated (if such a word exists in English). You are spawning one child process, exit parents, then the child spawns another child and exits. The child of the child is then closing all possible (even not opened) file descriptors, then it opens "/dev/null" and redirect standard input, output and error there. The "daemonisation" is finished and your program tries to write some number into a file "fp" in the main function. However, this fp have been closed long time ago in daemonize.
In other words, the main problem is that your daemonize function is closing all possible file descriptors in the loop:
for(i = 0; i < rl.rlim_max; i++) close(i);
However, if you want to daemonize a process why not to start with a simple solution and once it works you can add features while keeping it working. For example if you start with:
int daemonize() {
pid_t pid;
pid = fork();
if (pid > 0) exit(EXIT_SUCCESS);
if (pid < 0) printf("Can't fork\n");
return(pid);
}
Then you can add code for closing standard input close(STDIN_FILENO); before return, and so on. After each modification test if it is still working.

UNIX FIFO: the process hangs if I don't close the input side of the fifo

I've just started working with UNIX FIFOs, and I discovered something while experimenting with my first FIFO program. The program works this way: after creating the FIFO, two processes are started using the fork() function. The child process reads what the father passes to him through the FIFO, and prints it on the screen. The data exchanged is the string specified as an argument. The question is: in the father section, if I forget to close the input side of the FIFO (meaning that I exclude the close(fd) line) the program would just hang, even if the data between the processes is exchanged correctly. Otherwise, everything works fine and the program terminates withouth hanging. Can someone please explain me why?
Thanks for your patience. Here is the code of the main function:
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("An argument must be specified\n");
return -1;
}
int ret = mkfifo("./fifo.txt", 0644);
char buf;
if(ret < 0)
{
perror("Error creating FIFO");
return -1;
}
pid_t pid = fork();
if(pid < 0)
{
perror("Error creating child process");
return -1;
}
if(pid == 0) /* child */
{
int fd = open("./fifo.txt", O_RDONLY); /* opens the fifo in reading mode */
while(read(fd, &buf, 1) > 0)
{
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(fd);
return 0;
}
else /* father */
{
int fd = open("./fifo.txt", O_WRONLY); /* opens the fifo in writing mode */
write(fd, argv[1], strlen(argv[1]));
close(fd);
waitpid(pid, NULL, 0);
return 0;
}
}
read(2) blocks until there are characters available or the channel is closed at the other end. The father process must close the pipe in order for the last child read() to return. If you omit the close(fd) in the father, the child will block in the read() until the father exits (closing the pipe automatically) but father will hang in waitpid() until the child exits.
First things first: there are several issues with the code you posted.
There are no #include directives, hence no prototypes in scope for any of the functions you call. C89 requires prototypes for variadic functions such as printf(); C99 requires prototypes for all functions. Both C89 and C99 require declarations in scope for O_RDONLY, O_WRONLY, STDOUT_FILENO and NULL.
-1 is not an allowed return value for main().
C89 does not allow mixing declarations and statements.
A minor nit: the usual nomenclature is "parent and child", not "father and child".
I have modified your program to correct this issue and improve readability:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
if (argc != 2) {
printf("An argument must be specified\n");
return 1;
}
int ret = mkfifo("./fifo.txt", 0644);
char buf;
if (ret < 0) {
perror("Error creating FIFO");
return 1;
}
pid_t pid = fork();
if (pid < 0) {
perror("Error creating child process");
return 1;
}
if (pid == 0) { /* child */
int fd = open("./fifo.txt", O_RDONLY); /* opens the fifo in reading mode */
while(read(fd, &buf, 1) > 0) {
write(STDOUT_FILENO, &buf, 1);
}
write(STDOUT_FILENO, "\n", 1);
close(fd);
return 0;
} else { /* parent */
int fd = open("./fifo.txt", O_WRONLY); /* opens the fifo in writing mode */
write(fd, argv[1], strlen(argv[1]));
close(fd);
waitpid(pid, NULL, 0);
return 0;
}
}
But most importantly, you did not mention what operating system and compiler you are using.
I am unable to reproduce the issue, and I suspect it may be related to one of the issues listed above.

Resources