Search code examples
cprocessfork

How to implement a logger function as a child process in c?


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.


Solution

  • 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;