Author Archives: goran

RSS scraping with Python and passing new items to Slack using Webhooks

So my use case is that I wanted new Red Hat Errata published at https://rhn.redhat.com/rpc/recent-errata.pxt to be sent to one of our Slack channels. However the above url contains all errata and I´m only interested in critical errata to keep down the noise in the slack channel.

* First step is to create a slack webhook, visit: https://my.slack.com/services/new/incoming-webhook/ . Add the new webhook to the webhook variable in the script below. Also change channel name, etc in the payload variable.
* Next you need save the script to your linux box, lets create directory /usr/local/bin/errata-to-slack .
* I´m also using a library called feedparser to parse the RSS: https://github.com/kurtmckee/feedparser , download it and place it in the same directory as your script.
* Next you need to touch the datafile /var/lib/rhn-errata.txt which will hold a list of errata already sent to slack. If you will run the script as another user than root, you will need to chown the datafile to the correct user.

#!/usr/bin/env python                                                                                                                                               
 
datafile='/var/lib/rhn-errata.txt'                                                                                                                                  
import feedparser                                                                                                                                                   
import json                                                                                                                                                         
import os                                                                                                                                                           
import sys                                                                                                                                                          
import requests                                                                                                                                                     
 
d = feedparser.parse('https://rhn.redhat.com/rpc/recent-errata.pxt')                                                                                                
webhook = 'https://hooks.slack.com/services/XYXYXYXYXYX/XYXYXYXYXY/xyxyxyxyxyxyxy'                                                                           
 
for post in d.entries:                                                                                                                                              
  if post.title.find('Critical:') > 0:                                                                                                                              
    f = open(datafile, 'r+')                                                                                                                                        
    if post.title not in f.read():                                                                                                                                  
      payload = {                                                                                                                                                   
        'text': post.title + ": " + post.link + "\n"                                                                                                                
      }                                                                                                                                                             
      payload['channel'] = "#alerts"                                                                                                                                
      payload['username'] = "alerts"                                                                                                                                
      payload['icon_emoji'] = "ghost"                                                                                                                               
      try:                                                                                                                                                          
        res = requests.post(webhook, data=json.dumps(payload))                                                                                                      
      except Exception as e:                                                                                                                                        
        sys.stderr.write('An error occurred when trying to deliver the message:\n  {0}'.format(e.message))                                                          
      if res.ok:                                                                                                                                                    
        f.write(post.title+"\n")                                                                                                                                    
    f.close

Quick setup of Lets Encrypt on Apache with virtual hosts

This is a quick guide of how I setup letsencrypt on a Apache server with 3 SSL Virtual Hosts.

References:
* https://community.letsencrypt.org/t/quick-start-guide/1631
* https://community.letsencrypt.org/t/apache-configuration-example/2338
* https://community.letsencrypt.org/t/how-to-automatically-renew-certificates/4393

Setup:
* CentOS7/RHEL7
* Apache with SNI support and virtualhosts already configured.
* Virtualhost web dir is /var/www/vhosts and conf dir is /etc/httpd/conf.d . Assuming one conf file per vhost.

* UPDATE: Old way has been replaced with certbot: https://www.eff.org/sv/deeplinks/2016/05/announcing-certbot-new-tls-robot , instruction below has been updated.
* Im using RHEL7 so enabling EPEL repo: https://fedoraproject.org/wiki/EPEL
* Since its RHEL7, I also need to enable Optional repo with: subscription-manager repos –enable=rhel-7-server-optional-rpms

yum install certbot
 
certbot certonly -a webroot -w /var/www/vhosts/site1.domain.com/ -d site1.domain.com -w /var/www/vhosts/site2.domain.com/ -d site2.domain.com -w /var/www/vhosts/site3.domain.com/ -d site3.domain.com

Update all apache configuration files for your vhosts in /etc/httpd/conf.d , comment out existing certificate files and add the new certificate:

SSLCertificateFile      /etc/letsencrypt/live/..../cert.pem
SSLCertificateKeyFile   /etc/letsencrypt/live/..../privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/..../fullchain.pem

Restart apache: systemctl restart httpd

Verify that the new certificates are working fine.

The certificates are only valid for a few months so you need to renew them or else they will expire.
I use the below script /usr/local/bin/letsencrypt-renew that I run as a cronjob:

