1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-27 10:25:13 +03:00

Initial import of the cluster log daemon from the 'cluster' repository.

There is a rudimentary make file in place so people can build by hand
from 'LVM2/daemons/clogd'.  It is not hooked into the main build system
yet.  I am checking this in to provide people better access to the
source code.

There is still work to be done to make better use of existing code in
the LVM repository.  (list.h could be removed in favor of existing list
implementations, for example.  Logging might also be removed in favor
of what is already in the tree.)

I will probably defer updating WHATS_NEW_DM until this code is linked
into the main build system (unless otherwise instructed).
This commit is contained in:
Jonathan Earl Brassow 2009-01-08 17:12:33 +00:00
parent c48e40391b
commit bf0378593c
14 changed files with 4884 additions and 0 deletions

77
daemons/clogd/Makefile Normal file
View File

@ -0,0 +1,77 @@
###############################################################################
###############################################################################
##
## Copyright (C) 2009 Red Hat, Inc. All rights reserved.
##
## This copyrighted material is made available to anyone wishing to use,
## modify, copy, or redistribute it subject to the terms and conditions
## of the GNU General Public License v.2.
##
###############################################################################
###############################################################################
SOURCES = clogd.c cluster.c functions.c link_mon.c local.c logging.c
TARGET = $(shell if [ ! -e /usr/include/linux/dm-clog-tfr.h ]; then \
echo 'no_clogd_kernel_headers'; \
elif [ ! -e /usr/include/linux/ext2_fs.h ]; then \
echo 'no_e2fsprogs_devel'; \
elif [ ! -e /usr/include/openais/saCkpt.h ]; then \
echo 'no_openais_devel'; \
else \
echo 'clogd'; \
fi)
ifneq ($(DEBUG), )
CFLAGS += -DDEBUG
endif
ifneq ($(MEMB), )
CFLAGS += -DMEMB
endif
ifneq ($(CKPT), )
CFLAGS += -DCKPT
endif
ifneq ($(RESEND), )
CFLAGS += -DRESEND
endif
CFLAGS += -g
LDFLAGS += $(shell if [ -e /usr/lib64/openais ]; then \
echo '-L/usr/lib64/openais -L/usr/lib64'; \
else \
echo '-L/usr/lib/openais -L/usr/lib'; \
fi)
LDFLAGS += -lcpg -lSaCkpt -lext2fs
all: ${TARGET}
clogd: ${SOURCES}
${CC} ${CFLAGS} -o $@ $^ ${LDFLAGS}
no_clogd_kernel_headers:
echo "Unable to find clogd kernel headers"
exit 1
no_e2fsprogs_devel:
echo "Unable to find ext2fs kernel headers."
echo "Install 'e2fsprogs-devel'?"
exit 1
no_openais_devel:
echo "Unable to find openAIS headers."
echo "http://sources.redhat.com/cluster/wiki/"
exit 1
install: clogd
install -d /usr/sbin
install clogd /usr/sbin
uninstall:
rm /usr/sbin/clogd
clean:
rm -f *.o clogd *~

283
daemons/clogd/clogd.c Normal file
View File

