File Locks on Linux
Speaking of locks if you are writing something that can run a long time and produces a log file it might be nice if you locked the log file before appending a new log entry.<p>If you do that, someone who wants to rotate logs can do it by (1) locking the log file, (2) copying it to the rotated file, (3) truncating the log file, and (4) releasing the lock.<p>Here&#x27;s a C program, cat-trunc, that does that. Usage is &quot;cat-trunc logfile&quot;.<p><pre><code>  #include &lt;stdio.h&gt;
  #include &lt;sys&#x2F;types.h&gt;
  #include &lt;sys&#x2F;stat.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;unistd.h&gt;

  char buf[65536];

  int main(int argc, char *argv[])
  {
    struct flock l;
    ssize_t got;
    ssize_t copied = 0;
    off_t eof;
    int finished = 0;
    int status = 0;

    if (argc != 2)
    {
        fprintf(stderr, &quot;%s: error: no log file specified!\n&quot;, argv[0]);
        return 1;
    }

    int fd = open(argv[1], O_RDWR);
    if (fd == -1)
    {
        perror(argv[0]);
        return 2;
    }

    l.l_whence = SEEK_SET;
    l.l_start = 0;
    l.l_len = 0;

    while (! finished) {
        got = read(fd, buf, sizeof buf);
        if (got &gt; 0) {
            copied += got;
            if (write(1, buf, got) != got) {
                status = -1;
                finished = 1;
                perror(argv[0]);
            }
        } else if (got == 0) {
            l.l_type = F_WRLCK;
            fcntl(fd, F_SETLKW, &amp;l); 
            eof = lseek(fd, 0, SEEK_END);
            if (eof == copied) {
                ftruncate(fd, 0);
                finished = 1;
            } else {
                lseek(fd, copied, SEEK_SET);
            }
            l.l_type = F_UNLCK;
            fcntl(fd, F_SETLKW, &amp;l);
        } else {
            status = -1;
            finished = 1;
            perror(argv[0]);
        }
    }
    return status;
  }
</code></pre>
That&#x27;s a little more complicated than is necessary, because it tries to minimize the time it holds the lock.<p>If you&#x27;ve got a long running program whose stdout you want to log here&#x27;s a C++ program (but written like it is C plus std::string and std::list because I am lazy), lock-save, to help with that. Usage is &quot;lock-save [-t] file&quot;. It copies stdin to file, writing a line at a time, locking the file for those writes. You can then use cat-trunc to rotate the file. The -t option makes it also copy stdin to stdout, making it essentially tee with locks.<p><pre><code>  #include &lt;stdio.h&gt;
  #include &lt;sys&#x2F;types.h&gt;
  #include &lt;sys&#x2F;stat.h&gt;
  #include &lt;pwd.h&gt;
  #include &lt;fcntl.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;string&gt;
  #include &lt;list&gt;

  std::list&lt;std::string&gt;  lines;

  int main(int argc, char *argv[])
  {
    char * prog_name = argv[0];
    char * file_name = 0;
    int t_flag = 0;
    struct flock l;
    int c;
    std::string line;

    if (argc &gt; 1 &amp;&amp; strcmp(argv[1], &quot;-t&quot;) == 0) {
        t_flag = 1;
    }
    if (argc != 2+t_flag) {
        fprintf(stderr, &quot;Usage: %s [-t] file\n&quot;, prog_name);
        return 1;
    }
    file_name = argv[1+t_flag];

    int fd = open(file_name, O_RDWR | O_CREAT | O_APPEND, 0666);
    if (fd == -1) {
        perror(prog_name);
        return 2;
    }

    l.l_whence = SEEK_SET;
    l.l_start = 0;
    l.l_len = 0;
    while ((c = getchar()) != EOF) {
        if (t_flag) {
            putchar(c);
        }
        line += c;
        if (c == &#x27;\n&#x27;) {
            lines.push_back(line);
            l.l_type = F_WRLCK;
            if (fcntl(fd, F_SETLK, &amp;l) != -1) {
                while (lines.size())
                {
                    line = lines.front();
                    lines.pop_front();
                    write(fd, line.c_str(), line.length());
                }
                l.l_type = F_UNLCK;
                fcntl(fd, F_SETLK, &amp;l);
                line = &quot;&quot;;
            }
        }
    }
    return 0;
  }</code></pre>