diff options
author | Joern Engel <joern@logfs.org> | 2011-12-18 20:03:35 -0800 |
---|---|---|
committer | Joern Engel <joern@logfs.org> | 2011-12-18 20:03:35 -0800 |
commit | b74052cde99a3631e54cc30dadecdbffc00a4b30 (patch) | |
tree | 59fb973d65339b55d89ed836e4a5c2b0c0a0491f | |
download | cancd-b74052cde99a3631e54cc30dadecdbffc00a4b30.tar.gz |
cancd 0.1.0 as found on oss.oracle.com
Signed-off-by: Joern Engel <joern@logfs.org>
-rw-r--r-- | Makefile | 45 | ||||
-rw-r--r-- | cancd.c | 719 | ||||
-rwxr-xr-x | cancd.init | 206 | ||||
-rw-r--r-- | cancd.spec | 55 | ||||
-rw-r--r-- | kernel-list.h | 116 |
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" @@ -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 |