#!/bin/bash
/usr/bin/certbot certonly --config /etc/letsencrypt/cli.ini -w /var/www/vhosts/site1.domain.com/ -d site1.domain.com -w /var/www/vhosts/site2.domain.com/ -d site2.domain.com -w /var/www/vhosts/site3.domain.com/ -d site3.domain.com
if [ "$?" -eq "0" ]; then
  /bin/systemctl restart httpd
fi

The config file /etc/letsencrypt/cli.ini contains:

authenticator = webroot
renew-by-default

And the cronjob /etc/cron.d/letsencrypt-renew runs at 08:00 the first day of the month every 2 months.

00 08 01 */2 * root /usr/local/bin/letsencrypt-renew >/var/log/letsencrypt-renew.log 2>&1

You can also add monitoring of certificate expiry using the check_http nagios plugin if you like:

check_http -H siteX.domain.com -S -C 30 --sni

This will trigger an alert if the certificate expires in less than 30 days which it never should if the letsencrypt-renew cronjob is running correctly.

Publishing internal HTTPS websites using pfSense Apache reverse proxy

So I though I should share a tip about an issue I had with setting up Apache reverse proxy in pfSense to serve internal HTTPS websites that are using self signed certificates.
I got a message similar to The proxy server could not handle the request. Error during SSL Handshake with remote server.

I solved it by going to the Virtual Hosts tab in the Web GUI, selecting the virtual host and then adding the below directives to the Custom Options field:

# Disable cert checks
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off

Access Mediawiki RSS Recent Changes on a private wiki without authentication

The most simple solution would be to add Special:Recentchanges to the $wgWhitelistRead config variable, this does however give the whole world access to that page. Below is an alternative solution that add some additional security.

<?php
// Simple wrapper script to allow access to MediaWiki Recent changes without authentication.
// Only a SECRET is used to add some kind of additional security. Set variable $secret = "" below to disable it.
 
// REST library , may be better ones out there.
// https://github.com/nischayn22/MediaWiki_Api
// INSTALL NOTE: You need to chmod cookies.tmp file which the library uses so the webserver user can write to it.
require_once( 'mediawikiapi.php' );
// NOTE: I needed to add trim($data) in mediawikiapi.php everywhere where simplexml_load_string was used to work with mediawiki.
// Custom additions to above lib:
/*
function getFeedRecentChanges($days, $limit) {
        $url  = $this->siteUrl . "/api.php?days=$days&limit=$limit&action=feedrecentchanges&feedformat=atom";
        $data = httpRequest($url, $params = '');
        $xml  = simplexml_load_string(trim($data));
        errorHandler($xml);
        return $xml;
}
*/
 
 
// Config - PS: HTTPS is recommended
$mediawikiurl = "https://my-mediawiki/api.php";
 
// API Login credentials, create this mediawiki user.
// You could get this info from the querystring as well and skip the secret (?user=myuser&pass=mypw) but note that your webserver access logs will display all user passwords in clear text if you do so!!!
$username = "rss";
$password = 'ThisIsMySecretPassword';
 
// Primitive authentication - The secret that needs to be passed by querystring to access the page rss.php
// rss.php?secret=blahablaha
$secret = "c5f63b6039e347a5899c8b3cc5e45966";
 
if ($_GET['secret'] != $secret) {
  die("Secret is incorrect!");
}
 
if (isset($_GET['days'])) {
  $days = $_GET['days'];
} else {
  $days = "7";
}
 
if (isset($_GET['limit'])) {
  $limit = $_GET['limit'];
} else {
  $limit = "50";
}
 
$api = new MediaWikiApi($mediawikiurl);
$api->login($username, $password);
$xml = $api->getFeedRecentChanges($days, $limit);
 
header("Content-Type: application/xml");
/*
# For some additional security you can clear the summary/author fields which may contain sensitive information - uncomment these rows.
foreach ($xml->entry as $entry) {
  $entry->summary = "Summary has been stripped for security reasons ...";
  $entry->author = "";
}
*/
print $xml->asXML();
 
?>
</php>

Nginx Reverse Proxy on Asus Merlin

A reverse proxy is what you need when you want to make your home things with Web GUIs, like web cameras, media servers, web servers, etc available on the internet.

