cron script to rotate log files

I have a mac server. I have been having problems with my logs. My hard disk became full, when i researched into why it was full it was due to massive log files. There was barley any log rotation policies in place on the server. I tired to use logrotate. This doesn't work on my server. It is a MAC server and doesn't seems to have logrotate. However some of the log files on the server had been being rotated. I looked into why. I found a file that by default is run at 4:30 every Saturday. it is /etc/weekly. I opened the file and found some script that was doing the logrotate on the files that had been being rotated. The script looked like this.

echo ""
printf %s "Rotating log files:"
cd /var/log
for i in ftp.log lookupd.log lpr.log mail.log netinfo.log hwmond.log ipfw.log p$
    if [ -f "${i}" ]; then
        printf %s " $i"
        if [ -x /usr/bin/gzip ]; then gzext=".gz"; else gzext=""; fi
        if [ -f "${i}.3${gzext}" ]; then mv -f "${i}.3${gzext}" "${i}.4${gzext}$
        if [ -f "${i}.2${gzext}" ]; then mv -f "${i}.2${gzext}" "${i}.3${gzext}$
        if [ -f "${i}.1${gzext}" ]; then mv -f "${i}.1${gzext}" "${i}.2${gzext}$
        if [ -f "${i}.0${gzext}" ]; then mv -f "${i}.0${gzext}" "${i}.1${gzext}$
        if [ -f "${i}" ]; then mv -f "${i}" "${i}.0" && if [ -x /usr/bin/gzip ]$
        touch "${i}" && chmod 640 "${i}" && chown root:admin "${i}"
    fi
