Back to the main page

The xfer2syslog Solaris service

Some FTP servers, like ProFTP doesn't have option of logging to remote hosts (or there is, but I couldn't find it). So if you have more instances of FTP server, it's convenient to log in one central log server.
And probably the most important is transfer logging, which is the file xfer.log (you know, you can put software for customers on your FTP and later when you want to charge them, they say "we didn't download anything", so first place you check is transfer log file).
So here is the Perl script, that in real time reads xfer.log file and copy lines to local syslog file (example on Solaris). And syslog.conf is then configured to log to central server.

But first here is README that tells you:
SMF : xfer2syslog  (copy proftpd xfer.log entries to syslog file on local machine)

Required packages
        From Blastwave:
        pm_filetail (CSWpmfiletail)
        pm_unixsyslog (CSWpmunixsyslog)
------------------
The SMF service should have next entities:

1. Executable
        Example: /usr/ups/bin/xfer2syslog.pl

2. Method (usually executable shell script) to define how service start/stop
        Default location is directory /lib/svc/method/
        Example: /lib/svc/method/svc-xfer2syslog

3. Fault Management Resource Identifier (FMRI) to identify name/instance
        Example: application/xfer2syslog or xfer2syslog

4. Log file (usually created automatically on its own, in directory /var/svc/log/ )
        Example: /var/svc/log/xfer2syslog:default.log

5. Manifest  (XML file) to define service's properties
        Default location is directory /var/svc/manifest/
        Example: /var/svc/manifest/application/xfer2syslog.xml
-----------------

Roll out the service

1. Validate service's manifest (xml file)
        example:
        > svccfg validate /var/svc/manifest/application/xfer2syslog.xml

2.Import service into SMF
        example:
        > svccfg import /var/svc/manifest/application/xfer2syslog.xml

3.Enable service
        example:
        > svcadm enable application/xfer2syslog

4. Verify service
        example: list service information and associated processes
        > svcs -lp application/xfer2syslog

        example:list explanation for service's state
        > svcs -xv application/xfer2syslog

After you read README, here is the Perl script
#!/opt/csw/bin/perl -w
use strict;

use File::Tail;
use Unix::Syslog;

# define log file to be read
my $logfile="/var/log/proftpd/xfer.log" ;

# check if xfer.log exist
if ( ! -e $logfile ) {
        die "EXIT : Cannot read $logfile: $! ";
}

# maxinterval = max number of sec that script sleeps, default = 60s
my $readfile=File::Tail->new(
        name=>$logfile,
        maxinterval=>3
        );

# variables for Unix::Syslog
#my $ident="ftp_transfer";
my $ident="xfer";
my $option="1";         # LOG_PID
my $facility="0";       # no affect so far?
my $priority=6;         # info

# read ftp transfer file and log to syslog
while (my $line=$readfile->read) {
        # test : print on STDOUT
        # print "${line}";
        Unix::Syslog::openlog($ident, $option, $facility);
        Unix::Syslog::syslog($priority, $line);
        # don't close syslog file
        #closelog();
}

I created method to be like this (or you try something else)
#!/sbin/sh
#set -x
#
# Start method script for the service xfer2syslog
# -- zdudic : December  8, 2010

#. /lib/svc/share/smf_include.sh

# die  funcion
die() {
        echo "\n ERROR: $* \n"
        exit 1
}

# Check if argument (stop/start) is provided
if [ $# != 1 ]
then
        die "Usage : `/usr/bin/basename $0` [stop|start] "
fi

SCRIPT=/usr/ups/bin/xfer2syslog.pl
GREP=/usr/sfw/bin/gegrep

# ---- number of running scripts in background
PROCNUM=`ps -ef | ${GREP} xfer2syslog.pl$ | wc -l`

case "$1" in
'start')

        # --- exit if more than one ( > 1 ) script is running
        if [ ${PROCNUM} -gt 1 ]
        then
        die " \n${PROCNUM} scripts are already running, which causes multiplications of logs.
        The additional xfer2syslog processes can be killed manually. "
        fi

        # --- exit if one ( = 1 )script is running
        if [ ${PROCNUM} -eq 1 ]
        then
                die " The script is already running in background. Exit without doing anything."
        fi

        # ---- if script is not running ( = 0 ), run it once in background
        if [ ${PROCNUM} -eq 0 ]
        then
                /opt/csw/bin/perl ${SCRIPT} &
                echo "The script xfer2syslog.pl has been started in background."
                exit 0
        fi

        ;;
'stop')
        # do nothing and exit if script doesn't run
        if [ ${PROCNUM} -eq 0 ]
        then
                die "Nothing to stop. The script xfer2syslog.pl doesn't run at all."
        fi

        # if one script runs, kill it
        if [ ${PROCNUM} -eq 1 ]
        then
                PROCID=`ps -ef | ${GREP} xfer2syslog.pl$ | awk '{print $2}'`
                kill ${PROCID} || die "Script xfer2syslog.pl cannot be killed."
                echo "Script xfer2syslog.pl  - PID ${PROCID} has been killed."
        fi

        # if there are more running scripts, kill them all
        if [ ${PROCNUM} -gt 1 ]
        then
                PROCID=`ps -ef | ${GREP} xfer2syslog.pl$ | awk '{print $2}'`
                for i in ${PROCID}
                do
                kill ${i} || die "Process ${i} cannot be killed."
                echo "Killed : Process ${i}"
                done
        fi

        ;;
*)
        die "Usage : `/usr/bin/basename $0` [stop|start] "
        ;;
esac
exit 0

Here is the manifest.
<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>

<!--
        Service manifest for xfer2syslog
        by zdudic
-->
<!--
    The Service bundle possesses two attributes:
        type    How this file is to be understood by the framework.
                Standard types are: 'archive', 'manifest', and 'profile'.
        name    A name for the bundle.
-->
<service_bundle type='manifest' name='xfer2syslog'>

<service
        name='xfer2syslog'
        type='service'
        version='1'>

<create_default_instance enabled='false' />

<!-- regardless of method configuration, restarter will not
        start multiple instances simultaneously
-->
<single_instance />

<!-- service requires xfer.log file -->
<dependency
        name='proftpd_transfer_log_file'
        grouping='require_all'
        restart_on='refresh'
        type='path'>
        <service_fmri value='file://localhost/var/log/proftpd/xfer.log' />
</dependency>

<dependency
        name='proftpd'
        grouping='require_all'
        restart_on='refresh'
        type='service'>
        <service_fmri value='svc:/network/ftp/tcp' />
</dependency>

<dependency
        name='system-log'
        grouping='require_all'
        restart_on='refresh'
        type='service'>
        <service_fmri value='svc:/system/system-log' />
</dependency>

<!-- start method -->
        <exec_method
                type='method'
                name='start'
                exec='/lib/svc/method/svc-xfer2syslog start'
                timeout_seconds='10'>
                <method_context>
                        <method_credential user='root' group='root' />
                </method_context>
        </exec_method>

<!-- stop method -->
        <exec_method
                type='method'
                name='stop'
                exec='/lib/svc/method/svc-xfer2syslog stop'
                timeout_seconds='10'>
                <method_context>
                        <method_credential user='root' group='root' />
                </method_context>
        </exec_method>


</service>
</service_bundle>
Back to the main page