I´ve got a Asus RT-N66U router with Asuswrt-Merlin firmware installed which means I can install lots of additional stuff using OPTWARE with ipkg command, like nginx.

So, here´s the step by step guide.

1. I want nginx to listen to the standard http and https ports and since the administration web interface listens on the http port, I need to move the admin web interface to a different port.
In the Web-GUI: Administration / System – Change Authentication Method to https and enter port 8443, Save.

2. SSH to your asus router and login with the admin account.

3. Install nginx package

ipkg install nginx

4. Add startup scripts for allowing http/https in firewall and for starting nginx.

admin@RT-N66U-6370:/# vi /jffs/scripts/firewall-start

#!/bin/sh
iptables -I INPUT -p tcp --dport 80 -j ACCEPT
iptables -I INPUT -p tcp --dport 443 -j ACCEPT

admin@RT-N66U-6370:/# vi /jffs/scripts/services-start

#!/bin/sh
/opt/etc/init.d/S80nginx start

5. Chmod the scripts making them executable.

chmod 755 /jffs/scripts/*

6. Create self-signed SSL certificate with openssl (google it if unsure, lots of howtos out there). Copy to /opt/etc/nginx/ directory with name cert.pem and cert.key . (If you don´t need https skip this part and comment out the servers listening on 443 in nginx.conf below)

7. Edit nginx config file, below is an example config file.

admin@RT-N66U-6370:/# vi /opt/etc/nginx/nginx.conf

user nobody nobody ;
worker_processes  1;
 
events {
    worker_connections  1024;
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    server_names_hash_bucket_size  64;
 
    sendfile        on;
    #tcp_nopush     on;
 
    #keepalive_timeout  0;
    keepalive_timeout  65;
 
    #gzip  on;
 
    #HTTP default website
    server {
        listen 80;
        server_name  localhost;
 
        location / {
            root   html;
            index  index.html index.htm;
        }
 
        error_page  404              /404.html;
 
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
 
    #HTTP reverse proxies
    server {
        listen 80;
        server_name  www.mydomain.com;
        location / {
            # A backend apache server
            proxy_pass http://192.168.1.4;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
 
    server {
        listen 80;
        server_name  myplex.mydomain.com;
        if ($request_uri !~ "^/web/.*")
        {
          return 301 http://$host/web/index.html;
        }
        location / {
            # Plex Web
            proxy_pass http://192.168.1.9:32400;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
 
    server {
        listen 80;
        server_name  webcam.mydomain.com;
        location / {
            # My webcam
            proxy_pass http://192.168.1.30;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
 
    server {
        listen 80;
        server_name  torrents.mydomain.com;
        location / {
            # Transmission Web interface.
            proxy_pass http://192.168.1.8:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
 
    # HTTP to HTTPS redirects
    server {
        listen 80;
        # Force SSL for Owncloud
        server_name  owncloud.mydomain.com;
        return 301 https://$host;
    }
 
    # HTTPS default website
    server {
        listen       443;
        server_name  localhost;
        ssl                  on;
        ssl_certificate      cert.pem;
        ssl_certificate_key  cert.key;
        ssl_session_timeout  5m;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers   on;
        location / {
            root   html;
            index  index.html index.htm;
        }
    }
 
    # HTTPS reverse proxies
    server {
        listen       443;
        server_name  owncloud.mydomain.com;
        ssl                  on;
        ssl_certificate      cert.pem;
        ssl_certificate_key  cert.key;
        ssl_session_timeout  5m;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers   on;
        location / {
            # Owncloud
            proxy_pass http://192.168.1.7;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
        }
    }

8. Start nginx

/opt/etc/init.d/S80nginx start

9. If you want to show the correct Remote IP adress of clients connecting in logs on backend webservers, you need to change so the backends uses the X-Forwarded-For HTTP header instead, otherwise it will look like your asus router is the client.

Apache example:
Add a row in httpd.conf:
LogFormat “\”%{X-Forwarded-For}i\” %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”” combined_revproxy
And in your vhost:
CustomLog /path/to/your/logfile combined_revproxy

OR: You can install apache module mod_extract_forwarded which will change the REMOTE_ADDR to display the correct Remote IP adress.
RHEL/CentOS: Make sure you have EPEL yum repository, yum install mod_extract_forwarded , edit /etc/httpd/conf.d/mod_extract_forwarded.conf , add MEFaccept all.

PS: Don´t forget to backup your jffs partition before upgrading firmware – in my experience it´s often wiped when you upgrade.
A backup is most easily done by SSH to your router and running tar cvf /tmp/mnt/sda1/backup/jffs-backup.tar /jffs

Installing Deluge on FreeNAS

Here´s my quick and dirty notes on how I installed Deluge on FreeNAS 9.2.
In my case I did a dual torrent client setup in the same jail as transmission but you might as well install a new jail if you like.

freenas> jls
JID  IP Address      Hostname                      Path
  1  -               transmission_1                /mnt/vol1/jails/transmission_1
freenas> jexec 1 csh
transmission_1> pkg_add -r deluge
# deluge and dependencies will now compile
transmission_1> pw usermod transmission -s /bin/csh
transmission_1> vi /usr/pbi/transmission-amd64/control.py
# To the top of control.py, add:
import subprocess
# And in this function:
def transmission_fcgi_start(args):
# Add (dont forget the indents):
    subprocess.call("/usr/bin/su - transmission -c /usr/local/bin/deluged", shell=True)
    subprocess.call("/usr/bin/su - transmission -c /usr/local/bin/deluge-web &", shell=True)
transmission_1> exit
freenas> warden stop transmission_1
freenas> warden start transmission_1
# Enter jail again and verify with "ps aux" that transmission and deluge is running
# Visit http://JAIL-IP:8112/ and login with password "deluge".

OP5 Monitor notifications by RSS

If you are using OP5 Monitor free version for personal/non-production/lab use you will find that notifications by e-mail or SMS is a bit overkill and annoying.
So I created this simple RSS function that consists of a notification script that is called from a check commmand that writes the notification to a text file on the op5-monitor server, and a custom PHP-script that reads the same text file and presents it in RSS format.
This way I can add https://op5monitor.mydomain.com/rss.php?channel=contactname in a RSS browser and receive alerts that way instead.

/opt/plugins/custom/notify
NOTE: In OP5 Monitor 7+ it looks like you should use: /opt/monitor/op5/notify/custom instead of /opt/plugins/custom , I had trouble with this after upgrading.

#!/bin/sh
LASTSTATECHANGE=$1
CONTACTNAME=$2
HOSTNAME=$3
SERVICEDESC=$4
# Comma is csv seperator so replace it with dot if found in output
OUTPUT=$(echo $5 | sed 's/,/./g')
STATE=$6
 
UUID=$(cat /proc/sys/kernel/random/uuid)
 
notificationfile=/opt/monitor/var/notifications-$CONTACTNAME.csv
echo "$UUID,$LASTSTATECHANGE,$HOSTNAME.$SERVICEDESC is $STATE,$OUTPUT" >>$notificationfile

Change the check command host-notify to the below (or create a new one)

$USER3$/notify/notify "$LASTHOSTSTATECHANGE$" "$CONTACTNAME$" "$HOSTNAME$" "Connectivity" "$HOSTOUTPUT$" "$HOSTSTATE$"

Change the check command service-notify to the below (or create a new one)

$USER3$/notify/notify "$LASTSERVICESTATECHANGE$" "$CONTACTNAME$" "$HOSTNAME$" "$SERVICEDESC$" "$SERVICEOUTPUT$" "$SERVICESTATE$"

Create the file /var/www/html/rss.php with the below content (note that “channel” is the same as contact)
Modify text, domain, etc as needed. Add your contacts/channels at the end.

<?php
date_default_timezone_set('UTC');
 
$channel = $_GET['channel'];
if ($channel != "") {
 
          $monitoringfile = "/opt/monitor/var/notifications-$channel.csv";
          header("Content-Type: application/xml; charset=ISO-8859-1");
          $item = $_GET['item'];
          echo "
<rss version=\"2.0\">
  <channel>
    <title>OP5 Monitor - $channel alerts</title>
    <link>http://op5monitor.mydomain.com/rss.php?channel=monitor</link>
    <description>A list of monitoring alerts</description>
";
         $file = file($monitoringfile);
         $file = array_reverse($file);
         $counter=0;
         foreach($file as $line){
            $displayItem = false;
            if (strlen($line) > 5) {
              if (strlen($item) > 0) {
                if(strpos($line, $item) !== false) {
                  $displayItem = true;
                }
              } else {
                $displayItem = true;
              }
              if ($displayItem) {
                $columns=str_getcsv($line);
                $datetime=date('D, d M Y H:i:s +0000', $columns[1]);
                if (strpos($columns[3], "|") > 0) {
                  $desc=split("\|", $columns[3]);
                  $description=$desc[0];
                } else {
                  $description=$columns[3];
                }
                echo "
    <item>
      <guid isPermaLink=\"false\">$columns[0]</guid>
      <pubDate>$datetime</pubDate>
      <title>$columns[2]</title>
      <description>$description</description>
      <link>
      http://op5monitor.mydomain.com/rss.php?channel=$channel&amp;item=$columns[0]
      </link>
      <author>OP5 Monitor</author>
    </item>
";
              }
            }
           $counter++;
           if ($counter > 100) break;
         }
         fclose($file);
          echo "
  </channel>
</rss>
";
} else {
       echo "
<HTML>
<HEAD>
<TITLE>op5monitor.mydomain.com RSS Alerts</TITLE>
</HEAD>
<BODY>
<h1>Choose channel</h1>
<ul>
  <li><a href=\"rss.php?channel=monitor\">All alerts</a></li>
  <li><a href=\"rss.php?channel=lab\">Lab alerts</a></li>
</ul>
</BODY>
</HTML>
";
}
?>

And add this if you feel its needed.
/etc/cron.daily/clear-notifications-rss.sh

#!/bin/sh
 
echo >/opt/monitor/var/notifications-lab.csv
echo >/opt/monitor/var/notifications-monitor.csv

Automated rpm resigning using GPG key with passphrase

Sometimes you want to sign multiple RPMs with your custom GPG key using a script.
But what if your GPG key has a passphrase? (which it should)
You probably don´t want to sit by the keyboard and enter the passphrase for each RPM.

This is simpler approach than using gpg-agent, a bash function that uses expect to send the passphrase to the rpm command.

function resignrpm {
 
  myrpm=$1
 
  PASSPHRASE="MySecretPassphrase"
 
  expect << EOF
spawn rpm --resign $myrpm
match_max 100000
expect "Enter pass phrase:"
send -- "$PASSPHRASE\n"
expect "*#"
EOF
 
}

Note: The function doesn´t check the exit status from the rpm command, it will always return 0 …

Monitor FreeNAS Alerts and Replications using Nagios/OP5

Created a quick and simple python script for monitoring FreeNAS alerts and replication status.
It´s based on one of the FreeNAS API examples found here:
https://github.com/freenas/freenas/blob/master/examples/api/startup.py

#!/usr/bin/env python
# Tip: To ignore capacity warnings which are set quite low, change these rows in check_alerts():
# if alert['level'] != 'OK':
#     if alert['message'].find('capacity for the volume') == -1:
#         errors = errors + 1
 
import argparse
import json
import sys
 
import requests
 
class Startup(object):
 
    def __init__(self, hostname, user, secret):
        self._hostname = hostname
        self._user = user
        self._secret = secret
 
        self._ep = 'http://%s/api/v1.0' % hostname
 
    def request(self, resource, method='GET', data=None):
        if data is None:
            data = ''
        try:
            r = requests.request(
                method,
                '%s/%s/' % (self._ep, resource),
                data=json.dumps(data),
                headers={'Content-Type': "application/json"},
                auth=(self._user, self._secret),
            )
        except:
            print 'UNKNOWN - Error when contacting freenas server: ' + str(sys.exc_info())
            sys.exit(3)
 
        if r.ok:
            try:
                return r.json()
            except:
                print 'UNKNOWN - Error when contacting freenas server: ' + str(sys.exc_info())
                sys.exit(3)
 
    def check_repl(self):
        repls = self.request('storage/replication')
        errors=0
        try:
            for repl in repls:
                if repl['repl_status'] != 'Succeeded':
                    errors = errors + 1
        except:
            print 'UNKNOWN - Error when contacting freenas server: ' + str(sys.exc_info())
            sys.exit(3)
 
        if errors > 0:
            print 'WARNING - There are ' + str(errors) + ' replication errors. Go to Storage > Replication Tasks > View Replication Tasks in FreeNAS for more details.'
            sys.exit(1)
        else:
            print 'OK - No replication errors'
            sys.exit(0)
 
    def check_alerts(self):
        alerts = self.request('system/alert')
        errors=0
        try:
            for alert in alerts:
                if alert['level'] != 'OK':
                    errors = errors + 1
        except:
            print 'UNKNOWN - Error when contacting freenas server: ' + str(sys.exc_info())
            sys.exit(3)
 
        if errors > 0:
            print 'WARNING - There are ' + str(errors) + ' alerts. Click Alert button in FreeNAS for more details.'
            sys.exit(1)
        else:
            print 'OK - No problem alerts'
            sys.exit(0)
 
def main():
    parser = argparse.ArgumentParser(description='Checks a freenas server using the API')
    parser.add_argument('-H', '--hostname', required=True, type=str, help='Hostname or IP address')
    parser.add_argument('-u', '--user', required=True, type=str, help='Normally only root works')
    parser.add_argument('-p', '--passwd', required=True, type=str, help='Password')
    parser.add_argument('-t', '--type', required=True, type=str, help='Type of check, either repl or alerts')
 
    args = parser.parse_args(sys.argv[1:])
 
    startup = Startup(args.hostname, args.user, args.passwd)
 
    if args.type == 'alerts':
        startup.check_alerts()
    elif args.type == 'repl':
        startup.check_repl()
    else:
        print "Unknown type: " + args.type
        sys.exit(3)
 
if __name__ == '__main__':
    main()

Note: Latest version of this script can be downloaded from:
https://github.com/gorantornqvist/nagios-plugins/blob/master/check_freenas.py

inotify – A lazy man´s git system

Sharing my inotify script that takes care of backing up puppet configuration when changes are done. Works well if you don´t know how or have the need or diciplin to use git. Can of course be used for something other than puppet configs 😉

Search for other versions of a file with something like:

find /etc/puppet-backups -wholename "*/etc/puppet/modules/banner/templates/motd.erb"

