Compare commits

...
3 Commits
Author SHA1 Message Date
Joachim Nilsson cbb50937cd Update ChangeLog and bump version.
New release, 1.5! :)

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-03 23:30:23 +02:00
Joachim Nilsson 83a4bf7947 Add dependency resolver for plugins.
This has been something I wanted to avoid, but then I stumbled so hard
on it myself I simply had to add it.  With this commit finit actually
comes of age -- it's now possible to do anything a modern init can --
only faster! :-)

Included in this commit is the OpenBSD version of sys/queue.h, which
unline all versions of the same file on Linux has the _FOREACH_SAFE
macros for iterating over a linked list.

The plugins are now in a TAILQ, loaded in alphabetic order and, unless
the dependency resolver says otherwise, added last in the queue.  So,
naming your plugins according to standard sysvinit rules: S01, S02,
etc. takes care of the most basic dependency resolution.  When that is
not sufficient, simply add a line ".depends = { "other_plugin",
"another_plugin" }," to your plugin_t definition.  Be careful though
for circular dependencies, we don't check for those.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-03 23:20:34 +02:00
Joachim Nilsson 0d7d5a9820 Setup standard FHS 2.3 structure in /var
This was missing from the 1.4 release due to it being hidden in one
of my private plugins. Sorry!

Also, add another log macro. Warnings are quite useful things to have.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2012-10-03 11:44:33 +02:00
11 changed files with 759 additions and 36 deletions
+5
View File
@@ -1,3 +1,8 @@
1.5 (20121003)
- Use bootmisc plugin to setup standard FHS 2.3 structure in /var
- Added FLOG_WARN() syslog macro, for plugins
- Add plugin dependency resolver. Checks plugin_t for .depends
1.4 (20121002) 1.4 (20121002)
- Fix I/O plugin watcher and load plugins earlier for a new hook - Fix I/O plugin watcher and load plugins earlier for a new hook
- Start refactoring helpers.c into a libite.so (-lite). This - Start refactoring helpers.c into a libite.so (-lite). This
+2 -2
View File
@@ -29,13 +29,13 @@
ROOTDIR = $(shell pwd) ROOTDIR = $(shell pwd)
#VERSION ?= $(shell git tag -l | tail -1) #VERSION ?= $(shell git tag -l | tail -1)
VERSION ?= 1.4 VERSION ?= 1.5
NAME = finit NAME = finit
PKG = $(NAME)-$(VERSION) PKG = $(NAME)-$(VERSION)
DEV = $(NAME)-dev DEV = $(NAME)-dev
ARCHIVE = $(PKG).tar.xz ARCHIVE = $(PKG).tar.xz
EXEC = finit reboot EXEC = finit reboot
HEADERS = plugin.h svc.h helpers.h HEADERS = plugin.h svc.h helpers.h queue.h
DISTFILES = LICENSE README ChangeLog finit.conf services DISTFILES = LICENSE README ChangeLog finit.conf services
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o
OBJS += strlcpy.o OBJS += strlcpy.o
+1
View File
@@ -36,6 +36,7 @@
} }
#define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args) #define FLOG_DEBUG(fmt, args...) DO_LOG(LOG_DEBUG, fmt, ##args)
#define FLOG_WARN(fmt, args...) DO_LOG(LOG_WARNING, fmt, ##args)
#define FLOG_ERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt, ##args) #define FLOG_ERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt, ##args)
#define FLOG_PERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt ". Error %d: %s", ##args, errno, strerror(errno)) #define FLOG_PERROR(fmt, args...) DO_LOG(LOG_CRIT, fmt ". Error %d: %s", ##args, errno, strerror(errno))
+144 -30
View File
@@ -26,18 +26,22 @@
#include <dirent.h> /* readdir() et al */ #include <dirent.h> /* readdir() et al */
#include <poll.h> #include <poll.h>
#include <string.h> #include <string.h>
#include <sys/queue.h> /* BSD sys/queue.h API */
#include "finit.h" #include "finit.h"
#include "private.h" #include "private.h"
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
#include "queue.h" /* BSD sys/queue.h API */
#include "lite.h"
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0) #define is_io_plugin(p) ((p)->io.cb && (p)->io.fd >= 0)
static size_t num_fds = 0; static char *plugpath = NULL; /* Set by first load. */
static size_t num_fds = 0;
static struct pollfd fds[MAX_NUM_FDS]; static struct pollfd fds[MAX_NUM_FDS];
static LIST_HEAD(, plugin) plugins = LIST_HEAD_INITIALIZER(); static TAILQ_HEAD(, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
static void check_plugin_depends(plugin_t *plugin);
int plugin_register(plugin_t *plugin) int plugin_register(plugin_t *plugin)
{ {
@@ -57,6 +61,14 @@ int plugin_register(plugin_t *plugin)
plugin->name = (char *)info.dli_fname; plugin->name = (char *)info.dli_fname;
} }
/* Already registered? */
if (plugin_find(plugin->name)) {
_d("... %s already loaded.", plugin->name);
return 0;
}
check_plugin_depends(plugin);
if (is_io_plugin(plugin)) { if (is_io_plugin(plugin)) {
if (num_fds + 1 >= MAX_NUM_FDS) { if (num_fds + 1 >= MAX_NUM_FDS) {
num_fds = MAX_NUM_SVC; num_fds = MAX_NUM_SVC;
@@ -88,14 +100,14 @@ int plugin_register(plugin_t *plugin)
return 1; return 1;
} }
LIST_INSERT_HEAD(&plugins, plugin, link); TAILQ_INSERT_TAIL(&plugins, plugin, link);
return 0; return 0;
} }
int plugin_unregister(plugin_t *plugin) int plugin_unregister(plugin_t *plugin)
{ {
LIST_REMOVE(plugin, link); TAILQ_REMOVE(&plugins, plugin, link);
if (plugin->svc.cb) { if (plugin->svc.cb) {
svc_t *svc = svc_find(plugin->name); svc_t *svc = svc_find(plugin->name);
@@ -111,12 +123,64 @@ int plugin_unregister(plugin_t *plugin)
return 0; return 0;
} }
#define SEARCH_PLUGIN(str) \
PLUGIN_ITERATOR(p, tmp) { \
_d("Comparing %s against plugin %s", str, p->name); \
if (!strcmp(p->name, str)) \
return p; \
}
/**
* plugin_find - Find a plugin by name
* @name: With or without path, or .so extension
*
* This function uses an opporunistic search for a suitable plugin and
* returns the first match. Albeit with at least some measure of
* heuristics.
*
* First it checks for an exact match. If no match is found and @name
* starts with a slash the search ends. Otherwise a new search with the
* plugin path prepended to @name is made. Also, if @name does not end
* with .so it too is added to @name before searching.
*
* Returns:
* On success the pointer to the matching &plugin_t is returned,
* otherwise %NULL is returned.
*/
plugin_t *plugin_find(char *name)
{
plugin_t *p, *tmp;
if (!name) {
errno = EINVAL;
return NULL;
}
SEARCH_PLUGIN(name);
if (name[0] != '/') {
int noext;
char path[256];
noext = strcmp(name + strlen(name) - 3, ".so");
snprintf (path, sizeof(path), "%s%s%s%s", plugpath,
fisslashdir(plugpath) ? "" : "/",
name, noext ? ".so" : "");
SEARCH_PLUGIN(path);
}
errno = ENOENT;
return NULL;
}
/* Private daemon API *******************************************************/ /* Private daemon API *******************************************************/
void plugin_run_hooks(hook_point_t no) void plugin_run_hooks(hook_point_t no)
{ {
plugin_t *p; plugin_t *p, *tmp;
PLUGIN_ITERATOR(p) { PLUGIN_ITERATOR(p, tmp) {
if (p->hook[no].cb) { if (p->hook[no].cb) {
_d("Calling %s hook n:o %d from runloop...", basename(p->name), no); _d("Calling %s hook n:o %d from runloop...", basename(p->name), no);
p->hook[no].cb(p->hook[no].arg); p->hook[no].cb(p->hook[no].arg);
@@ -127,10 +191,10 @@ void plugin_run_hooks(hook_point_t no)
/* Generic libev I/O callback, looks up correct plugin and calls its callback */ /* Generic libev I/O callback, looks up correct plugin and calls its callback */
static void generic_io_cb(int fd, int events) static void generic_io_cb(int fd, int events)
{ {
plugin_t *p; plugin_t *p, *tmp;
/* Find matching plugin, pick first matching fd */ /* Find matching plugin, pick first matching fd */
PLUGIN_ITERATOR(p) { PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p) && p->io.fd == fd) { if (is_io_plugin(p) && p->io.fd == fd) {
_d("Calling I/O %s from runloop...", basename(p->name)); _d("Calling I/O %s from runloop...", basename(p->name));
p->io.cb(p->io.arg, fd, events); p->io.cb(p->io.arg, fd, events);
@@ -168,9 +232,9 @@ void plugin_monitor(void)
static void init_plugins(void) static void init_plugins(void)
{ {
int i = 0; int i = 0;
plugin_t *p; plugin_t *p, *tmp;
PLUGIN_ITERATOR(p) { PLUGIN_ITERATOR(p, tmp) {
if (is_io_plugin(p)) { if (is_io_plugin(p)) {
_d("Initializing plugin %s for I/O", basename(p->name)); _d("Initializing plugin %s for I/O", basename(p->name));
fds[i].revents = 0; fds[i].revents = 0;
@@ -180,6 +244,70 @@ static void init_plugins(void)
} }
} }
/**
* load_one - Load one plugin
* @path: Path to finit plugins, usually %PLUGIN_PATH
* @name: Name of plugin, optionally ending in ".so"
*
* Loads a plugin from @path/@name[.so]. Note, if ".so" is missing from
* the plugin @name it is added before attempting to load.
*
* It is up to the plugin itself ot register itself as a "ctor" with the
* %PLUGIN_INIT macro so that plugin_register() is called automatically.
*
* Returns:
* POSIX OK(0) on success, non-zero otherwise.
*/
static int load_one(char *path, char *name)
{
int noext;
char plugin[256];
void *handle;
if (!path || !fisdir(path)) {
errno = EINVAL;
return 1;
}
noext = strcmp(name + strlen(name) - 3, ".so");
snprintf(plugin, sizeof(plugin), "%s/%s%s", path, name, noext ? ".so" : "");
_d("Loading plugin %s ...", basename(plugin));
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
_d("Failed loading plugin %s: %s", plugin, dlerror());
return 1;
}
return 0;
}
/**
* check_plugin_depends - Check and load any plugins this one depends on.
* @plugin: Plugin with possible depends to check
*
* Very simple dependency resolver, should actually load the plugin of
* the correct .name, but currently loads a matching filename.
*
* Works, but only for now. A better way might be to have a try_load()
* that actually loads all plugins and checks their &plugin_t for the
* correct .name.
*/
static void check_plugin_depends(plugin_t *plugin)
{
int i;
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
_d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
if (plugin_find(plugin->depends[i])) {
_d("OK plugin %s was already loaded.", plugin->depends[i]);
continue;
}
load_one(plugpath, plugin->depends[i]);
}
}
int plugin_load_all(char *path) int plugin_load_all(char *path)
{ {
int fail = 0; int fail = 0;
@@ -190,27 +318,13 @@ int plugin_load_all(char *path)
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno)); _e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
return 1; return 1;
} }
plugpath = path;
while ((entry = readdir(dp))) { while ((entry = readdir(dp))) {
char *ext = entry->d_name + strlen(entry->d_name) - 3; // print_desc(" Loading plugin ", basename(plugin));
if (load_one(path, entry->d_name))
if (!strcmp(ext, ".so")) { fail++;
int result = 0; // print_result(result);
void *handle;
char plugin[1024];
snprintf(plugin, sizeof(plugin), "%s/%s", path, entry->d_name);
// print_desc(" Loading plugin ", basename(plugin));
handle = dlopen(plugin, RTLD_LAZY | RTLD_GLOBAL);
if (!handle) {
_d("Failed loading plugin %s: %s", plugin, dlerror());
result = 1;
}
if (result)
fail++;
// print_result(result);
}
} }
closedir(dp); closedir(dp);
+10 -4
View File
@@ -25,16 +25,17 @@
#define FINIT_PLUGIN_H_ #define FINIT_PLUGIN_H_
#include <poll.h> #include <poll.h>
#include <sys/queue.h> /* BSD sys/queue.h API */ #include "queue.h" /* BSD sys/queue.h API */
#include "svc.h" #include "svc.h"
#define PLUGIN_DEP_MAX 10
#define PLUGIN_IO_READ POLLIN #define PLUGIN_IO_READ POLLIN
#define PLUGIN_IO_WRITE POLLOUT #define PLUGIN_IO_WRITE POLLOUT
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void) #define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void) #define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
#define PLUGIN_ITERATOR(x) LIST_FOREACH(x, &plugins, link) #define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
/* /*
* Predefined hook points for easier plugin debugging * Predefined hook points for easier plugin debugging
@@ -76,9 +77,9 @@ typedef enum {
*/ */
typedef struct plugin { typedef struct plugin {
/* BSD sys/queue.h linked list node. */ /* BSD sys/queue.h linked list node. */
LIST_ENTRY(plugin) link; TAILQ_ENTRY(plugin) link;
/* Plugin name, defaults to plugin path if unset. /* Plugin name, defaults to basename of plugin path if unset.
* NOTE: Must be same as @cmd for service plugins! */ * NOTE: Must be same as @cmd for service plugins! */
char *name; char *name;
@@ -101,12 +102,17 @@ typedef struct plugin {
void *arg; void *arg;
void (*cb)(void *arg, int fd, int events); void (*cb)(void *arg, int fd, int events);
} io; } io;
char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */
} plugin_t; } plugin_t;
/* Public plugin API */ /* Public plugin API */
int plugin_register (plugin_t *plugin); int plugin_register (plugin_t *plugin);
int plugin_unregister (plugin_t *plugin); int plugin_unregister (plugin_t *plugin);
/* Helper API */
plugin_t *plugin_find (char *name);
#endif /* FINIT_PLUGIN_H_ */ #endif /* FINIT_PLUGIN_H_ */
/** /**
+25
View File
@@ -28,12 +28,32 @@
#include "helpers.h" #include "helpers.h"
#include "plugin.h" #include "plugin.h"
/*
* Setup standard FHS 2.3 structure in /var, and
* write runlevel to UTMP, needed by, e.g., printerdrake.
*/
static void setup(void *UNUSED(arg)) static void setup(void *UNUSED(arg))
{ {
#ifdef RUNLEVEL #ifdef RUNLEVEL
struct utmp entry; struct utmp entry;
#endif #endif
_d("Setting up FHS structure in /var ...");
mkdir("/var/cache", 0755);
mkdir("/var/games", 0755);
mkdir("/var/lib", 0755);
mkdir("/var/lib/misc", 0755);
mkdir("/var/lib/alarm", 0755);
mkdir("/var/lock", 0755);
mkdir("/var/log", 0755);
mkdir("/var/mail", 0755);
mkdir("/var/opt", 0755);
mkdir("/var/run", 0755);
mkdir("/var/spool", 0755);
mkdir("/var/spool/cron", 0755);
mkdir("/var/tmp", 0755);
mkdir("/var/empty", 0755);
_d("Setting up necessary UTMP files ..."); _d("Setting up necessary UTMP files ...");
touch("/var/run/utmp"); touch("/var/run/utmp");
chown("/var/run/utmp", 0, getgroup("utmp")); chown("/var/run/utmp", 0, getgroup("utmp"));
@@ -55,12 +75,17 @@ static void setup(void *UNUSED(arg))
#endif #endif
_d("Setting up misc files ..."); _d("Setting up misc files ...");
mkdir("/var/run/lldpd", 0755); /* Needed by lldpd */
mkdir("/var/run/pluto", 0755); /* Needed by Openswan */
mkdir("/var/run/quagga", 0755); /* Needed by Quagga */
mkdir("/var/log/quagga", 0755); /* Needed by Quagga */
mkdir("/var/run/sshd", 01755); /* OpenSSH */ mkdir("/var/run/sshd", 01755); /* OpenSSH */
mkfifo("/dev/xconsole", 0640); /* sysklogd */ mkfifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, getgroup("tty")); chown("/dev/xconsole", 0, getgroup("tty"));
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = "bootmisc",
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
+1
View File
@@ -41,6 +41,7 @@ static plugin_t plugin = {
.hook[HOOK_NETWORK_UP] = { .hook[HOOK_NETWORK_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+1
View File
@@ -68,6 +68,7 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", },
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+1
View File
@@ -44,6 +44,7 @@ static plugin_t plugin = {
.hook[HOOK_BASEFS_UP] = { .hook[HOOK_BASEFS_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+1
View File
@@ -56,6 +56,7 @@ static plugin_t plugin = {
.hook[HOOK_NETWORK_UP] = { .hook[HOOK_NETWORK_UP] = {
.cb = setup .cb = setup
}, },
.depends = { "bootmisc", }
}; };
PLUGIN_INIT(plugin_init) PLUGIN_INIT(plugin_init)
+568
View File
@@ -0,0 +1,568 @@
/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head).cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head).cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */