diff --git a/src/lyskom/Makefile b/src/lyskom/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..0255c75f341765d780bd03cd8e81b9c5d80bc71c
--- /dev/null
+++ b/src/lyskom/Makefile
@@ -0,0 +1,26 @@
+# Makefile for lyskom Nagios utilities
+
+COBJS=lyskom.o buffer.o
+
+CFLAGS=-g -O
+
+boo:
+	@echo "Enter 'make linux' or 'make solaris'"
+
+linux:
+	$(MAKE) CC="$(CC)" CFLAGS="$(CFLAGS)" all
+
+solaris:
+	$(MAKE) LIBS="-lnsl -lsocket" CC="$(CC)" CFLAGS="$(CFLAGS)" all
+
+
+all: 	notify_lyskom check_lyskom
+
+notify_lyskom:	notify_lyskom.o $(COBJS)
+	$(CC) -o notify_lyskom notify_lyskom.o $(COBJS) $(LIBS)
+
+check_lyskom:	check_lyskom.o $(COBJS)
+	$(CC) -o check_lyskom check_lyskom.o $(COBJS) $(LIBS) -lm
+
+distclean clean:
+	-rm -f core notify_lyskom check_lyskom *.o *~ \#*
diff --git a/src/lyskom/README b/src/lyskom/README
new file mode 100644
index 0000000000000000000000000000000000000000..8157481e37bfed5c2812cd1cc9e5655fe8a1a434
--- /dev/null
+++ b/src/lyskom/README
@@ -0,0 +1,43 @@
+Author: Peter Eriksson <pen@lysator.liu.se>
+
+This directory contains two tools -
+
+notify_lyskom	
+	Can be used to send notification (alerts or write messages) to LysKOM
+
+check_lyskom
+	Test that the LysKOM server is running smoothly (checks time, login capability,
+	number of connected clients, and number of clients in the run queue)
+
+
+=== BUILD INSTRUCTIONS ===
+
+Possibly modify the Makefile, then "make solaris" or "make linux"
+
+
+=== INSTALL INSTRUCTIONS ===
+
+
+NAGIOS misccommands.cfg sample below.
+
+Replace LYSKOMSERVERIP, LYSKOMUSERID and SECRETPASSWORD.
+
+Note:
+LYSKOMSERVERIP must be an *IP* address, not a hostname.
+LYSKOMUSERID must be a numerical user id - not a text string.
+
+
+define command {
+        command_name    notify-by-lyskom
+
+        command_line    /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\n\nService: $SERVICEDESC$\nHost: $HOSTNAME$ ($HOSTALIAS$)\nAddress: $HOSTADDRESS$\nState: $SERVICESTATE$\n\nDate/Time: $LONGDATETIME$\n\nAdditional Info:\n\n$SERVICEOUTPUT$" | /ifm/bin/lyskom_notify -HLYSKOMSERVERIP -uLYSKOMUSERID -pSECRETPASSWORD -s"** $NOTIFICATIONTYPE$ alert - $SERVICEDESC$@$HOSTNAME$ is $SERVICESTATE$ **" $CONTACTEMAIL$
+
+}
+
+
+define command {
+        command_name    host-notify-by-lyskom
+
+        command_line    /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nState: $HOSTSTATE$\nHost: $HOSTNAME$ ($HOSTALIAS$)\nAddress: $HOSTADDRESS$\nInfo: $SERVICEOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /ifm/bin/lyskom_notify -HLYSKOMSERVERIP -uLYSKOMUSERID -pSECRETPASSWORD -s"Host $HOSTSTATE$ : $HOSTNAME$ ($HOSTALIAS$)" $CONTACTEMAIL$
+}
+
diff --git a/src/lyskom/buffer.c b/src/lyskom/buffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..457c3319ba072573f75fc258709b708e0ef0287a
--- /dev/null
+++ b/src/lyskom/buffer.c
@@ -0,0 +1,155 @@
+/*
+** buffer.c
+**
+** Simple character buffer subroutines
+**
+** Copyright (c) 2006 Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "buffer.h"
+
+
+void
+buf_init(BUFFER *bp)
+{
+    bp->buf = NULL;
+    bp->size = 0;
+    bp->len = 0;
+}
+
+
+
+int
+buf_putc(BUFFER *bp,
+	 char c)
+{
+    if (bp->len >= bp->size)
+    {
+	if (!bp->buf)
+	    bp->buf = malloc((bp->size = 256)+1);
+	else
+	    bp->buf = realloc(bp->buf, (bp->size += 256)+1);
+	if (!bp->buf)
+	    return -1;
+	
+	memset(bp->buf+bp->len, 0, bp->size+1-bp->len);
+    }
+
+    bp->buf[bp->len++] = c;
+    return bp->len;
+}
+
+
+
+int
+buf_puts(BUFFER *bp,
+	 const char *s)
+{
+    int rc = 0;
+    
+    while (rc >= 0 && *s)
+	rc = buf_putc(bp, *s++);
+
+    return rc;
+}
+
+
+
+void
+buf_clear(BUFFER *bp)
+{
+    free(bp->buf);
+    bp->size = 0;
+    bp->len = 0;
+}
+
+
+char *
+buf_getall(BUFFER *bp)
+{
+    return bp->buf ? bp->buf : "";
+}
+
+
+int
+buf_save(BUFFER *bp,
+	 FILE *fp)
+{
+    return fwrite(bp->buf, 1, bp->len, fp);
+}
+
+
+int
+buf_load(BUFFER *bp,
+	 FILE *fp)
+{
+    int rc, nsize;
+    struct stat sb;
+
+
+    if (fstat(fileno(fp), &sb) < 0 || !S_ISREG(sb.st_mode))
+    {
+	while ((rc = getc(fp)) != EOF)
+	    buf_putc(bp, rc);
+
+	return bp->len;
+    }
+
+    nsize = sb.st_size + bp->len;
+    if (nsize >= bp->size)
+    {
+	char *nbuf = realloc(bp->buf, nsize+1);
+	
+	if (!nbuf)
+	    return -1;
+
+	bp->buf = nbuf;
+	bp->size = nsize;
+	
+	memset(bp->buf+bp->len, 0, bp->size+1-bp->len);
+    }
+    
+
+    rc = fread(bp->buf+bp->len, 1, sb.st_size, fp);
+    if (rc > 0)
+	bp->len += rc;
+    
+    if (rc < 0)
+	return -1;
+
+    return bp->len;
+}
+
+
+int
+buf_length(BUFFER *bp)
+{
+    return bp->len;
+}
+
+
+int
+buf_strip(BUFFER *bp)
+{
+    int i;
+    
+    for (i = 0; i < bp->len && isspace(bp->buf[i]); ++i)
+	;
+    if (i < bp->len)
+	memcpy(bp->buf, bp->buf+i, bp->len-i);
+
+    bp->len -= i;
+
+    for (i = bp->len; i > 0 && isspace(bp->buf[i-1]); --i)
+	;
+    bp->len = i;
+
+    return bp->len;
+}
diff --git a/src/lyskom/buffer.h b/src/lyskom/buffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..1f1096b1274195f716a9ee8590ca507811cfeb43
--- /dev/null
+++ b/src/lyskom/buffer.h
@@ -0,0 +1,51 @@
+/*
+** buffer.h
+**
+** Simple character buffer subroutines
+**
+** Copyright (c) 2006 Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#ifndef BUFFER_H
+#define BUFFER_H 1
+
+typedef struct buffer
+{
+    char *buf;
+    int len;
+    int size;
+} BUFFER;
+
+
+extern void
+buf_init(BUFFER *bp);
+
+extern int
+buf_putc(BUFFER *bp,
+	 char c);
+
+extern int
+buf_puts(BUFFER *bp,
+	 const char *s);
+
+extern void
+buf_clear(BUFFER *bp);
+
+extern char *
+buf_getall(BUFFER *bp);
+
+extern int
+buf_save(BUFFER *bp,
+	 FILE *fp);
+
+extern int
+buf_load(BUFFER *bp,
+	 FILE *fp);
+
+extern int
+buf_length(BUFFER *bp);
+
+extern int
+buf_strip(BUFFER *bp);
+
+#endif
diff --git a/src/lyskom/check_lyskom.c b/src/lyskom/check_lyskom.c
new file mode 100644
index 0000000000000000000000000000000000000000..39b096389f76031d3539ff13ae72437dd7b864aa
--- /dev/null
+++ b/src/lyskom/check_lyskom.c
@@ -0,0 +1,200 @@
+/*
+** check_lyskom.c
+**
+** A simple Nagios plugin to verify LysKOM server status
+**
+** Copyright (c) 2006 Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <math.h>
+
+#include "lyskom.h"
+
+
+char *host = "130.236.254.15";
+int   port = 4894;
+
+char *ident = NULL;
+int   user = 0;
+char *pass = NULL;
+
+char *argv0 = "check_lyskom";
+
+int clients_crit = 10000;
+int clients_warn = 1000;
+int rqclients_crit = 10;
+int rqclients_warn = 2;
+
+void
+error(const char *msg, ...)
+{
+    va_list ap;
+
+    va_start(ap, msg);
+    printf("CRITICAL - ");
+    vprintf(msg, ap);
+    putchar('\n');
+    va_end(ap);
+    exit(2);
+}
+
+
+
+
+int
+main(int argc,
+     char *argv[])
+{
+    int i, rc, clients_c, clients_rq;
+    struct tm tmb;
+    time_t lyskom_t, our_t;
+    double d;
+#if 0
+    int proto;
+    char *ksoft, *kver;
+#endif
+    
+
+    argv0 = strdup(argv[0]);
+    
+    for (i = 1; i < argc && argv[i][0] == '-'; i++)
+	switch (argv[i][1])
+	{
+	  case '-':
+	    ++i;
+	    goto End;
+
+	  case 'H':
+	    host = strdup(argv[i]+2);
+	    break;
+
+	  case 'P':
+	    if (sscanf(argv[i]+2, "%u", &port) != 1)
+		error("Invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'I':
+	    ident = strdup(argv[i]+2);
+	    break;
+
+	  case 'u':
+	    if (sscanf(argv[i]+2, "%u", &user) != 1)
+		error("Invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'p':
+	    pass = strdup(argv[i]+2);
+	    break;
+
+	  case 'c':
+	    if (sscanf(argv[i]+2, "%u", &clients_crit) != 1)
+		error("Invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'w':
+	    if (sscanf(argv[i]+2, "%u", &clients_warn) != 1)
+		error("Invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'C':
+	    if (sscanf(argv[i]+2, "%u", &rqclients_crit) != 1)
+		error("Invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'W':
+	    if (sscanf(argv[i]+2, "%u", &rqclients_warn) != 1)
+		error("Invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'h':
+	    printf("Usage: %s [options]\n", argv[0]);
+	    puts("Options:");
+	    puts("\t-H<lyskom-server-addr>");
+	    puts("\t-P<lyskom-server-port>");
+	    puts("\t-u<lyskom-userid>");
+	    puts("\t-p<password>");
+	    puts("\t-c<clients-connected-critical-level>");
+	    puts("\t-w<clients-connected-warning-level>");
+	    puts("\t-C<clients-runqueue-critical-level>");
+	    puts("\t-W<clients-runqueue-warning-level>");
+	    return 0;
+	    
+	  default:
+	    error("Invalid switch: %s", argv[i]);
+	}
+
+  End:
+    if (kom_connect(host, port, ident ? ident : "unknown") != 0)
+	error("Connect failed: Host=%s, Port=%u", host, port);
+    
+    if (user > 0)
+    {
+	if (kom_login(user, pass ? pass : "") != 0)
+	    error("Login failed: User=%u", user);
+    }
+    
+    rc = kom_time(&tmb);
+    if (rc != 0)
+	error("Request failed: Server Time: rc=%u", rc);
+
+    lyskom_t = mktime(&tmb);
+    time(&our_t);
+
+    d = fabs(difftime(lyskom_t, our_t));
+
+    if (d > 60.0)
+    {
+	printf("CRITICAL - Server time is %d minute(s) off\n", (int) (d/60.0));
+	return 1;
+    }
+    
+    if (d > 1.0)
+    {
+	printf("WARNING - Server time is %d second(s) off\n", (int) d);
+	return 1;
+    }
+
+#if 0
+    if (kom_version(&proto, &ksoft, &kver) != 0)
+	error("Request failed: Server version");
+#endif
+    if (kom_clients(&clients_c) != 0)
+	error("Request failed: Connected clients");
+
+    if (clients_c > clients_crit)
+    {
+	printf("CRITICAL - Connected clients: %u\n", clients_c);
+	return 2;
+    }
+    
+    if (clients_c > clients_warn)
+    {
+	printf("WARNING - Connected clients: %u\n", clients_c);
+	return 1;
+    }
+    
+    if (kom_runqueue(&clients_rq) != 0)
+	error("Request failed: Clients in run-queue");
+
+    if (clients_rq > rqclients_crit)
+    {
+	printf("CRITICAL - Clients in run-queue: %u\n", clients_rq);
+	return 2;
+    }
+    
+    if (clients_rq > rqclients_warn)
+    {
+	printf("WARNING - Clients in run-queue: %u\n", clients_rq);
+	return 1;
+    }
+    
+    printf("OK - Clients: %u (%u in run-queue), Time: %s",
+	   clients_c, clients_rq, asctime(&tmb));
+    return 0;
+}
diff --git a/src/lyskom/lyskom.c b/src/lyskom/lyskom.c
new file mode 100644
index 0000000000000000000000000000000000000000..2a14446bea51b432c0d411c89a0b6abbfb0d98f5
--- /dev/null
+++ b/src/lyskom/lyskom.c
@@ -0,0 +1,306 @@
+/*
+** lyskom.c
+**
+** Simple LysKOM communications routines
+**
+** Copyright (c) 2006 Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "lyskom.h"
+
+
+static FILE *rfp = NULL;
+static FILE *wfp = NULL;
+static int rqid = 0;
+
+
+
+static int
+kom_get_response(FILE *fp,
+		 char *buf,
+		 size_t bufsize)
+{
+    int c, p = 0;
+
+
+    if (bufsize < 1)
+	return -1;
+
+    while ((c = getc(fp)) != EOF && isspace(c))
+	;
+    if (c == EOF)
+	return 0;
+    
+    buf[p++] = c;
+    while (p < bufsize-1 && (c = getc(fp)) != EOF && !(c == '\n' || c == '\r'))
+	buf[p++] = c;
+    buf[p] = '\0';
+    return 1;
+}
+
+
+int
+kom_connect(const char *host,
+	    int port,
+	    const char *ident)
+{
+    struct sockaddr_in sin;
+    int fd, rc;
+    char buf[1024];
+    
+
+    memset(&sin, 0, sizeof(sin));
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = inet_addr(host);
+    sin.sin_port = htons(port);
+
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+    if (fd < 0)
+	return -1;
+    
+    while ((rc = connect(fd, (struct sockaddr *) &sin, sizeof(sin))) < 0 &&
+	   errno == EINTR)
+	;
+    if (rc < 0)
+    {
+	close(fd);
+	return -2;
+    }
+
+    rfp = fdopen(fd, "r");
+    wfp = fdopen(dup(fd), "w");
+
+    fprintf(wfp, "A %uH%s\r", strlen(ident), ident);
+    fflush(wfp);
+    if (fgets(buf, sizeof(buf), rfp) == NULL)
+    {
+	fclose(rfp);
+	fclose(wfp);
+	return -3;
+    }
+    
+    if (strncmp(buf, "LysKOM", 6) != 0)
+    {
+	fclose(rfp);
+	fclose(wfp);
+	return -4;
+    }
+
+    return 0;
+}
+
+
+int
+kom_request(const char *msg, ...)
+{
+    char buf[1024];
+    va_list ap;
+    int id, rc, rs;
+    
+    
+    va_start(ap, msg);
+    fprintf(wfp, "%u ", ++rqid);
+    vfprintf(wfp, msg, ap);
+    putc('\n', wfp);
+    fflush(wfp);
+    va_end(ap);
+
+    do {
+	if (kom_get_response(rfp, buf, sizeof(buf)) < 1)
+	    return -1;
+    } while (buf[0] == ':'); /* Loop in case of Async Msg */
+    
+    if (sscanf(buf, "=%u", &id) == 1 && id == rqid)
+	return 0;
+    
+    else if (sscanf(buf, "%%%u %u %u", &id, &rc, &rs) == 3)
+	return rc;
+    
+    return -1;
+}
+
+     
+int
+kom_login(int user,
+	  const char *pass)
+{
+    return kom_request("62 %u %uH%s 1", user, strlen(pass), pass);
+}
+
+
+int
+kom_time(struct tm *tp)
+{
+    char buf[1024];
+    int id, rc, rs;
+    
+    
+    fprintf(wfp, "%u ", ++rqid);
+    fprintf(wfp, "35");
+    putc('\n', wfp);
+    fflush(wfp);
+
+    do {
+	if (kom_get_response(rfp, buf, sizeof(buf)) < 1)
+	    return -1;
+    } while (buf[0] == ':'); /* Loop in case of Async Msg */
+    
+    if (sscanf(buf, "=%u %u %u %u %u %u %u %u %u %u", &id,
+	       &tp->tm_sec, &tp->tm_min, &tp->tm_hour, &tp->tm_mday, &tp->tm_mon, &tp->tm_year,
+	       &tp->tm_wday, &tp->tm_yday, &tp->tm_isdst) == 10 && id == rqid)
+    {
+	return 0;
+    }
+    
+    else if (sscanf(buf, "%%%u %u %u", &id, &rc, &rs) == 3)
+	return rc;
+    
+    return -1;
+}
+
+
+char *
+kom_geth(const char *buf,
+	 int *ip)
+{
+    char *rp;
+    int len;
+
+
+    while (isspace(buf[*ip]))
+	++*ip;
+
+    if (sscanf(buf+*ip, "%u", &len) != 1)
+	return NULL;
+
+    while (isdigit(buf[*ip]))
+	++*ip;
+
+    if (buf[*ip] != 'H')
+	return NULL;
+
+    ++*ip;
+    
+    rp = malloc(len+1);
+    if (!rp)
+	return NULL;
+
+    memcpy(rp, buf+*ip, len);
+    *ip += len;
+    rp[len] = '\0';
+    
+    return rp;
+}
+
+
+int
+kom_version(int *proto,
+	    char **stype,
+	    char **sver)
+{
+    char buf[1024];
+    int id, rc, rs, i;
+    
+    
+    fprintf(wfp, "%u ", ++rqid);
+    fprintf(wfp, "75");
+    putc('\n', wfp);
+    fflush(wfp);
+
+    do {
+	if (kom_get_response(rfp, buf, sizeof(buf)) < 1)
+	    return -1;
+    } while (buf[0] == ':'); /* Loop in case of Async Msg */
+    
+    if (sscanf(buf, "=%u %u", &id, proto) == 2 && id == rqid)
+    {
+	for (i = 1; isdigit(buf[i]); ++i)
+	    ;
+	for (;isspace(buf[i]); ++i)
+	    ;
+	for (;isdigit(buf[i]); ++i)
+	    ;
+	for (;isspace(buf[i]); ++i)
+	    ;
+	*stype = kom_geth(buf, &i);
+	*sver = kom_geth(buf, &i);
+	return 0;
+    }
+    
+    else if (sscanf(buf, "%%%u %u %u", &id, &rc, &rs) == 3)
+	return rc;
+    
+    return -1;
+}
+
+
+int
+kom_clients(int *clients)
+{
+    char buf[1024];
+    int id, rc, rs, n;
+    
+    
+    fprintf(wfp, "%u ", ++rqid);
+    fprintf(wfp, "112 7Hclients");
+    putc('\n', wfp);
+    fflush(wfp);
+
+    do {
+	if (kom_get_response(rfp, buf, sizeof(buf)) < 1)
+	    return -1;
+    } while (buf[0] == ':'); /* Loop in case of Async Msg */
+    
+    if (sscanf(buf, "=%u %u { %u", &id, &n, clients) == 3 && id == rqid)
+	return 0;
+    
+    else if (sscanf(buf, "%%%u %u %u", &id, &rc, &rs) == 3)
+	return rc;
+    
+    return -1;
+}
+
+int
+kom_runqueue(int *clients)
+{
+    char buf[1024];
+    int id, rc, rs, n;
+    
+    
+    fprintf(wfp, "%u ", ++rqid);
+    fprintf(wfp, "112 16Hrun-queue-length");
+    putc('\n', wfp);
+    fflush(wfp);
+
+    do {
+	if (kom_get_response(rfp, buf, sizeof(buf)) < 1)
+	    return -1;
+    } while (buf[0] == ':'); /* Loop in case of Async Msg */
+    
+    if (sscanf(buf, "=%u %u { %u", &id, &n, clients) == 3 && id == rqid)
+	return 0;
+    
+    else if (sscanf(buf, "%%%u %u %u", &id, &rc, &rs) == 3)
+	return rc;
+    
+    return -1;
+}
+
+     
+
+
+
diff --git a/src/lyskom/lyskom.h b/src/lyskom/lyskom.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb85ae15d31737856b951320d47f4610c56dccfe
--- /dev/null
+++ b/src/lyskom/lyskom.h
@@ -0,0 +1,32 @@
+/*
+** lyskom.h
+**
+** Simple LysKOM communications routines
+**
+** Copyright (c) 2006 Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#ifndef LYSKOM_H
+
+#include <time.h>
+
+extern int kom_connect(const char *host,
+		       int port,
+		       const char *ident);
+
+extern int kom_login(int user,
+		     const char *pass);
+
+extern int kom_request(const char *msg, ...);
+
+extern int kom_time(struct tm *tp);
+
+extern int kom_version(int *proto,
+			   char **stype,
+			   char **sver);
+
+extern int kom_clients(int *clients);
+
+extern int kom_runqueue(int *clients);
+
+#endif
diff --git a/src/lyskom/notify_lyskom.c b/src/lyskom/notify_lyskom.c
new file mode 100644
index 0000000000000000000000000000000000000000..9526976bce71ca8f10cf168662744a11429d9ad3
--- /dev/null
+++ b/src/lyskom/notify_lyskom.c
@@ -0,0 +1,134 @@
+/*
+** notify_lyskom.c
+**
+** A simple tool to send alerts/write messages in a LysKOM database
+**
+** Copyright (c) 2006 Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "lyskom.h"
+#include "buffer.h"
+
+
+char *host = "130.236.254.15";
+int   port = 4894;
+
+char *ident = NULL;
+int   user = -1;
+char *pass = NULL;
+
+char *argv0 = "notify_lyskom";
+
+void
+error(const char *msg, ...)
+{
+    va_list ap;
+
+    va_start(ap, msg);
+    fprintf(stderr, "%s: ", argv0);
+    vfprintf(stderr, msg, ap);
+    putc('\n', stderr);
+    va_end(ap);
+    exit(1);
+}
+
+
+
+
+int
+main(int argc,
+     char *argv[])
+{
+    int async = 0, i, rc, target;
+    char *subject = NULL;
+    BUFFER buf;
+
+    
+    argv0 = strdup(argv[0]);
+    
+    for (i = 1; i < argc && argv[i][0] == '-'; i++)
+	switch (argv[i][1])
+	{
+	  case '-':
+	    ++i;
+	    goto End;
+
+	  case 'H':
+	    host = strdup(argv[i]+2);
+	    break;
+
+	  case 'P':
+	    if (sscanf(argv[i]+2, "%u", &port) != 1)
+		error("invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'I':
+	    ident = strdup(argv[i]+2);
+	    break;
+
+	  case 'u':
+	    if (sscanf(argv[i]+2, "%u", &user) != 1)
+		error("invalid argument: %s", argv[i]);
+	    break;
+
+	  case 'p':
+	    pass = strdup(argv[i]+2);
+	    break;
+
+	  case 'a':
+	    ++async;
+	    break;
+
+	  case 's':
+	    subject = strdup(argv[i]+2);
+	    break;
+
+	  default:
+	    error("invalid switch: %s", argv[i]);
+	}
+
+  End:
+    buf_init(&buf);
+    buf_load(&buf, stdin);
+    buf_strip(&buf);
+
+    if (kom_connect(host, port, ident ? ident : "unknown") != 0)
+	error("kom_connect: host=%s, port=%u", host, port);
+
+    if (user < 0 || !pass)
+	error("missing user/password");
+    
+    if (kom_login(user, pass) != 0)
+	error("kom_login: user=%u", user);
+
+    while (i < argc)
+    {
+	if (sscanf(argv[i], "%u", &target) != 1)
+	    error("invalid target: %s", argv[i]);
+
+	if (async)
+	    rc = kom_request("53 %u %uH%s", target, buf_length(&buf), buf_getall(&buf));
+	else
+	{
+	    
+	    
+	    rc = kom_request("86 %uH%s\n%s 1 { 0 %u } 0 { }",
+			strlen(subject ? subject : "")+buf_length(&buf)+1,
+			(subject ? subject : ""), buf_getall(&buf), target);
+	    
+	}
+
+	if (rc != 0)
+	    error("kom_request: rc=%u", rc);
+
+	++i;
+    }
+
+    return 0;
+}