With the found matches you can also do a “diff” and compare the different versions.

Also suggest creating the directory /etc/puppet-backups/thisyear/thismonth/today/initial/etc/puppet and copying your modules and environments directory there, this way you will have a base backup and a base to use when diffing.

#!/bin/bash
 
# inotifywait: Start/Stop inotifywait
#
# chkconfig: - 80 20
# description: inotifywait waits for changes to files using inotify.
#
# processname: inotifywait
 
. /etc/rc.d/init.d/functions
. /etc/sysconfig/network
 
LOGFILE=/var/log/inotify.log
LOCK=/var/lock/subsys/inotifywait
 
RETVAL=0
start() {
   echo -n $"Starting inotifywait: "
 
   (while true; do
    # For some reason, VIM creates a file called 4913 which messes things up for inotify
    dirfile=$(inotifywait -r -e create,modify --exclude '(.*\.sw[pox].*|4913)' --format '%w %f' /etc/puppet/modules /etc/puppet/environments)
    dir=$(echo $dirfile | awk '{ print $1 }')
    file=$(echo $dirfile | awk '{ print $2 }')
    year=$(date +%Y)
    month=$(date +%m)
    day=$(date +%d)
    time=$(date +%H%M%S)
    backupbase=/etc/puppet-backups/$year/$month/$day/$time
    mkdir -p $backupbase
    backupdir=${backupbase}${dir}
    mkdir -p $backupdir
    echo "${year}-${month}-${day} ${time} - Backing up new file version of file ${dir}${file} to directory $backupdir" &gt;&gt;$LOGFILE
    cp ${dir}${file} ${backupdir}/ 1&gt;&gt;$LOGFILE 2&gt;&amp;1
   done) &amp;
 
   RETVAL=$?
   echo
   [ $RETVAL -eq 0 ] &amp;&amp; touch $LOCK
   return $RETVAL
}
 
stop() {
   echo -n $"Stopping inotifywait: "
   killproc inotifywait
   RETVAL=$?
   echo
   [ $RETVAL -eq 0 ] &amp;&amp; rm -f $LOCK
   return $RETVAL
}
 
case "$1" in
   start)
      start
      ;;
   stop)
      stop
      ;;
   status)
      status inotifywait
      ;;
   restart)
      stop
      start
      ;;
   *)
      echo $"Usage: $0 {start|stop|status|restart}"
      exit 1
esac
exit $?