Adding stubs for listening - data driven

Following on from the previous post, I found myself with a few hours to myself tonight. The estrogen in the house went out as a group so I managed to do a bit more coding on the FreshPorts listening daemon.

Sometime this past week, I managed to add some triggers to the database that invoke the LISTEN command each time a port is updated. Here is the trigger I created:

CREATE OR REPLACE FUNCTION ports_clear_cache() RETURNS TRIGGER AS $$
      -- when a port changes, add an entry to the cache clearing table
   DECLARE
      l_cache_clearing_ports_id   int8;
      l_port      text;
      l_category  text;
   BEGIN
	IF TG_OP = 'UPDATE' THEN
         -- check first to see if this entry already exists
         SELECT id
           INTO l_cache_clearing_ports_id
           FROM cache_clearing_ports
          WHERE port_id = NEW.id;

          IF NOT FOUND THEN
            SELECT category, name
              INTO l_category, l_port
              FROM ports_all
             WHERE id = NEW.id;
            INSERT INTO cache_clearing_ports (port_id, category, port)
            VALUES (NEW.id, l_category, l_port);
          END IF;

          NOTIFY port_updated;
      END IF;

      RETURN NEW;
   END
$$ LANGUAGE 'plpgsql';

  DROP TRIGGER ports_clear_cache ON ports;
CREATE TRIGGER ports_clear_cache
    AFTER UPDATE on ports
    FOR EACH ROW
    EXECUTE PROCEDURE ports_clear_cache();

The cache_clearing_ports table looks like this:

create table cache_clearing_ports
(
    id          serial                not null,
    port_id     integer               not null,
    category    text                  not null,
    port        text                  not null,
    date_added  timestamp without time zone not null
        default CURRENT_TIMESTAMP,
    primary key (id)
);

alter table cache_clearing_ports
    add foreign key  (port_id)
       references ports (id) on update cascade on delete cascade;

When the FreshPorts listen daemon gets a NOTIFY, it will look at that table, remove the entries from the cache, and delete the rows it just processed.

Here is where we are with the listen daemon:

#!/usr/bin/env python
#
# This program listens for events on the database and processes them
#

#
# configuration items
#
DSN = 'dbname=freshports.org user=dan'

import sys, psycopg, select

import syslog

def RemoveCacheEntry():
  syslog.syslog(syslog.LOG_NOTICE, 'removing cache entries')

def ProcessPortsMoved():
  syslog.syslog(syslog.LOG_NOTICE, 'processing ports/MOVED')

def ProcessPortsUpdating():
  syslog.syslog(syslog.LOG_NOTICE, 'processing ports/UPDATING')

def ProcessVUXML():
  syslog.syslog(syslog.LOG_NOTICE, 'processing ports/security/portaudit/vuln.xml')

syslog.openlog('fp-listen')

syslog.syslog(syslog.LOG_NOTICE, 'Starting up')

print "Opening connection using dns:", DSN
conn = psycopg.connect(DSN)
conn.autocommit(1)

curs = conn.cursor()

curs.execute("SELECT name, script_name FROM listen_for ORDER BY id")
listens_for = curs.fetchall()

listens = dict()
for listen in listens_for:
  curs.execute("LISTEN %s" % listen[0])
  listens[listen[0]] = listen[1]
  print listen

print "Now listening..."
while 1:
  select.select([curs],[],[])==([],[],[])
  curs.execute("SELECT 1")
  notifies = curs.notifies()
  for n in notifies:
    print "got %s" % n[0]
    print "this is index %s" % listens[n[0]]
    # in real life, do something with each...
    print "got %s and I need to call %s" % (n[0], listens[n[0]])
    syslog.syslog(syslog.LOG_NOTICE, "got %s and I need to call %s" % (n[0], listens[n[0]]))
    if listens.has_key(n[0]):
      syslog.syslog(syslog.LOG_NOTICE, 'found that key')
      if listens[n[0]]   == 'listen_port':
        RemoveCacheEntry()
      elif listens[n[0]] == 'listen_ports_moved':
        ProcessPortsMoved()
      elif listens[n[0]] == 'listen_ports_updating':
        ProcessPortsUpdating()
      elif listens[n[0]] == 'listen_vuxml':
        ProcessVUXML()
      else:
        syslog.syslog(syslog.LOG_ERR, "Code does not know what to do when '%s' is found." % n[0])
    else:
      syslog.syslog(syslog.LOG_NOTICE, 'no such key!')

logging.error('terminating')

Here is some sample output from /var/log/messages as a commit is processed:

Sep  2 20:34:45 polo fp-listen: got port_updated and I need to call listen_port
Sep  2 20:34:45 polo fp-listen: found that key
Sep  2 20:34:45 polo fp-listen: removing cache entries
Sep  2 20:34:46 polo fp-listen: got port_updated and I need to call listen_port
Sep  2 20:34:46 polo fp-listen: found that key
Sep  2 20:34:46 polo fp-listen: removing cache entries

I’ll let this run in BETA for a while before I starting fleshing out the stubs at the top of the script. I’ll start by completing RemoveCacheEntry() first.

Watch this space.

Leave a Reply

Bot-Check