Sanket Joshi
Sanket Joshi

Reputation: 158

Read a continuously updated file and wait for new data to be written to the file in C

I have a log file and I want to write a program in C to read that log file and wait for new data if EOF is reached. I do not want to use tail -f in my code. I tried the following code but it is not working:

#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <unistd.h>

#define PATH "/home/sanket/demo.txt"

int main(void)
{
        FILE * fp;
    int fd;
    char * line = NULL;
    size_t len = 0;
    ssize_t read1;
    int notify_fd;
    int wd,length ,i=0;
    char buffer[EVENT_BUF_LEN];

    fp = fopen(PATH, "r");
    if (fp == NULL)
            exit(EXIT_FAILURE);

    fd = fileno(fp);
    while (1)
    {
            read1 = getline(&line, &len, fp);
            if(read1 != -1)
                    printf("%s",line);
            else
            {
                    lseek(fd,0,SEEK_DATA);
            }
    }

    if (line)
            free(line);
    exit(EXIT_SUCCESS);
}

Upvotes: 0

Views: 2038

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753675

When you reach the first end of file, the input stream flag for EOF is set. That has to be cleared before you can resume operations (clearerr(fp)). You should normally sleep, too. (Using lseek() on the file descriptor associated with the stream won't help.)

Here's a program based on your code — I had to change the value of PATH (which isn't a particularly good name):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define PATH "demo.txt"

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;

    fp = fopen(PATH, "r");
    if (fp == NULL)
    {
        perror(PATH);
        exit(EXIT_FAILURE);
    }

    while (1)
    {
        if (getline(&line, &len, fp) != -1)
            printf("%s",line);
        else
        {
            printf("EOF\n");
            sleep(1);
            clearerr(fp);
        }
    }

    if (line)
        free(line);
    return(EXIT_SUCCESS);
}

If you have a program that generates data to a file, you can test with it. I have a program dribbler that does that:

$ dribbler -f demo.txt -s 1.5 -r 0.5 &
[1] 20678
$ cat demo.txt
0: message written to file
1: message written to file
2: message written to file
$ tail11
0: message written to file
1: message written to file
2: message written to file
3: message written to file
4: message written to file
5: message written to file
EOF
6: message written to file
EOF
EOF
7: message written to file
EOF
8: message written to file
EOF
EOF
9: message written to file
EOF
10: message written to file
EOF
11: message written to file
EOF
EOF
^C
$

The options to dribbler are:

Usage: dribbler [-hlntV][-s nap.time][-r std.dev][-f outfile][-i infile][-m message][-o openstr][-F format]
  -V           Print version information and exit
  -f outfile   Write to named file (dribbler.out)
  -h           Print this help message and exit
  -i infile    Read lines from input file
  -l           Loop back to start of input file on EOF
  -m message   Write message on each line of output
  -n           Number lines read from input file
  -o openstr   Flags passed to fopen() (a+)
  -s nap.time  Sleep for given interval between writes (1.000 second)
  -r std.dev   Randomize the time (Gaussian around nap.time with std.dev)
  -t           Write to standard output instead of file
  -F format    Printf format to use instead of %zu

So, it was writing to the file demo.txt at a mean time of 1.5 seconds, with a Gaussian random distribution with 0.5 seconds standard deviation. That's why there were sometimes 2 EOF messsages between successive lines of output.

Upvotes: 4

Related Questions