@ -0,0 +1,283 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/dm-clog-tfr.h>
#include <linux/dm-ioctl.h>
#include "functions.h"
#include "local.h"
#include "cluster.h"
#include "common.h"
#include "logging.h"
#include "link_mon.h"
static int exit_now = 0;
static sigset_t signal_mask;
static int signal_received;
static void process_signals(void);
static void daemonize(void);
static void init_all(void);
static void cleanup_all(void);
static void set_priority(void);
int main(int argc, char *argv[])
{
daemonize();
init_all();
/* Parent can now exit, we're ready to handle requests */
kill(getppid(), SIGTERM);
/* set_priority(); -- let's try to do w/o this */
LOG_PRINT("Starting clogd:");
LOG_PRINT(" Built: "__DATE__" "__TIME__"\n");
LOG_DBG(" Compiled with debugging.");
while (!exit_now) {
links_monitor();
links_issue_callbacks();
process_signals();
}
exit(EXIT_SUCCESS);
}
/*
* parent_exit_handler: exit the parent
* @sig: the signal
*
*/
static void parent_exit_handler(int sig)
{
exit_now = 1;
}
/*
* create_lockfile - create and lock a lock file
* @lockfile: location of lock file
*
* Returns: 0 on success, -1 otherwise
*/
static int create_lockfile(char *lockfile)
{
int fd;
struct flock lock;
char buffer[50];
if((fd = open(lockfile, O_CREAT | O_WRONLY,
(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0)
return -errno;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) < 0) {
close(fd);
return -errno;
}
if (ftruncate(fd, 0) < 0) {
close(fd);
return -errno;
}
sprintf(buffer, "%d\n", getpid());
if(write(fd, buffer, strlen(buffer)) < strlen(buffer)){
close(fd);
unlink(lockfile);
return -errno;
}
return 0;
}
static void sig_handler(int sig)
{
sigaddset(&signal_mask, sig);
++signal_received;
}
static void process_signal(int sig){
int r = 0;
switch(sig) {
case SIGINT:
case SIGQUIT:
case SIGTERM:
case SIGHUP:
r += log_status();
break;
case SIGUSR1:
case SIGUSR2:
log_debug();
/*local_debug();*/
cluster_debug();
return;
default:
LOG_PRINT("Unknown signal received... ignoring");
return;
}
if (!r) {
LOG_DBG("No current cluster logs... safe to exit.");
cleanup_all();
exit(EXIT_SUCCESS);
}
LOG_ERROR("Cluster logs exist. Refusing to exit.");
}
static void process_signals(void)
{
int x;
if (!signal_received)
return;
signal_received = 0;
for (x = 1; x < _NSIG; x++) {
if (sigismember(&signal_mask, x)) {
sigdelset(&signal_mask, x);
process_signal(x);
}
}
}
/*
* daemonize
*
* Performs the steps necessary to become a daemon.
*/
static void daemonize(void)
{
int pid;
int status;
signal(SIGTERM, &parent_exit_handler);
pid = fork();
if (pid < 0) {
LOG_ERROR("Unable to fork()");
exit(EXIT_FAILURE);
}
if (pid) {
/* Parent waits here for child to get going */
while (!waitpid(pid, &status, WNOHANG) && !exit_now);
if (exit_now)
exit(EXIT_SUCCESS);
switch (WEXITSTATUS(status)) {
case EXIT_LOCKFILE:
LOG_ERROR("Failed to create lockfile");
LOG_ERROR("Process already running?");
break;
case EXIT_KERNEL_TFR_SOCKET:
LOG_ERROR("Unable to create netlink socket");
break;
case EXIT_KERNEL_TFR_BIND:
LOG_ERROR("Unable to bind to netlink socket");
break;
case EXIT_KERNEL_TFR_SETSOCKOPT:
LOG_ERROR("Unable to setsockopt on netlink socket");
break;
case EXIT_CLUSTER_CKPT_INIT:
LOG_ERROR("Unable to initialize checkpoint service");
LOG_ERROR("Has the cluster infrastructure been started?");
break;
case EXIT_FAILURE:
LOG_ERROR("Failed to start: Generic error");
break;
default:
LOG_ERROR("Failed to start: Unknown error");
break;
}
exit(EXIT_FAILURE);
}
setsid();
chdir("/");
umask(0);
close(0); close(1); close(2);
open("/dev/null", O_RDONLY); /* reopen stdin */
open("/dev/null", O_WRONLY); /* reopen stdout */
open("/dev/null", O_WRONLY); /* reopen stderr */
LOG_OPEN("clogd", LOG_PID, LOG_DAEMON);
if (create_lockfile("/var/run/clogd.pid"))
exit(EXIT_LOCKFILE);
signal(SIGINT, &sig_handler);
signal(SIGQUIT, &sig_handler);
signal(SIGTERM, &sig_handler);
signal(SIGHUP, &sig_handler);
signal(SIGPIPE, SIG_IGN);
signal(SIGUSR1, &sig_handler);
signal(SIGUSR2, &sig_handler);
sigemptyset(&signal_mask);
signal_received = 0;
}
/*
* init_all
*
* Initialize modules. Exit on failure.
*/
static void init_all(void)
{
int r;
if ((r = init_local()) ||
(r = init_cluster())) {
exit(r);
}
}
/*
* cleanup_all
*
* Clean up before exiting
*/
static void cleanup_all(void)
{
cleanup_local();
cleanup_cluster();
}
static void set_priority(void)
{
struct sched_param sched_param;
int res;
res = sched_get_priority_max(SCHED_RR);
if (res != -1) {
sched_param.sched_priority = res;
res = sched_setscheduler(0, SCHED_RR, &sched_param);
}
if (res == -1)
LOG_ERROR("Unable to set SCHED_RR priority.");
}

1551
daemons/clogd/cluster.c Normal file

File diff suppressed because it is too large Load Diff

13
daemons/clogd/cluster.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __CLUSTER_LOG_CLUSTER_DOT_H__
#define __CLUSTER_LOG_CLUSTER_DOT_H__
int init_cluster(void);
void cleanup_cluster(void);
void cluster_debug(void);
int create_cluster_cpg(char *str);
int destroy_cluster_cpg(char *str);
int cluster_send(struct clog_tfr *tfr);
#endif /* __CLUSTER_LOG_CLUSTER_DOT_H__ */

40
daemons/clogd/common.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef __CLUSTER_LOG_COMMON_DOT_H__
#define __CLUSTER_LOG_COMMON_DOT_H__
/*
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
*/
#define EXIT_LOCKFILE 2
#define EXIT_KERNEL_TFR_SOCKET 3 /* Failed netlink socket create */
#define EXIT_KERNEL_TFR_BIND 4
#define EXIT_KERNEL_TFR_SETSOCKOPT 5
#define EXIT_CLUSTER_CKPT_INIT 6 /* Failed to init checkpoint */
#define EXIT_QUEUE_NOMEM 7
/* Located in dm-clog-tfr.h
#define RQ_TYPE(x) \
((x) == DM_CLOG_CTR) ? "DM_CLOG_CTR" : \
((x) == DM_CLOG_DTR) ? "DM_CLOG_DTR" : \
((x) == DM_CLOG_PRESUSPEND) ? "DM_CLOG_PRESUSPEND" : \
((x) == DM_CLOG_POSTSUSPEND) ? "DM_CLOG_POSTSUSPEND" : \
((x) == DM_CLOG_RESUME) ? "DM_CLOG_RESUME" : \
((x) == DM_CLOG_GET_REGION_SIZE) ? "DM_CLOG_GET_REGION_SIZE" : \
((x) == DM_CLOG_IS_CLEAN) ? "DM_CLOG_IS_CLEAN" : \
((x) == DM_CLOG_IN_SYNC) ? "DM_CLOG_IN_SYNC" : \
((x) == DM_CLOG_FLUSH) ? "DM_CLOG_FLUSH" : \
((x) == DM_CLOG_MARK_REGION) ? "DM_CLOG_MARK_REGION" : \
((x) == DM_CLOG_CLEAR_REGION) ? "DM_CLOG_CLEAR_REGION" : \
((x) == DM_CLOG_GET_RESYNC_WORK) ? "DM_CLOG_GET_RESYNC_WORK" : \
((x) == DM_CLOG_SET_REGION_SYNC) ? "DM_CLOG_SET_REGION_SYNC" : \
((x) == DM_CLOG_GET_SYNC_COUNT) ? "DM_CLOG_GET_SYNC_COUNT" : \
((x) == DM_CLOG_STATUS_INFO) ? "DM_CLOG_STATUS_INFO" : \
((x) == DM_CLOG_STATUS_TABLE) ? "DM_CLOG_STATUS_TABLE" : \
NULL
*/
#endif /* __CLUSTER_LOG_COMMON_DOT_H__ */

1788
daemons/clogd/functions.c Normal file

File diff suppressed because it is too large Load Diff

20
daemons/clogd/functions.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef __CLOG_FUNCTIONS_DOT_H__
#define __CLOG_FUNCTIONS_DOT_H__
#include <linux/dm-clog-tfr.h>
#define LOG_RESUMED 1
#define LOG_SUSPENDED 2
int local_resume(struct clog_tfr *tfr);
int cluster_postsuspend(char *);
int do_request(struct clog_tfr *tfr, int server);
int push_state(const char *uuid, const char *which, char **buf);
int pull_state(const char *uuid, const char *which, char *buf, int size);
int log_get_state(struct clog_tfr *tfr);
int log_status(void);
void log_debug(void);
#endif /* __CLOG_FUNCTIONS_DOT_H__ */

138
daemons/clogd/link_mon.c Normal file
View File

@ -0,0 +1,138 @@
#include <stdlib.h>
#include <errno.h>
#include <poll.h>
#include "logging.h"
struct link_callback {
int fd;
char *name;
void *data;
int (*callback)(void *data);
struct link_callback *next;
};
static int used_pfds = 0;
static int free_pfds = 0;
static struct pollfd *pfds = NULL;
static struct link_callback *callbacks = NULL;
int links_register(int fd, char *name, int (*callback)(void *data), void *data)
{
int i;
struct link_callback *lc;
for (i = 0; i < used_pfds; i++) {
if (fd == pfds[i].fd) {
LOG_ERROR("links_register: Duplicate file descriptor");
return -EINVAL;
}
}
lc = malloc(sizeof(*lc));
if (!lc)
return -ENOMEM;
lc->fd = fd;
lc->name = name;
lc->data = data;
lc->callback = callback;
if (!free_pfds) {
struct pollfd *tmp;
tmp = realloc(pfds, sizeof(struct pollfd) * ((used_pfds*2) + 1));
if (!tmp) {
free(lc);
return -ENOMEM;
}
pfds = tmp;
free_pfds = used_pfds + 1;
}
free_pfds--;
pfds[used_pfds].fd = fd;
pfds[used_pfds].events = POLLIN;
pfds[used_pfds].revents = 0;
used_pfds++;
lc->next = callbacks;
callbacks = lc;
LOG_DBG("Adding %s/%d", lc->name, lc->fd);
LOG_DBG(" used_pfds = %d, free_pfds = %d",
used_pfds, free_pfds);
return 0;
}
int links_unregister(int fd)
{
int i;
struct link_callback *p, *c;
for (i = 0; i < used_pfds; i++)
if (fd == pfds[i].fd) {
/* entire struct is copied (overwritten) */
pfds[i] = pfds[used_pfds - 1];
used_pfds--;
free_pfds++;
}
for (p = NULL, c = callbacks; c; p = c, c = c->next)
if (fd == c->fd) {
LOG_DBG("Freeing up %s/%d", c->name, c->fd);
LOG_DBG(" used_pfds = %d, free_pfds = %d",
used_pfds, free_pfds);
if (p)
p->next = c->next;
else
callbacks = c->next;
free(c);
break;
}
return 0;
}
int links_monitor(void)
{
int i, r;
for (i = 0; i < used_pfds; i++) {
pfds[i].revents = 0;
}
r = poll(pfds, used_pfds, -1);
if (r <= 0)
return r;
r = 0;
/* FIXME: handle POLLHUP */
for (i = 0; i < used_pfds; i++)
if (pfds[i].revents & POLLIN) {
LOG_DBG("Data ready on %d", pfds[i].fd);
/* FIXME: Add this back return 1;*/
r++;
}
return r;
}
int links_issue_callbacks(void)
{
int i;
struct link_callback *lc;
for (i = 0; i < used_pfds; i++)
if (pfds[i].revents & POLLIN)
for (lc = callbacks; lc; lc = lc->next)
if (pfds[i].fd == lc->fd) {
LOG_DBG("Issuing callback on %s/%d",
lc->name, lc->fd);
lc->callback(lc->data);
break;
}
return 0;
}

9
daemons/clogd/link_mon.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __LINK_MON_DOT_H__
#define __LINK_MON_DOT_H__
int links_register(int fd, char *name, int (*callback)(void *data), void *data);
int links_unregister(int fd);
int links_monitor(void);
int links_issue_callbacks(void);
#endif /* __LINK_MON_DOT_H__ */

471
daemons/clogd/list.h Normal file
View File

@ -0,0 +1,471 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
/*
* 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 = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* 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;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
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;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_empty_careful - tests whether a list is
* empty _and_ checks that no other CPU might be
* in the process of still modifying either member
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*
* @head: the list to test.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}
static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;
first->prev = head;
head->next = first;
last->next = at;
at->prev = last;
}
/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}
/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
/**
* __list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*
* This variant differs from list_for_each() in that it's the
* simplest possible list iteration code, no prefetching is done.
* Use this for code that knows the list to be very short (empty
* or 1 entry) most of the time.
*/
#define __list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
pos = pos->prev)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member); \
prefetch(pos->member.prev), &pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))
/**
* list_prepare_entry - prepare a pos entry for use as a start point in
* list_for_each_entry_continue
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_struct within the struct.
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))
/**
* list_for_each_entry_continue - iterate over list of given type
* continuing after existing point
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
static inline void hlist_del_init(struct hlist_node *n)
{
if (n->pprev) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}
static inline void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)
{
next->next = n->next;
n->next = next;
next->pprev = &n->next;
if(next->next)
next->next->pprev = &next->next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)
/**
* hlist_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(tpos, pos, member) \
for (pos = (pos)->next; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_from - iterate over a hlist continuing from existing point
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(tpos, pos, member) \
for (; pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
#endif

379
daemons/clogd/local.c Normal file
View File

@ -0,0 +1,379 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <linux/connector.h>
#include <linux/netlink.h>
#include "linux/dm-clog-tfr.h"
#include "functions.h"
#include "cluster.h"
#include "common.h"
#include "logging.h"
#include "link_mon.h"
#include "local.h"
static int cn_fd; /* Connector (netlink) socket fd */
static char recv_buf[2048];
/* FIXME: merge this function with kernel_send_helper */
static int kernel_ack(uint32_t seq, int error)
{
int r;
unsigned char buf[sizeof(struct nlmsghdr) + sizeof(struct cn_msg)];
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
struct cn_msg *msg = NLMSG_DATA(nlh);
if (error < 0) {
LOG_ERROR("Programmer error: error codes must be positive");
return -EINVAL;
}
memset(buf, 0, sizeof(buf));
nlh->nlmsg_seq = 0;
nlh->nlmsg_pid = getpid();
nlh->nlmsg_type = NLMSG_DONE;
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg));
nlh->nlmsg_flags = 0;
msg->len = 0;
msg->id.idx = 0x4;
msg->id.val = 0x1;
msg->seq = seq;
msg->ack = error;
r = send(cn_fd, nlh, NLMSG_LENGTH(sizeof(struct cn_msg)), 0);
/* FIXME: do better error processing */
if (r <= 0)
return -EBADE;
return 0;
}
/*
* kernel_recv
* @tfr: the newly allocated request from kernel
*
* Read requests from the kernel and allocate space for the new request.
* If there is no request from the kernel, *tfr is NULL.
*
* This function is not thread safe due to returned stack pointer. In fact,
* the returned pointer must not be in-use when this function is called again.
*
* Returns: 0 on success, -EXXX on error
*/
static int kernel_recv(struct clog_tfr **tfr)
{
int r = 0;
int len;
struct cn_msg *msg;
*tfr = NULL;
memset(recv_buf, 0, sizeof(recv_buf));
len = recv(cn_fd, recv_buf, sizeof(recv_buf), 0);
if (len < 0) {
LOG_ERROR("Failed to recv message from kernel");
r = -errno;
goto fail;
}
switch (((struct nlmsghdr *)recv_buf)->nlmsg_type) {
case NLMSG_ERROR:
LOG_ERROR("Unable to recv message from kernel: NLMSG_ERROR");
r = -EBADE;
goto fail;
case NLMSG_DONE:
msg = (struct cn_msg *)NLMSG_DATA((struct nlmsghdr *)recv_buf);
len -= sizeof(struct nlmsghdr);
if (len < sizeof(struct cn_msg)) {
LOG_ERROR("Incomplete request from kernel received");
r = -EBADE;
goto fail;
}
if (msg->len > DM_CLOG_TFR_SIZE) {
LOG_ERROR("Not enough space to receive kernel request (%d/%d)",
msg->len, DM_CLOG_TFR_SIZE);
r = -EBADE;
goto fail;
}
if (!msg->len)
LOG_ERROR("Zero length message received");
len -= sizeof(struct cn_msg);
if (len < msg->len)
LOG_ERROR("len = %d, msg->len = %d", len, msg->len);
msg->data[msg->len] = '\0'; /* Cleaner way to ensure this? */
*tfr = (struct clog_tfr *)msg->data;
if (!(*tfr)->request_type) {
LOG_DBG("Bad transmission, requesting resend [%u]", msg->seq);
r = -EAGAIN;
if (kernel_ack(msg->seq, EAGAIN)) {
LOG_ERROR("Failed to NACK kernel transmission [%u]",
msg->seq);
r = -EBADE;
}
}
break;
default:
LOG_ERROR("Unknown nlmsg_type");
r = -EBADE;
}
fail:
if (r)
*tfr = NULL;
return (r == -EAGAIN) ? 0 : r;
}
static int kernel_send_helper(void *data, int out_size)
{
int r;
struct nlmsghdr *nlh;
struct cn_msg *msg;
unsigned char buf[2048];
memset(buf, 0, sizeof(buf));
nlh = (struct nlmsghdr *)buf;
nlh->nlmsg_seq = 0; /* FIXME: Is this used? */
nlh->nlmsg_pid = getpid();
nlh->nlmsg_type = NLMSG_DONE;
nlh->nlmsg_len = NLMSG_LENGTH(out_size + sizeof(struct cn_msg));
nlh->nlmsg_flags = 0;
msg = NLMSG_DATA(nlh);
memcpy(msg->data, data, out_size);
msg->len = out_size;
msg->id.idx = 0x4;
msg->id.val = 0x1;
msg->seq = 0;
r = send(cn_fd, nlh, NLMSG_LENGTH(out_size + sizeof(struct cn_msg)), 0);
/* FIXME: do better error processing */
if (r <= 0)
return -EBADE;
return 0;
}
/*
* do_local_work
*
* Any processing errors are placed in the 'tfr'
* structure to be reported back to the kernel.
* It may be pointless for this function to
* return an int.
*
* Returns: 0 on success, -EXXX on failure
*/
static int do_local_work(void *data)
{
int r;
struct clog_tfr *tfr = NULL;
r = kernel_recv(&tfr);
if (r)
return r;
if (!tfr)
return 0;
LOG_DBG("[%s] Request from kernel received: [%s/%u]",
SHORT_UUID(tfr->uuid), RQ_TYPE(tfr->request_type),
tfr->seq);
switch (tfr->request_type) {
case DM_CLOG_CTR:
case DM_CLOG_DTR:
case DM_CLOG_IN_SYNC:
case DM_CLOG_GET_SYNC_COUNT:
case DM_CLOG_STATUS_INFO:
case DM_CLOG_STATUS_TABLE:
case DM_CLOG_PRESUSPEND:
/* We do not specify ourselves as server here */
r = do_request(tfr, 0);
if (r)
LOG_DBG("Returning failed request to kernel [%s]",
RQ_TYPE(tfr->request_type));
r = kernel_send(tfr);
if (r)
LOG_ERROR("Failed to respond to kernel [%s]",
RQ_TYPE(tfr->request_type));
break;
case DM_CLOG_RESUME:
/*
* Resume is a special case that requires a local
* component to join the CPG, and a cluster component
* to handle the request.
*/
r = local_resume(tfr);
if (r) {
LOG_DBG("Returning failed request to kernel [%s]",
RQ_TYPE(tfr->request_type));
r = kernel_send(tfr);
if (r)
LOG_ERROR("Failed to respond to kernel [%s]",
RQ_TYPE(tfr->request_type));
break;
}
/* ELSE, fall through */
case DM_CLOG_IS_CLEAN:
case DM_CLOG_FLUSH:
case DM_CLOG_MARK_REGION:
case DM_CLOG_GET_RESYNC_WORK:
case DM_CLOG_SET_REGION_SYNC:
case DM_CLOG_IS_REMOTE_RECOVERING:
case DM_CLOG_POSTSUSPEND:
r = cluster_send(tfr);
if (r) {
tfr->data_size = 0;
tfr->error = r;
kernel_send(tfr);
}
break;
case DM_CLOG_CLEAR_REGION:
r = kernel_ack(tfr->seq, 0);
r = cluster_send(tfr);
if (r) {
/*
* FIXME: store error for delivery on flush
* This would allow us to optimize MARK_REGION
* too.
*/
}
break;
case DM_CLOG_GET_REGION_SIZE:
default:
LOG_ERROR("Invalid log request received, ignoring.");
return 0;
}
if (r && !tfr->error)
tfr->error = r;
return r;
}
/*
* kernel_send
* @tfr: result to pass back to kernel
*
* This function returns the tfr structure
* (containing the results) to the kernel.
* It then frees the structure.
*
* WARNING: should the structure be freed if
* there is an error? I vote 'yes'. If the
* kernel doesn't get the response, it should
* resend the request.
*
* Returns: 0 on success, -EXXX on failure
*/
int kernel_send(struct clog_tfr *tfr)
{
int r;
int size;
if (!tfr)
return -EINVAL;
size = sizeof(struct clog_tfr) + tfr->data_size;
if (!tfr->data_size && !tfr->error) {
/* An ACK is all that is needed */
/* FIXME: add ACK code */
} else if (size > DM_CLOG_TFR_SIZE) {
/*
* If we gotten here, we've already overrun
* our allotted space somewhere.
*
* We must do something, because the kernel
* is waiting for a response.
*/
LOG_ERROR("Not enough space to respond to server");
tfr->error = -ENOSPC;
size = sizeof(struct clog_tfr);
}
r = kernel_send_helper(tfr, size);
if (r)
LOG_ERROR("Failed to send msg to kernel.");
return r;
}
/*
* init_local
*
* Initialize kernel communication socket (netlink)
*
* Returns: 0 on success, values from common.h on failure
*/
int init_local(void)
{
int r = 0;
int opt;
struct sockaddr_nl addr;
cn_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (cn_fd < 0)
return EXIT_KERNEL_TFR_SOCKET;
/* memset to fix valgrind complaint */
memset(&addr, 0, sizeof(struct sockaddr_nl));
addr.nl_family = AF_NETLINK;
addr.nl_groups = 0x4;
addr.nl_pid = 0;
r = bind(cn_fd, (struct sockaddr *) &addr, sizeof(addr));
if (r < 0) {
close(cn_fd);
return EXIT_KERNEL_TFR_BIND;
}
opt = addr.nl_groups;
r = setsockopt(cn_fd, 270, NETLINK_ADD_MEMBERSHIP, &opt, sizeof(opt));
if (r) {
close(cn_fd);
return EXIT_KERNEL_TFR_SETSOCKOPT;
}
/*
r = fcntl(cn_fd, F_SETFL, FNDELAY);
*/
links_register(cn_fd, "local", do_local_work, NULL);
return 0;
}
/*
* cleanup_local
*
* Clean up before exiting
*/
void cleanup_local(void)
{
links_unregister(cn_fd);
close(cn_fd);
}

