I've been struggling to implement a logger function in C that records when messages are written to a text file using communication via a pipe. In the simplified implementation below I'm trying to write messages from the parent process and print them from the child process without the file I/O but I don't ever get the child printfs.
In my main
function, I spawn the logger by calling spawn_logger
which forks a child process (log_message
) that will run continuously. The parent process returns to the main, starts to send messages through the pipe, and finally kills the child process.
The main
function:
int main(void){
spawn_logger();
char wmsg[] = "Greetings";
send_message(wmsg);
strcpy(wmsg, "Hello");
send_message(wmsg);
kill_child();
return 0;
}
The spawn_logger
function:
// global vars
pid_t pid;
int fd[2];
int spawn_logger() {
if (pipe(fd) == -1) {
printf("Pipe failed\n");
return -1;
}
pid = fork();
if (pid < 0) { // fork error
printf("fork failed");
return -1;
}
if (pid > 0) { // parent process
close(fd[READ_END]);
return 0; // return to main
}
// child process
// spawn the receiver process
log_message();
// the receiver process will never reach this point
return 0;
}
The send_message
function:
int send_message(char message[]){
// do something with the message
// e.g. write in a file
printf("Message by parent sent: %s \n", message);
// write the message to logger process
int n = strlen(message) + 1;
write(fd[WRITE_END], &n, sizeof(int));
write(fd[WRITE_END], &message, sizeof(char) * strlen(message));
return 0;
}
The log_message
and kill_child
functions:
// global vars
extern pid_t pid;
extern int fd[2];
int log_message(){
//child process
// will read from the pipe every time the parent process writes to it
close(fd[WRITE_END]);
int n;
char *message;
// read messages until parent process closes the pipe
while (read(fd[READ_END], &n, sizeof(int)) > 0) {
message = malloc(sizeof(char) * n);
read(fd[READ_END], &message, sizeof(char) * n);
printf("Message by logger received: %s \n", message);
}
close(fd[READ_END]);
exit(0);
}
int kill_child(){
close(fd[WRITE_END]);
kill(pid, SIGKILL);
return 0;
}
When I run the program all I get are the print messages printf("Message by parent sent: %s \n", message);
and I think the problem comes from log_message
.
I thought the child process would remain stuck in the while loop trying to read the buffer as long as the parent's write end is open but while debugging the child process in Clion I noticed that once it reaches the first line the program just stops. When I debug the parent process it just goes over all the writing instructions without any broken pipe
errors.
How can I fix that? Thanks in advance for the help.
After solving the issues pointed out by by andrew-henle and some-programmer-dude, I found the following solution to work the best.
int log_message(){ //child process
close(fd[WRITE_END]);
char buffer[BUFSIZ];
char message[BUFSIZ];
FILE * log;
log = fopen("gateway.log", "a");
// read is looping over every byte in the pipe
// and is a blocking call until there's something to read
// or the pipe is closed
while(read( fd[READ_END], buffer, BUFSIZ) > 0 ) {
int j = 0;
memset(message, ' ', BUFSIZ); // make sure its empty
// look for null terminator in buffer
for(int i= 0; i < BUFSIZ; i++){
// copy every byte until the null terminator
message[i-j] = buffer[i];
if(buffer[i] == '\0'){
if(message[0] != '\0'){
printf("Message by logger received: %s \n", message);
}
buffer[i] = ' ';
// reset j such that i - j is 0 for the next char of the buffer
j = i + 1;
}
}
memset(message, ' ', BUFSIZ); // clear message
}
fclose(log); // for now, we'll open and close the log file every time
close(fd[READ_END]);
kill(getpid(), SIGSEGV);
return 0;