done
if [ -f /var/run/syslog.pid ]; then kill -HUP $(cat /var/run/syslog.pid | head $
echo ""

Cool I had found the script that did the log rotations. Bravely i decided to edit that script. I edited it. I ensured the following.

  1. I could add my own log files to the for loop
  2. Since the log files i was adding would have different permissions i would need to return octal file permissions, the user group and user of that log file. so i could use it in the touch, chmod and chown command
  3. I wanted it to only rotate logs if the size of the log file was over 2MB. Otherwise not to bother.

So i designed my script to abide by those rules.
Here is my script

echo ""
printf %s "Rotating log files:"
cd /var/log
# Add new logs to this list
for i in daily_out.log asl.log monthly.out weekly.out lastlog windowserver.log SerialNumberSupport.log servermgrd.log rsync_backup.log crashreporter.log ftp.log lookupd.log lpr.log mail.log netinfo.log hwmond.log ipfw.log ppp.log secure.log; do
    if [ -f "${i}" ]; then
        #Mods by Tim Golding - shell noob. this mod makes sure logrotations only occure if file over 2M. Should allow not system logs to be added to the for loop.
        file_size=`ls -l "${i}"| awk '{printf "%s",$5}'`
        max=2097152
        if [ $file_size -gt $max ]; then
        #end of mods                                                                           
                ch_mod=`ls -l "${i}"| awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf("%0o ",k);print}'| awk '{printf "%s",$1}'`
                this_group=`ls -l "${i}"| awk '{printf "%s",$3}'`
                this_user=`ls -l "${i}"| awk '{printf "%s",$4}'`
                printf %s " $i"
                if [ -x /usr/bin/gzip ]; then gzext=".gz"; else gzext=""; fi
                if [ -f "${i}.3${gzext}" ]; then mv -f "${i}.3${gzext}" "${i}.4${gzext}"; fi
                if [ -f "${i}.2${gzext}" ]; then mv -f "${i}.2${gzext}" "${i}.3${gzext}"; fi
                if [ -f "${i}.1${gzext}" ]; then mv -f "${i}.1${gzext}" "${i}.2${gzext}"; fi
                if [ -f "${i}.0${gzext}" ]; then mv -f "${i}.0${gzext}" "${i}.1${gzext}"; fi
                if [ -f "${i}" ]; then mv -f "${i}" "${i}.0" && if [ -x /usr/bin/gzip ]; then gzip -9 "${i}.0"; fi; fi
                touch "${i}" && chmod "${ch_mod}" "${i}" && chown "${this_group}":"${this_user}" "${i}"
        fi
    fi
done
if [ -f /var/run/syslog.pid ]; then kill -HUP $(cat /var/run/syslog.pid | head -1); fi
echo ""

So i wrote this script telling myself how clever i was. Until Saturday when the script run. There were only two log files over 2M. asl.log and weekly_out.log. They were both huges. the script had backed up the old logs gzipped the backups and created new logs with the correct permissions. But the new logs are 0 bytes and are not being written to. What have a done wrong. Is it something to do with not killing the process as is done in the bottom line of the script with syslog.pid. The bottom line is tired to improve the logs policies but have made the situation alot worse. Can anyone offer any advice?

Anyone?

Please someone help me :frowning:

Without any more information on the services that write to those logs I'll take a wild guess and say that these still have an open handle on the files you're rotating. Usually there's a mechanism like in the second-to-last line, sending a signal (usually SIGHUP or SIGUSR1) to the process, telling it to re-read the configuration and/or recreating the log files.

If there's no such thing, you'll probably have to restart the daemon.

Thankyou for that information. Here is a list of log files that i added

How can i find out which process has a handle open to that log file? If i can find out which deamon uses which log I can at least research into what the mechanism might be for each one?

These look like something generated by cron or similar:

  • daily_out.log
  • monthly.out
  • weekly.out

This is used by the login management system and should never be rotated or otherwise modified manually:

  • lastlog

Probably used by the UI and/or for support issues:

  • windowserver.log
  • SerialNumberSupport.log
  • crashreporter.log

Probably generated by a script:

  • rsync_backup.log

No clue:

  • asl.log
  • servermgrd.log
  • ppp.log
  • secure.log

Just look through the config files for the daemons you're using to check what files they're writing to. And the mechanism needed is usually noted in the man page (or other shipped documentation). If there's no explicit information about signal involved, better play it save and just restart the daemon.

look into 'man fuser' - it will give you the PIDs of the processes using the files.

Thankyou both very much :slight_smile:

Hi i am looking into fuser. It seems to be missing from my system somehow. Though this page suggests that it comes with MAC OS X
Mac OS X Manual Page For fuser(1)

But this is what i get in the console:

WebServer:/ timgolding$ man fuser
No manual entry for fuser
WebServer:/ timgolding$ fuser
-bash: fuser: command not found
WebServer:/ timgolding$ locate fuser
WebServer:/ timgolding$ locate ldconfig

see if you have 'man lsof'

Ah yes i do thanks I'll look into that.

Hi is this good enough?

WebServer:/ timgolding$ sudo lsof /var/log/asl.log
Password:
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
syslogd  39 root    4w  VREG   14,4   189853 11006823 /private/var/log/asl.log
WebServer:/ timgolding$  

looks good enough to me - you have a PID and a command locking the file (syslogd).
Now you need to parse this for whatever you need to do.

Not too sure what that is at the moment? Learn how to restart it with a script? Or as Pludi says:

Hi since the commands below. Outputs as follows

WebServer:/ timgolding$ cd /var/run
WebServer:/var/run timgolding$ sudo grep "39" *
grep: asl_input: Operation not supported on socket
grep: cupsd: Operation not supported on socket
grep: mDNSResponder: Operation not supported on socket
grep: portmap.socket: Operation not supported on socket
grep: pppconfd: Operation not supported on socket
grep: syslog: Operation not supported on socket
syslog.pid:39

I assume my original script

if [ -f /var/run/syslog.pid ]; then kill -HUP $(cat /var/run/syslog.pid | head -1); fi

is already doing what's required? If so onto the next log file :slight_smile:

Correct. /private/var/log/asl.log is written by syslogd (the system logging daemon), and the above line already tells it to reload. Also, most open source daemons I know react like this to SIGHUP, just check the man page for any mentioning of "GNU" or alike.

If I remember correctly you can pass more than one filename to lsof, so you can quickly eliminate all logs managed by syslog.

Ah OK well thanks a lot for all your help. You've been very helpful :slight_smile: I think i can use this information for the rest tomorrow. bye bye

Well that took some time. Here is my final script i have come up with. Hope it's ok

echo ""
# Log rotations modded to hell by Tim Golding
printf %s "Rotating log files:"
cd /var/log
# Add new logs to this list
for i in asl.log servermgrd.log crashreporter.log ftp.log lookupd.log lpr.log mail.log netinfo.log hwmond.log ipfw.log secure.log; do
    if [ -f "${i}" ]; then
    if [ "crashreporter.log" == "${i}" ] && !(lsof "${i}") || [ "crashrepoter.log" != "${i}" ]; then
        file_size=`ls -l "${i}"| awk '{printf "%s",$5}'`
        max=2097152
        if [ $file_size -gt $max ]; then
                ch_mod=`ls -l "${i}"| awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf("%0o ",k);print}'| awk '{printf "%s",$1}'`
                this_group=`ls -l "${i}"| awk '{printf "%s",$3}'`
                this_user=`ls -l "${i}"| awk '{printf "%s",$4}'`
                printf %s " $i"
                if [ -x /usr/bin/gzip ]; then gzext=".gz"; else gzext=""; fi
                if [ -f "${i}.3${gzext}" ]; then mv -f "${i}.3${gzext}" "${i}.4${gzext}"; fi
                if [ -f "${i}.2${gzext}" ]; then mv -f "${i}.2${gzext}" "${i}.3${gzext}"; fi
                if [ -f "${i}.1${gzext}" ]; then mv -f "${i}.1${gzext}" "${i}.2${gzext}"; fi
                if [ -f "${i}.0${gzext}" ]; then mv -f "${i}.0${gzext}" "${i}.1${gzext}"; fi
                if [ -f "${i}" ]; then mv -f "${i}" "${i}.0" && if [ -x /usr/bin/gzip ]; then gzip -9 "${i}.0"; fi; fi
                touch "${i}" && chmod "${ch_mod}" "${i}" && chown "${this_group}":"${this_user}" "${i}"
        fi
    fi
 fi
done
if [ -f /var/run/servermgrd.pid ]; then kill -HUP $(cat /var/run/servermgrd.pid | head -1); fi
if [ -f /var/run/syslog.pid ]; then kill -HUP $(cat /var/run/syslog.pid | head -1); fi
echo ""

I took some of the logs out of the loop and will monitor those manually for the time being. I kept some in and crashreporter i kept but i made a rule to only rotate it if its not being used. My logic being if there's a crash being reported i don't really want to be stopping the process. I took weekly.out, monthly.out and daily.out because i figured that this script that is called weekly, when it is running maybe will be the time weekly.out is being written to plus in a config file somewhere that had weekly.out set a log file path it had a config line saying that log clean is enabled. So I'm hoping that they are already being taken care of.
The other ones i left in are being handled by servermgrd or syslog which are safe and easy to restart. My only concerns now are still what weekly_out.log was and the rysnc_backup.log that is 643kb so ideally wouldn't mind rotating but couldn't find much info about. Well i'll just see how this goes for now.

Thanks alot for all your help. :slight_smile:

hi,

There is syntax error in the line

ch_mod=`ls -l "${i}"| awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k)printf("%0o ",k);print}'| awk '{printf "%s",$1}'`

which affects the write permission to the file.

I'm not getting a syntax error it returns the permissions for a file. I just tested it and it returned 644 for a file that has permissions 644. could you tell me what that error is?