summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoern Engel <joern@logfs.org>2011-12-18 20:03:35 -0800
committerJoern Engel <joern@logfs.org>2011-12-18 20:03:35 -0800
commitb74052cde99a3631e54cc30dadecdbffc00a4b30 (patch)
tree59fb973d65339b55d89ed836e4a5c2b0c0a0491f
downloadcancd-b74052cde99a3631e54cc30dadecdbffc00a4b30.tar.gz
cancd 0.1.0 as found on oss.oracle.com
Signed-off-by: Joern Engel <joern@logfs.org>
-rw-r--r--Makefile45
-rw-r--r--cancd.c719
-rwxr-xr-xcancd.init206
-rw-r--r--cancd.spec55
-rw-r--r--kernel-list.h116
5 files changed, 1141 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..498c9d3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,45 @@
+
+VERSION=0.1.0
+
+CFLAGS += -Wall -g -O2 -DVERSION="\"$(VERSION)\""
+
+cancd: cancd.o
+
+install:
+ mkdir -p $(DESTDIR)/usr/sbin
+ cp cancd $(DESTDIR)/usr/sbin
+ chmod 0755 $(DESTDIR)/usr/sbin/cancd
+ mkdir -p $(DESTDIR)/etc/init.d
+ cp cancd.init $(DESTDIR)/etc/init.d/cancd
+ chmod 0755 $(DESTDIR)/etc/init.d/cancd
+
+clean:
+ rm cancd cancd.o
+
+DIST_FILES = \
+ Makefile \
+ cancd.c \
+ cancd.init \
+ cancd.spec \
+ kernel-list.h
+
+DIST_DIR = cancd-$(VERSION)
+DIST_ARCHIVE = $(DIST_DIR).tar.gz
+
+dist:
+ @rm -rf $(DIST_DIR)
+ @mkdir -p $(DIST_DIR)
+ @cp $(DIST_FILES) $(DIST_DIR)
+ tar -czvf $(DIST_ARCHIVE) $(DIST_DIR)
+ @rm -rf $(DIST_DIR)
+
+RPM_TOPDIR = $(CURDIR)
+RPMBUILD = $(shell /usr/bin/which rpmbuild 2>/dev/null || /usr/bin/which rpm 2>/dev/null || echo /bin/false)
+
+CHKCONFIG_DEP = $(shell if test -r /etc/SuSE-release; then echo aaa_base; else echo chkconfig; fi)
+
+srpm: dist
+ $(RPMBUILD) -bs --define "_sourcedir $(RPM_TOPDIR)" --define "_srcrpmdir $(RPM_TOPDIR)" --define "CANCD_VERSION $(VERSION)" --define "CHKCONFIG_DEP $(CHKCONFIG_DEP)" cancd.spec
+
+rpm: srpm
+ $(RPMBUILD) --rebuild --define "CANCD_VERSION $(VERSION)" --define "CHKCONFIG_DEP $(CHKCONFIG_DEP)" "cancd-$(VERSION)-1.src.rpm"
diff --git a/cancd.c b/cancd.c
new file mode 100644
index 0000000..5dc9b06
--- /dev/null
+++ b/cancd.c
@@ -0,0 +1,719 @@
+/*
+ * cancd - CA NetConsole Daemon
+ *
+ * Simply collect netconsole messages.
+ *
+ * Author: Joel Becker <joel.becker@oracle.com>
+ * Copyright (C) 2005 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "kernel-list.h"
+
+#ifndef VERSION
+#define VERSION "0.0.0"
+#endif
+
+#define PROGNAME "cancd"
+
+
+/*
+ * Log prefix. This is the path all logs are placed under. Modified
+ * with the '-l' option.
+ */
+static char *log_prefix = "/var/crash";
+
+/*
+ * Output file format. The format is a strftime(3) string with %Q (the
+ * only unused letter in strftime(3)) representing the IP address of
+ * the machine sending the netconsole message.
+ * The default of "%Q/%Y-%m-%d-%H:%M/log" Means that a machine
+ * 10.0.0.1 sending a message at 3:30pm on 2005/10/1 would output
+ * to 10.0.0.1/2005-10-01-15:30/log. The files are underneath
+ * the log_prefix. Modified with the '-o' option.
+ */
+static char *log_format = "%Q/%Y-%m-%d-%H:%M/log";
+
+/*
+ * Port number to listen on. Modified with the '-p' option.
+ */
+static uint16_t log_port = 6667;
+
+/* Socket we are using */
+static int sock_fd;
+
+/* Are we a daemon? */
+static int daemonize = 1;
+
+/* What signal did we catch? */
+sig_atomic_t caught_sig = 0;
+
+
+void handler(int signum)
+{
+ caught_sig = signum;
+}
+
+static int setup_signals()
+{
+ int rc = 0;
+ struct sigaction act;
+
+ act.sa_sigaction = NULL;
+ act.sa_restorer = NULL;
+ sigemptyset(&act.sa_mask);
+ act.sa_handler = handler;
+#ifdef SA_INTERRUPT
+ act.sa_flags = SA_INTERRUPT;
+#endif
+
+ rc += sigaction(SIGTERM, &act, NULL);
+ rc += sigaction(SIGINT, &act, NULL);
+ act.sa_handler = SIG_IGN;
+ rc += sigaction(SIGPIPE, &act, NULL); /* Get EPIPE instead */
+
+ return rc;
+}
+
+static char *get_path(int af, const void *src)
+{
+ char ntop[INET6_ADDRSTRLEN + 1];
+ char format[PATH_MAX];
+ size_t ntop_len;
+ char *newstr, *ptr, *fptr;
+ int havep;
+ time_t now;
+
+ if (!inet_ntop(af, src, ntop, sizeof(ntop)))
+ {
+ syslog(LOG_ERR, "Unable to resolve address: %s",
+ strerror(errno));
+ return NULL;
+ }
+
+ ntop_len = strlen(ntop);
+
+ for (havep = 0, ptr = log_format, fptr = format; *ptr; ptr++)
+ {
+ switch (*ptr)
+ {
+ case '%':
+ if (havep)
+ {
+ /* '%%' should be passed on to strftime(3) */
+ *fptr = '%';
+ fptr++;
+ *fptr = '%';
+ fptr++;
+
+ havep = 0;
+ }
+ else
+ havep = 1;
+ break;
+
+ case 'Q':
+ if (havep)
+ {
+ /* Copy our address in */
+ memmove(fptr, ntop, ntop_len);
+ fptr += ntop_len;
+ havep = 0;
+ break;
+ }
+ /* Fall Through */
+
+ default:
+ if (havep)
+ {
+ *fptr = '%';
+ fptr++;
+ havep = 0;
+ }
+ *fptr = *ptr;
+ fptr++;
+ break;
+ }
+ }
+
+ newstr = malloc(sizeof(char) * PATH_MAX);
+ if (!newstr)
+ {
+ syslog(LOG_ERR,
+ "Unable to allocate memory while formating log filename");
+ return NULL;
+ }
+
+ *newstr = '\0';
+ strncat(newstr, log_prefix, PATH_MAX);
+ strncat(newstr, "/", PATH_MAX);
+
+ now = time(NULL);
+ if (!strftime(newstr + strlen(newstr), PATH_MAX - strlen(newstr),
+ format, localtime(&now)))
+ {
+ syslog(LOG_ERR, "Unable to format filename: %s",
+ strerror(errno));
+ free(newstr);
+ newstr = NULL;
+ }
+
+ return newstr;
+}
+
+struct dir_to_make
+{
+ struct list_head list;
+ char *path;
+};
+
+static int make_tree(const char *path, int mode)
+{
+ struct stat stat_buf;
+ char *ptr, *tmp;
+ int rc;
+ LIST_HEAD(to_make);
+ struct list_head *p, *n;
+ struct dir_to_make *m;
+
+ if (!path)
+ {
+ syslog(LOG_ERR, "Can\'t make empty path!");
+ return -EINVAL;
+ }
+
+ ptr = strdup(path);
+ if (!ptr)
+ {
+ syslog(LOG_ERR, "Unable to allocate memory for path \"%s\"",
+ path);
+ return -ENOMEM;
+ }
+
+ while (*ptr && strcmp(ptr, ".") && strcmp(ptr, "/"))
+ {
+ rc = stat(ptr, &stat_buf);
+ if (rc)
+ {
+ if (errno != ENOENT)
+ {
+ rc = -errno;
+ syslog(LOG_ERR,
+ "Unable to stat \"%s\" while creating \"%s\": %s",
+ ptr, path, strerror(-rc));
+ goto out_error;
+ }
+ }
+ else
+ {
+ if (!S_ISDIR(stat_buf.st_mode))
+ {
+ rc = -ENOTDIR;
+ syslog(LOG_ERR,
+ "Path \"%s\" is not a directory while creating \"%s\"\n",
+ ptr, path);
+ goto out_error;
+ }
+
+ /* Found an existing parent, we're done */
+ break;
+ }
+
+ /* If we got here, there's a path component we need to make */
+ rc = -ENOMEM;
+ m = malloc(sizeof(struct dir_to_make));
+ if (!m)
+ {
+ syslog(LOG_ERR,
+ "Unable to allocate memory while creating path \"%s\"",
+ path);
+ goto out_error;
+ }
+
+ list_add(&m->list, &to_make);
+ m->path = ptr;
+
+ /* Create a temporary copy for dirname(3) */
+ tmp = strdup(ptr);
+ if (!tmp)
+ {
+ syslog(LOG_ERR,
+ "Unable to allocate memory while creating path \"%s\"",
+ path);
+ goto out_error;
+ }
+
+ ptr = dirname(tmp);
+ ptr = strdup(ptr); /* Because dirname could return static */
+ free(tmp);
+ if (!ptr)
+ {
+ syslog(LOG_ERR,
+ "Unable to allocate memory while creating path \"%s\"",
+ path);
+ goto out_error;
+ }
+ }
+
+ list_for_each(p, &to_make) {
+ m = list_entry(p, struct dir_to_make, list);
+ rc = mkdir(m->path, mode);
+ if (rc)
+ {
+ rc = -errno;
+ syslog(LOG_ERR,
+ "Unable to create directory \"%s\" while creating path \"%s\": %s",
+ m->path, path, strerror(-rc));
+ goto out_error;
+ }
+ }
+
+ rc = 0;
+
+out_error:
+ free(ptr);
+
+ list_for_each_safe(p, n, &to_make) {
+ m = list_entry(p, struct dir_to_make, list);
+ list_del(&m->list);
+ if (m->path)
+ free(m->path);
+ free(m);
+ }
+
+ return rc;
+}
+
+static int open_socket()
+{
+ int rc;
+ struct sockaddr_in servaddr = {0, };
+
+ sock_fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sock_fd < 0)
+ {
+ rc = -errno;
+ syslog(LOG_ERR, "Unable to open socket: %s", strerror(-rc));
+ return rc;
+ }
+
+ servaddr.sin_family = PF_INET;
+ servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ servaddr.sin_port = htons(log_port);
+
+ rc = bind(sock_fd, (struct sockaddr *)&servaddr,
+ sizeof(struct sockaddr_in));
+ if (rc)
+ {
+ rc = -errno;
+ syslog(LOG_ERR, "Unable to bind socket: %s", strerror(-rc));
+ close(sock_fd);
+ sock_fd = -1;
+ return rc;
+ }
+
+ return 0;
+}
+
+/* Only return nonzero if fatal */
+static int do_output(const char *buf, int len,
+ struct sockaddr_in *addr, socklen_t socklen)
+{
+ int fd, rc, tot;
+ char *name, *tmp, *dir;
+
+ name = get_path(PF_INET, &addr->sin_addr);
+ if (!name)
+ return 0;
+
+ tmp = strdup(name);
+ if (!tmp)
+ {
+ syslog(LOG_ERR, "Unable to allocate memory while logging to \"%s\"",
+ name);
+ return 0;
+ }
+
+ dir = dirname(tmp);
+ rc = make_tree(dir, 0700);
+ free(tmp);
+
+ if (rc)
+ return 0;
+
+ fd = open(name, O_WRONLY | O_APPEND | O_CREAT, 0600);
+ if (fd < 0)
+ syslog(LOG_ERR, "Unable to open \"%s\": %s", name,
+ strerror(errno));
+ else
+ {
+ tot = 0;
+ while (tot < len)
+ {
+ rc = write(fd, buf + tot, len - tot);
+ if (rc < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "Error writing to \"%s\": %s", name,
+ strerror(errno));
+ break;
+ }
+ tot += rc;
+ }
+ close(fd);
+ }
+ free(name);
+
+ return 0;
+}
+
+static int set_blocking(int blocking)
+{
+ int flags, rc;
+
+ flags = fcntl(sock_fd, F_GETFL, 0);
+ if (flags < 0)
+ {
+ rc = -errno;
+ syslog(LOG_ERR, "Cannot get blocking state: %s", strerror(-rc));
+ return rc;
+ }
+
+ if (blocking)
+ flags &= ~O_NONBLOCK;
+ else
+ flags |= O_NONBLOCK;
+
+ rc = fcntl(sock_fd, F_SETFL, flags);
+ if (rc)
+ {
+ rc = -errno;
+ syslog(LOG_ERR, "Cannot set blocking state: %s", strerror(-rc));
+ }
+
+ return rc;
+}
+
+/*
+ * There is some magic state here. To allow us to batch things
+ * if we want, we go O_NONBLOCK once data is available. Once we
+ * see that data isn't available anymore, we go back to blocking.
+ */
+static int do_recvfrom(int fd, void *buf, size_t bufsize, int flags,
+ struct sockaddr_in *from, socklen_t *fromlen)
+{
+ int rc;
+ static int block = 1;
+
+ rc = recvfrom(sock_fd, buf, bufsize, 0,
+ (struct sockaddr *)from, fromlen);
+ if (rc < 0)
+ {
+ rc = -errno;
+ if (rc == -EAGAIN)
+ {
+ if (!block)
+ {
+ rc = set_blocking(1);
+ if (!rc)
+ {
+ block = 1;
+ rc = -EAGAIN;
+ }
+ else
+ syslog(LOG_ERR,
+ "Never blocking means eating CPU, goodbye");
+ }
+ }
+ else if (rc != -EINTR)
+ syslog(LOG_ERR, "Error reading from socket: %s",
+ strerror(-rc));
+ }
+ else
+ {
+ if (block)
+ {
+ if (!set_blocking(0))
+ block = 0;
+ else
+ syslog(LOG_ERR, "Unable to set nonblocking");
+ }
+ }
+
+ return rc;
+}
+
+static int run()
+{
+ int rc;
+ char *buf;
+ size_t bufsize = 8 * getpagesize();
+ struct sockaddr_in from;
+ socklen_t fromlen;
+
+ buf = malloc(sizeof(char) * bufsize);
+ if (!buf)
+ {
+ syslog(LOG_ERR,
+ "Unable to allocate memory for receive buffer: %s",
+ strerror(errno));
+ return -ENOMEM;
+ }
+
+ syslog(LOG_INFO, "Logging to %s on port %d", log_prefix, log_port);
+ while (1)
+ {
+ rc = do_recvfrom(sock_fd, buf, bufsize, 0, &from, &fromlen);
+ if (rc < 0)
+ {
+ if (rc == -EAGAIN)
+ continue;
+ if (rc == -EINTR)
+ syslog(LOG_INFO, "Caught signal %d",
+ caught_sig);
+ else
+ syslog(LOG_ERR, "Error reading from socket: %s",
+ strerror(-rc));
+ break;
+ }
+ /* For now, we process one at a time */
+ rc = do_output(buf, rc, &from, fromlen);
+ if (rc) /* do_output() better not return error if nonfatal */
+ break;
+ }
+ syslog(LOG_INFO, "Shutting down");
+
+ return 0;
+}
+
+static int init_dir()
+{
+ int rc;
+ rc = make_tree(log_prefix, 0755);
+ if (!rc)
+ {
+ rc = chdir(log_prefix);
+ if (rc)
+ {
+ rc = -errno;
+ syslog(LOG_ERR, "Unable to change to \"%s\"; %s",
+ log_prefix, strerror(-rc));
+ }
+ }
+
+ return rc;
+}
+
+static int init_self()
+{
+ int rc;
+
+ openlog(PROGNAME, LOG_PERROR | LOG_PID, LOG_DAEMON);
+
+ rc = init_dir();
+ if (rc)
+ return rc;
+
+ if (daemonize)
+ {
+ rc = daemon(1, 0);
+ if (rc)
+ {
+ rc = -errno;
+ syslog(LOG_ERR, "daemon() failed: %s", strerror(-rc));
+ }
+ }
+
+ if (!rc)
+ {
+ rc = setup_signals();
+ if (rc)
+ syslog(LOG_ERR, "Unable to set up signal handling");
+ }
+
+ return rc;
+}
+
+static int valid_format()
+{
+ struct in_addr addr = {0, };
+ char *name;
+ int rc;
+
+ name = get_path(PF_INET, &addr);
+ rc = !!name;
+ if (name)
+ free(name);
+
+ return rc;
+}
+
+static void print_version(void)
+{
+ fprintf(stdout, PROGNAME " version %s\n", VERSION);
+ exit(0);
+}
+
+static void print_usage(int rc)
+{
+ FILE *output = rc ? stderr : stdout;
+
+ fprintf(output,
+ "Usage: " PROGNAME " [-l <log_prefix>] [-o <log_name_format>] [-p <port>]\n"
+ " " PROGNAME " -h\n"
+ " " PROGNAME " -V\n");
+
+ exit(rc);
+}
+
+extern char *optarg;
+extern int optopt;
+extern int opterr;
+static int parse_options(int argc, char *argv[])
+{
+ int c;
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, ":hVDl:o:p:-:")) != EOF)
+ {
+ switch (c)
+ {
+ case 'h':
+ print_usage(0);
+ break;
+
+ case 'V':
+ print_version();
+ break;
+
+ case '-':
+ if (!strcmp(optarg, "version"))
+ print_version();
+ else if (!strcmp(optarg, "help"))
+ print_usage(0);
+ else
+ {
+ fprintf(stderr,
+ PROGNAME ": Invalid option: \'--%s\'\n",
+ optarg);
+ print_usage(-EINVAL);
+ }
+ break;
+
+ case 'l':
+ if (!optarg || !*optarg)
+ {
+ fprintf(stderr,
+ PROGNAME ": Invalid log prefix \"%s\"\n",
+ optarg);
+ print_usage(-EINVAL);
+ }
+ log_prefix = optarg;
+ break;
+
+ case 'o':
+ log_format = optarg;
+ if (!log_format || !*log_format || !valid_format())
+ {
+ fprintf(stderr,
+ PROGNAME ": Invalid log filename format \"%s\"\n",
+ optarg);
+ print_usage(-EINVAL);
+ }
+ break;
+
+ case 'p':
+ log_port = (uint16_t)(atoi(optarg) & (uint16_t)-1);
+ if (!log_port)
+ {
+ fprintf(stderr,
+ PROGNAME ": Invalid port: \"%s\"\n",
+ optarg);
+ print_usage(-EINVAL);
+ }
+ break;
+
+ case 'D':
+ daemonize = 0;
+ break;
+
+ case '?':
+ fprintf(stderr, PROGNAME ": Invalid option: \'-%c\'\n",
+ optopt);
+ print_usage(-EINVAL);
+ break;
+
+ case ':':
+ fprintf(stderr, PROGNAME ": Option \'-%c\' requires an argument\n",
+ optopt);
+ print_usage(-EINVAL);
+ break;
+
+ default:
+ fprintf(stderr, PROGNAME ": Shouldn\'t get here\n");
+ exit(1);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+
+ rc = parse_options(argc, argv);
+ if (rc)
+ return rc;
+
+ rc = init_self();
+ if (rc)
+ goto out;
+
+ rc = open_socket();
+ if (rc)
+ goto out;
+
+ rc = run();
+
+ close(sock_fd);
+
+out:
+ closelog();
+
+ return rc;
+}
diff --git a/cancd.init b/cancd.init
new file mode 100755
index 0000000..69902c6
--- /dev/null
+++ b/cancd.init
@@ -0,0 +1,206 @@
+#! /bin/sh
+# init fragment for cancd
+#
+# chkconfig: 2345 29 20
+# description: Start cancd at system boot
+#
+### BEGIN INIT INFO
+# Provides: cancd
+# Required-Start: network
+# Should-Start:
+# Required-Stop:
+# Default-Start: 2 3 5
+# Default-Stop:
+# Description: start cancd driver at system boot
+### END INIT INFO
+
+
+# Force LC_ALL=C
+export LC_ALL=C
+
+if [ -f /etc/redhat-release ]
+then
+. /etc/init.d/functions
+
+init_status()
+{
+ return 0
+}
+
+success_status()
+{
+ success
+ echo
+}
+
+failure_status()
+{
+ failure $1
+ echo
+}
+
+exit_status()
+{
+ exit $?
+}
+elif [ -f /etc/SuSE-release -o -f /etc/UnitedLinux-release ]
+then
+. /etc/rc.status
+
+init_status()
+{
+ rc_reset
+}
+
+success_status()
+{
+ /bin/true
+ rc_status -v
+}
+
+failure_status()
+{
+ /bin/false
+ rc_status -v
+}
+
+exit_status()
+{
+ rc_exit
+}
+else
+init_status()
+{
+ return 0
+}
+
+success_status()
+{
+ echo "OK"
+ return 0
+}
+
+failure_status()
+{
+ echo "Failed"
+ return 0
+}
+
+exit_status()
+{
+ exit $?
+}
+fi
+
+# Source configuration
+[ -f /etc/sysconfig/cancd ] && . /etc/sysconfig/cancd
+
+init_status
+
+
+#
+# if_fail()
+#
+# Evaluates return codes. If 0, prints "OK", if 1, prints "Failed"
+# and exits. If 2, status is "already done" and nothing is printed.
+# The rest of the functions in here all honor this convention.
+#
+if_fail()
+{
+ RC="$1"
+ REASON="$2"
+ if [ "$RC" = "0" ]
+ then
+ success_status
+ return
+ elif [ "$RC" = "2" ]
+ then
+ return
+ fi
+ failure_status ${REASON}
+ exit 1
+}
+
+find_running()
+{
+ ps -ef | awk '/awk/{next}/\/usr\/sbin\/cancd/{print $2}'
+}
+
+start()
+{
+ OPT_PORT=
+ if [ -n "$CANCD_PORT" ]
+ then
+ OPT_PORT="-p ${CANCD_PORT}"
+ fi
+ OPT_PATH=
+ if [ -n "$CANCD_PATH" ]
+ then
+ OPT_PATH="-l ${CANCD_PATH}"
+ fi
+
+ echo -n "Starting cancd server: "
+ OUTPUT="`find_running 2>/dev/null`"
+ if [ -z "$OUTPUT" ]
+ then
+ if [ -n "$CANCD_USER" ]
+ then
+ su - "$CANCD_USER" -c "/usr/sbin/cancd $OPT_PORT $OPT_PATH"
+ else
+ /usr/sbin/cancd $OPT_PORT $OPT_PATH
+ fi
+ if_fail $? "Unable to start cancd"
+ else
+ if_fail 0 "Already running"
+ fi
+}
+
+stop()
+{
+ echo -n "Stopping cancd: "
+ OUTPUT="`find_running 2>/dev/null`"
+ if [ -z "$OUTPUT" ]
+ then
+ if_fail 0 "Not running"
+ else
+ kill $OUTPUT
+ if_fail $? "Unable to kill cancd"
+ fi
+}
+
+status()
+{
+ OUTPUT="`find_running 2>/dev/null`"
+ if [ -n "$OUTPUT" ]
+ then
+ echo "cancd is running"
+ else
+ echo "cancd is not running"
+ fi
+}
+
+case "$1" in
+ start)
+ start
+ ;;
+
+ status)
+ status
+ ;;
+
+ stop)
+ stop
+ ;;
+
+ restart)
+ stop
+ start
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|restart|status}"
+ exit 1
+esac
+
+exit 0
+
diff --git a/cancd.spec b/cancd.spec
new file mode 100644
index 0000000..141b1e5
--- /dev/null
+++ b/cancd.spec
@@ -0,0 +1,55 @@
+#
+# Spec file for cvsman
+#
+
+# Macros
+# This one is hardcoded because, well, it belongs there
+%define _prefix /usr
+
+Summary: The CA NetConsole Daemon
+Name: cancd
+Version: %{CANCD_VERSION}
+Release: 1
+Copyright: GPL
+Group: Applications/File
+Source: http://oss.oracle.com/projects/cancd/files/source/cancd-%{PACKAGE_VERSION}.tar.gz
+URL: http://oss.oracle.com/projects/cancd/
+Distribution: CALPG
+Vendor: Oracle Corporate Architecture Linux Projects Group
+Packager: Joel Becker <joel.becker@oracle.com>
+
+# Needs an initscripts require
+Requires: initscripts >= 6.44, %{CHKCONFIG_DEP}
+
+BuildRoot: %{_tmppath}/cancd-%{PACKAGE_VERSION}-%{PACKAGE_RELEASE}-root
+
+%description
+This is the CA NetConsole Daemon, a daemon to receive output from the
+Linux netconsole driver.
+
+%prep
+%setup
+
+%build
+make
+
+%install
+make DESTDIR="$RPM_BUILD_ROOT" install
+mkdir "${RPM_BUILD_ROOT}/etc/rc.d"
+mv "${RPM_BUILD_ROOT}/etc/init.d" "${RPM_BUILD_ROOT}/etc/rc.d"
+
+%clean
+rm -rf "$RPM_BUILD_ROOT"
+
+%post
+/sbin/chkconfig --add cancd
+
+%preun
+if [ $1 = 0 ]; then
+ /sbin/chkconfig --del cancd
+fi
+
+%files
+%defattr(-,root,root)
+/usr/sbin/cancd
+%config /etc/rc.d/init.d/cancd
diff --git a/kernel-list.h b/kernel-list.h
new file mode 100644
index 0000000..a922d7a
--- /dev/null
+++ b/kernel-list.h
@@ -0,0 +1,116 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = { &name, &name }
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+#if (!defined(__GNUC__) && !defined(__WATCOMC__))
+#define __inline__
+#endif
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/*
+ * Insert a new entry after the specified head..
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Insert a new entry at the tail
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/*
+ * Splice in "list" into "head"
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#endif