views:

264

answers:

3

Suppose a long-running process writes to a log file. Suppose that log file is kept open indefinitely. Suppose that a careless system administrator deletes that log file. Can the program detect that this has happened?

Is it safe to assume that fstat() will report a link count of zero for a deleted file?

Truncation, it seems to me, is slightly trickier. In part, it depends on whether the file descriptor is running in O_APPEND mode. If the log file is not running with O_APPEND, then the current write position of the program's log descriptor doesn't change, and the truncation removes the leading bytes, but the program continues to write at 'the end', leaving a gap of phantasmal zero bytes (they read as zeroes, but don't necessarily occupy space on disk). If the program runs with O_APPEND, then it will write at the end of the file as it currently exists. The only way to observe the truncation is to note that the file position is not where the program expected it - which in turn means tracking that position explicitly.

On the whole, I'm not so worried about truncation as deletion, but any thoughts would be welcome.

+2  A: 

Checking that fstat() returns a link count of zero would fail if the file was hard linked or renamed. I would probably instead periodically compare stat()'s inode number against fstat()'s.

I'm not sure about truncation.

tail -F checks for deletion and maybe truncation, so I'd check its source to see how it implements it.

Josh Kelley
Good point on the link count not being zero if the file was hard linked. For my purposes, renaming is not a problem. I'll need to think about whether linking is a problem - the data is still available via some name, which is the key thing.
Jonathan Leffler
+1  A: 

You could use inotify to watch your log file, monitoring it for file system events.

Jeff Bauer
Interesting software - but I'm looking at it from the perspective of the program writing the log file, not from the outside 'consumer' of the log file.
Jonathan Leffler
+2  A: 

Suppose the careless system administrator kills the process. Do you really want to protect against the admin doing random things? I guess you're just looking for a way to start a new logfile from time to time, like using logrotate. There it is enough to provide a way to manually let the program reopen the logfile. The standard way to do this is to listen for the HUP-Signal in the program, and reopen the logfile if it arrives:

#include <signal.h>

volatile int f_sighup;

void sighup_handler() {
  f_sighup = 1;
}

void trap_sighup() {
  struct sigaction sa;
  int rv;

  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = &sighup_handler;
  rv = sigaction(SIGHUP, &sa, NULL);
  if (-1 == rv) {
    fprintf(stderr, "warning: setting SIGHUP signal handler failed");
  }
}

int main() {
  f_sighup = 0;
  trap_sighup();
  ...
}

Then regularly check the f_sighup flag in the main program to see if the logfile should be reopened. This plays nice with tools like logrotate, which can rename the old logfile and then call kill -s HUP $PID. And the careless sysadmin can do this manually, after deleting (or better renaming) the old logfile.

sth
Should that be "sig_atomic_t volatile f_sighup = 0;"?
Jonathan Leffler
Probably can't hurt, though I would assume int to be always atomic. volatile might be a good idea in any case.
sth