9
daemons/clogd/local.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __CLUSTER_LOG_LOCAL_DOT_H__
#define __CLUSTER_LOG_LOCAL_DOT_H__
int init_local(void);
void cleanup_local(void);
int kernel_send(struct clog_tfr *tfr);
#endif /* __CLUSTER_LOG_LOCAL_DOT_H__ */

25
daemons/clogd/logging.c Normal file
View File

@ -0,0 +1,25 @@
#include <syslog.h>
int log_tabbing = 0;
int log_is_open = 0;
/*
* Variables for various conditional logging
*/
#ifdef MEMB
int log_membership_change = 1;
#else
int log_membership_change = 0;
#endif
#ifdef CKPT
int log_checkpoint = 1;
#else
int log_checkpoint = 0;
#endif
#ifdef RESEND
int log_resend_requests = 1;
#else
int log_resend_requests = 0;
#endif

81
daemons/clogd/logging.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef __CLUSTER_LOG_LOGGING_DOT_H__
#define __CLUSTER_LOG_LOGGING_DOT_H__
#include <stdio.h>
#include <syslog.h>
#if (BITS_PER_LONG == 64)
#define PRIu64 "lu"
#define PRId64 "ld"
#define PRIo64 "lo"
#define PRIx64 "lx"
#define PRIX64 "lX"
#define SCNu64 "lu"
#define SCNd64 "ld"
#define SCNo64 "lo"
#define SCNx64 "lx"
#define SCNX64 "lX"
#else
#define PRIu64 "Lu"
#define PRId64 "Ld"
#define PRIo64 "Lo"
#define PRIx64 "Lx"
#define PRIX64 "LX"
#define SCNu64 "Lu"
#define SCNd64 "Ld"
#define SCNo64 "Lo"
#define SCNx64 "Lx"
#define SCNX64 "LX"
#endif
/* SHORT_UUID - print last 8 chars of a string */
#define SHORT_UUID(x) (strlen(x) > 8) ? ((x) + (strlen(x) - 8)) : (x)
extern int log_tabbing;
extern int log_is_open;
extern int log_membership_change;
extern int log_checkpoint;
extern int log_resend_requests;
#define LOG_OPEN(ident, option, facility) do { \
openlog(ident, option, facility); \
log_is_open = 1; \
} while (0)
#define LOG_CLOSE(void) do { \
log_is_open = 0; \
closelog(); \
} while (0)
#define LOG_OUTPUT(level, f, arg...) do { \
int __i; \
char __buffer[16]; \
FILE *fp = (level > LOG_NOTICE) ? stderr : stdout; \
if (log_is_open) { \
for (__i = 0; (__i < log_tabbing) && (__i < 15); __i++) \
__buffer[__i] = '\t'; \
__buffer[__i] = '\0'; \
syslog(level, "%s" f "\n", __buffer, ## arg); \
} else { \
for (__i = 0; __i < log_tabbing; __i++) \
fprintf(fp, "\t"); \
fprintf(fp, f "\n", ## arg); \
} \
} while (0)
#ifdef DEBUG
#define LOG_DBG(f, arg...) LOG_OUTPUT(LOG_DEBUG, f, ## arg)
#else /* DEBUG */
#define LOG_DBG(f, arg...)
#endif /* DEBUG */
#define LOG_COND(__X, f, arg...) do {\
if (__X) { \
LOG_OUTPUT(LOG_NOTICE, f, ## arg); \
} \
} while (0)
#define LOG_PRINT(f, arg...) LOG_OUTPUT(LOG_NOTICE, f, ## arg)
#define LOG_ERROR(f, arg...) LOG_OUTPUT(LOG_ERR, f, ## arg)
#endif /* __CLUSTER_LOG_LOGGING_DOT_H__ */