** Description changed:
- Cronlog, when used with -S/-l or -P, fails to maintain a symlink to the
+ Cronolog, when used with -S/-l or -P, fails to maintain a symlink to the
newest logfile if the previous link target is moved.
- Consider a case where Apache writing out to cronolog:
+ Consider a case where Apache is writing out to cronolog:
CustomLog |/usr/sbin/cronolog -S /foo/access_log
/foo/%Y/%m/%d/access_log
Now, if no one hits that site for a day or so, and your out-of-band log
- archiving processes go and compress the old log files, the symlink's
- broken; cronolog will subsequently fail to re-link /foo/access_log,
- though logs WILL be written to /foo/%Y/%m/%d
+ archiving processes go and compress your old log files, the access_log
+ symlink's broken; cronolog will subsequently fail to re-link
+ /foo/access_log, though logs WILL be written to /foo/%Y/%m/%d
Ultimately, this boils down to cronolog calling stat() against the
symlink; stat() will return -1 on the broken link and skip the unlink()
- code; cronolog should be calling lstat().
+ code; the subsequent call to symlink() will fail because an access_log
+ symlink already exists. cronolog should be calling lstat(), since it
+ cares about the symlink, not the actual target.
+
+ From lstat(2):
+lstat() is identical to stat(), except that if path is a symbolic
link, then the link itself is stat-ed, not the file that it refers to.
+
+ from symlink(2):
+If newpath exists it will not be overwritten.
+ ...
+EEXIST newpath already exists.
Here's an example:
gkuchta@ve:~/dev/cronolog$ mkdir -p 2012/06/11
gkuchta@ve:~/dev/cronolog$ echo foo 2012/06/11/access_log
gkuchta@ve:~/dev/cronolog$ ln -s 2012/06/11/access_log access_log
- gkuchta@ve:~/dev/cronolog$ gzip 2012/06/11/access_log
+ gkuchta@ve:~/dev/cronolog$ gzip 2012/06/11/access_log
gkuchta@ve:~/dev/cronolog$ ls -l
total 4
drwxr-xr-x 3 gkuchta gkuchta 4096 2012-06-12 13:37 2012
lrwxrwxrwx 1 gkuchta gkuchta 21 2012-06-12 13:37 access_log -
2012/06/11/access_log
gkuchta@ve:~/dev/cronolog$ echo bar | /usr/bin/cronolog -S
/home/gkuchta/dev/cronolog/access_log
/home/gkuchta/dev/cronolog/%Y/%m/%d/access_log
gkuchta@ve:~/dev/cronolog$ cat access_log
cat: access_log: No such file or directory
gkuchta@ve:~/dev/cronolog$ ls -l
total 4
drwxr-xr-x 3 gkuchta gkuchta 4096 2012-06-12 13:37 2012
lrwxrwxrwx 1 gkuchta gkuchta 21 2012-06-12 13:37 access_log -
2012/06/11/access_log
gkuchta@ve:~/dev/cronolog$ find .
.
./2012
./2012/06
./2012/06/11
./2012/06/11/access_log.gz
./2012/06/12
./2012/06/12/access_log
./access_log
If we catch the error from symlink() we see what's happening.
- gkuchta@ve:~/scratch$ diff cronolog-1.6.2/src/cronoutils.c
cronolog-1.6.2-patch/src/cronoutils.c
+ gkuchta@ve:~/scratch$ diff cronolog-1.6.2/src/cronoutils.c
cronolog-1.6.2-patch/src/cronoutils.c
214c214,217
symlink(pfilename, linkname);
---
if (symlink(pfilename, linkname) != 0)
{
perror(symlink);
}
-
gkuchta@ve:~/dev/cronolog$ mkdir -p 2012/06/11
gkuchta@ve:~/dev/cronolog$ echo foo 2012/06/11/access_log
- gkuchta@ve:~/dev/cronolog$ ln -s 2012/06/11/access_log
- gkuchta@ve:~/dev/cronolog$ gzip 2012/06/11/access_log
+ gkuchta@ve:~/dev/cronolog$ ln -s 2012/06/11/access_log
+ gkuchta@ve:~/dev/cronolog$ gzip 2012/06/11/access_log
gkuchta@ve:~/dev/cronolog$ echo bar |
/home/gkuchta/scratch/cronolog/sbin/cronolog -S
/home/gkuchta/dev/cronolog/access_log
/home/gkuchta/dev/cronolog/%Y/%m/%d/access_log
symlink: File exists
Here's what I believe to be the appropriate behavior. While digging
around for where to submit this patch, it looks like someone else
submitted something similar about 4 years ago, but the cronolog mailing
list is dead and the maintainer seems AWOL.
- gkuchta@ve:~/$ cat cronoutils.patch
+ gkuchta@ve:~/$ cat cronoutils.patch
--- cronolog-1.6.2/src/cronoutils.c 2001-05-03 16:43:21.0 +
+++ cronolog-1.6.2-patch/src/cronoutils.c 2012-06-12 14:33:15.0
+
@@ -195,11 +195,11 @@
- {
- struct stat stat_buf;
-
+ {
+ struct stat stat_buf;
+
-if (stat(prevlinkname, stat_buf) == 0)
+if (lstat(prevlinkname, stat_buf) == 0)
- {
- unlink(prevlinkname);
- }
+ {
+ unlink(prevlinkname);
+ }
-if (stat(linkname, stat_buf) == 0)
+if (lstat(linkname, stat_buf) == 0)
- {
- if (prevlinkname) {
- rename(linkname, prevlinkname);
-
+ {
+ if (prevlinkname) {
+ rename(linkname, prevlinkname);
gkuchta@ve:~/dev/cronolog$ mkdir -p 2012/06/11
gkuchta@ve:~/dev/cronolog$ echo foo 2012/06/11/access_log
gkuchta@ve:~/dev/cronolog$ ln -s 2012/06/11/access_log access_log
- gkuchta@ve:~/dev/cronolog$ gzip 2012/06/11/access_log
+ gkuchta@ve:~/dev/cronolog$ gzip 2012/06/11/access_log