bitkeeper revision 1.1662.1.15 (42a5968eiZE_DjdIFPjxvzLw6ACvCQ)

Add xenstore daemon and library.
Makefile:
  Add xenstore subdirectory.
  Remove xs_stress on clean.
Many files:
  new file
ignore:
  Update ignore list for xenstore.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (authored)
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
This commit is contained in:
cl349@firebug.cl.cam.ac.uk committed 2005-06-07 12:43:58 +00:00
1 parent 636a81e970
commit 29c9e570b1
40 files changed
+9230 -1

No files matched your search

+37
View File
@@ -996,6 +996,43 @@
4292540couq-V0TPwyQ6bspNEWNcvw tools/xcutils/Makefile
42925407VysDb9O06OK_RUzTZxfLoA tools/xcutils/xc_restore.c
42936745WTLYamYsmXm_JGJ72JX-_Q tools/xcutils/xc_save.c
42a57d97mxMTlPnxBKep6R4ViI5rjg tools/xenstore/.gdbinit
42a57d97ZEoHuhMAFTuBMlLzA9v_ng tools/xenstore/Makefile
42a57d97ccA4uY-RxONvIH0P8U0gqg tools/xenstore/TODO
42a57d972RzmyLgsoH9b8qqk-UjcCA tools/xenstore/fake_libxc.c
42a57d97IjoPvbIVc4BUzwoKyM0VSw tools/xenstore/list.h
42a57d97fKgtf0HQLiQkAkVsOvuSyA tools/xenstore/talloc.c
42a57d98U3p0XP6xzCybTuaVQscUdw tools/xenstore/talloc.h
42a57d98LFN6Mug-uR4xgAxCE7lwUg tools/xenstore/talloc_guide.txt
42a57d98S69vKJYwO_WUjoFQZ6KzQg tools/xenstore/testsuite/01simple.sh
42a57d98BHcFpZz_fXHweylUEUU97Q tools/xenstore/testsuite/02directory.sh
42a57d98ua4Xeb6pmtbFNTAI833dyw tools/xenstore/testsuite/03write.sh
42a57d98nbuCUsVT0RJj1zA1JyMDsw tools/xenstore/testsuite/04rm.sh
42a57d98_ULKHP3_uX1PK2nPMTzWSQ tools/xenstore/testsuite/05filepermissions.sh
42a57d98YGCLyTDSGmoyFqRqQUlagQ tools/xenstore/testsuite/06dirpermissions.sh
42a57d98fdO519YyATk4_Zwr1STNfQ tools/xenstore/testsuite/07watch.sh
42a57d98zZUtvirUMjmHxFphJjmO7Q tools/xenstore/testsuite/08transaction.sh
42a57d98sn9RbpBgHRv1D99Kt7LwYA tools/xenstore/testsuite/09domain.sh
42a57d98tSuoFCHnnM2GgENXJrRQmw tools/xenstore/testsuite/test.sh
42a57d98zxDP2Ti7dTznGROi66rUGw tools/xenstore/utils.c
42a57d98SDvOYCEjmCjwHSk6390GLA tools/xenstore/utils.h
42a57d98hFKbOY9D0mCE4H4NDoKr1w tools/xenstore/xenstored.h
42a57d981KFHLmJ0CjKkn1_gZhYvdw tools/xenstore/xenstored_core.c
42a57d98bcgE13vYaFxGTusmWbrFDA tools/xenstore/xenstored_core.h
42a57d98cD9wOFyRYfaEP0QgtqL1Xw tools/xenstore/xenstored_domain.c
42a57d98noLWvXU8ePbcqvvmu4p2Gw tools/xenstore/xenstored_domain.h
42a57d98kxHaQ1ApS7RpqmFoEnDmbg tools/xenstore/xenstored_test.h
42a57d981c9P3aFkWtxWEIRUapt_FQ tools/xenstore/xenstored_transaction.c
42a57d99pVo__10bbckp_b_rm6i59A tools/xenstore/xenstored_transaction.h
42a57d99izTIjWfG-IjQAPqYlDWJNg tools/xenstore/xenstored_watch.c
42a57d99-zLxBjzC7rfj_perV-orUg tools/xenstore/xenstored_watch.h
42a57d99BnkhISKgCCRcUqhteyuxCw tools/xenstore/xs.c
42a57d99FyiYSz9AkKKROrRydnA-gQ tools/xenstore/xs.h
42a57d99SrtsJCDUlKyRPf3EX86A1Q tools/xenstore/xs_lib.c
42a57d99L2pYeMFyjQ_4Rnb17xTSMg tools/xenstore/xs_lib.h
42a57d99Kl6Ba8oCHv2fggl7QN9QZA tools/xenstore/xs_random.c
42a57d99SHYR1lQOD0shuErPDg9NKQ tools/xenstore/xs_stress.c
42a57d996aBawpkQNOWkNWXD6LrhPg tools/xenstore/xs_test.c
403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
40a107afN60pFdURgBv9KwEzgRl5mQ tools/xentrace/formats
420d52d2_znVbT4JAPIU36vQOme83g tools/xentrace/xenctx.c
+6 -1
View File
@@ -128,8 +128,13 @@ tools/xcs/xcs
tools/xcs/xcsdump
tools/xcutils/xc_restore
tools/xcutils/xc_save
tools/xenstore/testsuite/tmp/*
tools/xenstore/xen
tools/xenstore/xenstored_test
tools/xenstore/xs_random
tools/xenstore/xs_stress
tools/xenstore/xs_test
tools/xentrace/xentrace
tools/xfrd/xfrd
xen/BLOG
xen/TAGS
xen/arch/x86/asm-offsets.s
+1
View File
@@ -9,6 +9,7 @@ SUBDIRS += xentrace
SUBDIRS += python
SUBDIRS += xcs
SUBDIRS += xcutils
SUBDIRS += xenstore
SUBDIRS += pygrub
.PHONY: all install clean check check_clean ioemu eioemuinstall ioemuclean
+4
View File
@@ -0,0 +1,4 @@
set environment XENSTORED_RUNDIR=testsuite/tmp
set environment XENSTORED_ROOTDIR=testsuite/tmp
handle SIGUSR1 noprint nostop
handle SIGPIPE noprint nostop
+97
View File
@@ -0,0 +1,97 @@
XEN_ROOT=../..
# This does something wrong to TARGET_ARCH.
#include $(XEN_ROOT)/tools/Rules.mk
LIBDIR = lib
XEN_LIBXC = $(XEN_ROOT)/tools/libxc
INSTALL = install
INSTALL_DATA = $(INSTALL) -m0644
INSTALL_PROG = $(INSTALL) -m0755
INSTALL_DIR = $(INSTALL) -d -m0755
PROFILE=#-pg
BASECFLAGS=-Wall -W -g
# Make gcc generate dependencies.
BASECFLAGS += -Wp,-MD,.$(@F).d
PROG_DEP = .*.d
#BASECFLAGS+= -O3 $(PROFILE)
#BASECFLAGS+= -I$(XEN_ROOT)/tools
BASECFLAGS+= -I$(XEN_ROOT)/tools/libxc
BASECFLAGS+= -I$(XEN_ROOT)/xen/include/public
BASECFLAGS+= -I.
CFLAGS+=$(BASECFLAGS)
LDFLAGS=$(PROFILE) -L$(XEN_LIBXC)
TESTDIR=`pwd`/testsuite/tmp
TESTFLAGS=-DTESTING
TESTENV=XENSTORED_ROOTDIR=$(TESTDIR) XENSTORED_RUNDIR=$(TESTDIR)
all: xen xenstored libxenstore.a
testcode: xen xs_test xenstored_test xs_random
xen:
ln -sf $(XEN_ROOT)/xen/include/public $@
xenstored: xenstored_core.o xenstored_watch.o xenstored_domain.o xenstored_transaction.o xs_lib.o talloc.o utils.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -lxc -o $@
xenstored_test: xenstored_core_test.o xenstored_watch_test.o xenstored_domain_test.o xenstored_transaction_test.o xs_lib.o talloc_test.o fake_libxc.o utils.o
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@
xs_test: xs_test.o xs_lib.o utils.o
xs_random: xs_random.o xs_test_lib.o xs_lib.o talloc.o utils.o
xs_stress: xs_stress.o xs_test_lib.o xs_lib.o talloc.o utils.o
xs_test.o xs_stress.o xenstored_core_test.o xenstored_watch_test.o xenstored_transaction_test.o xenstored_domain_test.o xs_random.o xs_test_lib.o talloc_test.o fake_libxc.o: CFLAGS=$(BASECFLAGS) $(TESTFLAGS)
xenstored_%_test.o: xenstored_%.c
$(COMPILE.c) -o $@ $<
xs_test_lib.o: xs.c
$(COMPILE.c) -o $@ $<
talloc_test.o: talloc.c
$(COMPILE.c) -o $@ $<
libxenstore.a: libxenstore.a(xs.o) libxenstore.a(xs_lib.o)
clean: testsuite-clean
rm -f *.o *.a xs_test xenstored xenstored_test xs_random xs_stress xen
-$(RM) $(PROG_DEP)
check: testsuite-run randomcheck stresstest
testsuite-run: xen xenstored_test xs_test
$(TESTENV) testsuite/test.sh
testsuite-clean:
rm -rf $(TESTDIR)
# Make this visible so they can see repeat tests without --fast if they
# fail.
RANDSEED=$(shell date +%s)
randomcheck: xs_random xenstored_test
$(TESTENV) ./xs_random --simple --fast /tmp/xs_random 200000 $(RANDSEED)
$(TESTENV) ./xs_random --fast /tmp/xs_random 100000 $(RANDSEED)
$(TESTENV) ./xs_random --fail /tmp/xs_random 10000 $(RANDSEED)
stresstest: xs_stress xenstored_test
rm -rf $(TESTDIR)/store
export $(TESTENV); PID=`./xenstored_test --output-pid`; ./xs_stress 10000; ret=$$?; kill $$PID; exit $$ret
TAGS:
etags `find . -name '*.[ch]'`
tarball: clean
cd .. && tar -c -j -v -h -f xenstore.tar.bz2 xenstore/
install: xenstored libxenstore.a
$(INSTALL_DIR) -p $(DESTDIR)/var/run/xenstored
$(INSTALL_DIR) -p $(DESTDIR)/var/lib/xenstored
$(INSTALL_DIR) -p $(DESTDIR)/usr/sbin
$(INSTALL_PROG) xenstored $(DESTDIR)/usr/sbin
$(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)
$(INSTALL_DATA) libxenstore.a $(DESTDIR)/usr/$(LIBDIR)
-include $(PROG_DEP)
+7
View File
@@ -0,0 +1,7 @@
TODO in no particular order. Some of these will never be done. There
are omissions of important but necessary things. It is up to the
reader to fill in the blanks.
- Remove calls to system() from daemon
- Timeout failed watch responses
- Timeout blocking transactions
+119
View File
@@ -0,0 +1,119 @@
/*
Fake libxc which doesn't require hypervisor but talks to xs_test.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <assert.h>
#include <signal.h>
#include "utils.h"
#include "xenstored_core.h"
#include "xenstored_domain.h"
#include "xenstored_test.h"
static int sigfd;
static int xs_test_pid;
static u16 port;
/* The event channel maps to a signal, shared page to an mmapped file. */
int xc_evtchn_send(int xc_handle __attribute__((unused)), int local_port)
{
assert(local_port == port);
if (kill(xs_test_pid, SIGUSR2) != 0)
barf_perror("fake event channel failed");
return 0;
}
void *xc_map_foreign_range(int xc_handle, u32 dom __attribute__((unused)),
int size, int prot,
unsigned long mfn __attribute__((unused)))
{
void *ret;
ret = mmap(NULL, size, prot, MAP_SHARED, xc_handle, 0);
if (ret == MAP_FAILED)
return NULL;
/* xs_test tells us pid and port by putting it in buffer, we reply. */
xs_test_pid = *(int *)(ret + 32);
port = *(int *)(ret + 36);
*(int *)(ret + 32) = getpid();
return ret;
}
int xc_interface_open(void)
{
int fd;
char page[getpagesize()];
fd = open("/tmp/xcmap", O_RDWR|O_CREAT|O_TRUNC, 0600);
if (fd < 0)
return fd;
memset(page, 0, sizeof(page));
if (!write_all(fd, page, sizeof(page)))
barf_perror("Failed to write /tmp/xcmap page");
return fd;
}
int xc_interface_close(int xc_handle)
{
close(xc_handle);
return 0;
}
static void send_to_fd(int signo __attribute__((unused)))
{
int saved_errno = errno;
write(sigfd, &port, sizeof(port));
errno = saved_errno;
}
void fake_block_events(void)
{
signal(SIGUSR2, SIG_IGN);
}
void fake_ack_event(void)
{
signal(SIGUSR2, send_to_fd);
}
int fake_open_eventchn(void)
{
int fds[2];
if (pipe(fds) != 0)
return -1;
if (signal(SIGUSR2, send_to_fd) == SIG_ERR) {
int saved_errno = errno;
close(fds[0]);
close(fds[1]);
errno = saved_errno;
return -1;
}
sigfd = fds[1];
return fds[0];
}
+508
View File
@@ -0,0 +1,508 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
/* Taken from Linux kernel code, but de-kernelized for userspace. */
#include <stddef.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)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/*
* 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)
#define list_top(head, type, member) \
({ \
struct list_head *_head = (head); \
list_empty(_head) ? NULL : list_entry(_head->next, type, member); \
})
/*
* 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);
}
/*
* 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_rcu(struct list_head * new,
struct list_head * prev,
struct list_head * next)
{
new->next = next;
new->prev = prev;
next->prev = new;
prev->next = new;
}
/**
* list_add_rcu - add a new entry to rcu-protected list
* @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_rcu(struct list_head *new, struct list_head *head)
{
__list_add_rcu(new, head, head->next);
}
/**
* list_add_tail_rcu - add a new entry to rcu-protected list
* @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_rcu(struct list_head *new, struct list_head *head)
{
__list_add_rcu(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_rcu - deletes entry from list without re-initialization
* @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. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the list.
*/
static inline void list_del_rcu(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
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(struct list_head *head)
{
return head->next == head;
}
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);
}
}
/**
* 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; 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; 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); \
&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); \
&pos->member != (head); \
pos = list_entry(pos->member.prev, 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); \
&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(struct hlist_node *h)
{
return !h->pprev;
}
static __inline__ int hlist_empty(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;
}
/**
* hlist_del_rcu - deletes entry from hash list without re-initialization
* @entry: the element to delete from the hash list.
*
* Note: list_unhashed() on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the hash list.
*/
static inline void hlist_del_rcu(struct hlist_node *n)
{
__hlist_del(n);
n->pprev = LIST_POISON2;
}
static __inline__ void hlist_del_init(struct hlist_node *n)
{
if (n->pprev) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
#define hlist_del_rcu_init hlist_del_init
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;
}
static __inline__ void hlist_add_head_rcu(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
n->pprev = &h->first;
if (first)
first->pprev = &n->next;
h->first = n;
}
/* 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;
*(next->pprev) = n;
n->next = next;
}
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
/* Cannot easily do prefetch unfortunately */
#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos; pos = pos->next)
#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
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 && ({ 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 && ({ 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 && ({ 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
File diff suppressed because it is too large. Load diff
+134
View File
@@ -0,0 +1,134 @@
#ifndef _TALLOC_H_
#define _TALLOC_H_
/*
Unix SMB/CIFS implementation.
Samba temporary memory allocation functions
Copyright (C) Andrew Tridgell 2004-2005
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* this is only needed for compatibility with the old talloc */
typedef void TALLOC_CTX;
/*
this uses a little trick to allow __LINE__ to be stringified
*/
#define _STRING_LINE_(s) #s
#define _STRING_LINE2_(s) _STRING_LINE_(s)
#define __LINESTR__ _STRING_LINE2_(__LINE__)
#define __location__ __FILE__ ":" __LINESTR__
#ifndef TALLOC_DEPRECATED
#define TALLOC_DEPRECATED 0
#endif
/* useful macros for creating type checked pointers */
#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
#define malloc_p(type) (type *)malloc(sizeof(type))
#define malloc_array_p(type, count) (type *)realloc_array(NULL, sizeof(type), count)
#define realloc_p(p, type, count) (type *)realloc_array(p, sizeof(type), count)
#define data_blob(ptr, size) data_blob_named(ptr, size, "DATA_BLOB: "__location__)
#define data_blob_talloc(ctx, ptr, size) data_blob_talloc_named(ctx, ptr, size, "DATA_BLOB: "__location__)
#define data_blob_dup_talloc(ctx, blob) data_blob_talloc_named(ctx, (blob)->data, (blob)->length, "DATA_BLOB: "__location__)
#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
#if TALLOC_DEPRECATED
#define talloc_zero_p(ctx, type) talloc_zero(ctx, type)
#define talloc_p(ctx, type) talloc(ctx, type)
#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count)
#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count)
#define talloc_destroy(ctx) talloc_free(ctx)
#endif
#ifndef PRINTF_ATTRIBUTE
#if (__GNUC__ >= 3)
/** Use gcc attribute to check printf fns. a1 is the 1-based index of
* the parameter containing the format, and a2 the index of the first
* argument. Note that some gcc 2.x versions don't handle this
* properly **/
#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
#else
#define PRINTF_ATTRIBUTE(a1, a2)
#endif
#endif
/* The following definitions come from talloc.c */
void *_talloc(const void *context, size_t size);
void talloc_set_destructor(const void *ptr, int (*destructor)(void *));
void talloc_increase_ref_count(const void *ptr);
void *talloc_reference(const void *context, const void *ptr);
int talloc_unlink(const void *context, void *ptr);
void talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
void talloc_set_name_const(const void *ptr, const char *name);
void *talloc_named(const void *context, size_t size,
const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
void *talloc_named_const(const void *context, size_t size, const char *name);
const char *talloc_get_name(const void *ptr);
void *talloc_check_name(const void *ptr, const char *name);
void talloc_report_depth(const void *ptr, FILE *f, int depth);
void *talloc_parent(const void *ptr);
void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
int talloc_free(void *ptr);
void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
void *talloc_steal(const void *new_ctx, const void *ptr);
off_t talloc_total_size(const void *ptr);
off_t talloc_total_blocks(const void *ptr);
void talloc_report_full(const void *ptr, FILE *f);
void talloc_report(const void *ptr, FILE *f);
void talloc_enable_null_tracking(void);
void talloc_enable_leak_report(void);
void talloc_enable_leak_report_full(void);
void *_talloc_zero(const void *ctx, size_t size, const char *name);
void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
char *talloc_strdup(const void *t, const char *p);
char *talloc_strndup(const void *t, const char *p, size_t n);
char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
char *talloc_asprintf_append(char *s,
const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
void *talloc_autofree_context(void);
size_t talloc_get_size(const void *ctx);
typedef int talloc_fail_handler(void *);
talloc_fail_handler *talloc_set_fail_handler(talloc_fail_handler *, void *);
#endif
+569
View File
@@ -0,0 +1,569 @@
Using talloc in Samba4
----------------------
Andrew Tridgell
September 2004
The most current version of this document is available at
http://samba.org/ftp/unpacked/samba4/source/lib/talloc/talloc_guide.txt
If you are used to talloc from Samba3 then please read this carefully,
as talloc has changed a lot.
The new talloc is a hierarchical, reference counted memory pool system
with destructors. Quite a mounthful really, but not too bad once you
get used to it.
Perhaps the biggest change from Samba3 is that there is no distinction
between a "talloc context" and a "talloc pointer". Any pointer
returned from talloc() is itself a valid talloc context. This means
you can do this:
struct foo *X = talloc(mem_ctx, struct foo);
X->name = talloc_strdup(X, "foo");
and the pointer X->name would be a "child" of the talloc context "X"
which is itself a child of mem_ctx. So if you do talloc_free(mem_ctx)
then it is all destroyed, whereas if you do talloc_free(X) then just X
and X->name are destroyed, and if you do talloc_free(X->name) then
just the name element of X is destroyed.
If you think about this, then what this effectively gives you is an
n-ary tree, where you can free any part of the tree with
talloc_free().
If you find this confusing, then I suggest you run the testsuite to
watch talloc in action. You may also like to add your own tests to
testsuite.c to clarify how some particular situation is handled.
Performance
-----------
All the additional features of talloc() over malloc() do come at a
price. We have a simple performance test in Samba4 that measures
talloc() versus malloc() performance, and it seems that talloc() is
about 10% slower than malloc() on my x86 Debian Linux box. For Samba,
the great reduction in code complexity that we get by using talloc
makes this worthwhile, especially as the total overhead of
talloc/malloc in Samba is already quite small.
talloc API
----------
The following is a complete guide to the talloc API. Read it all at
least twice.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
(type *)talloc(const void *context, type);
The talloc() macro is the core of the talloc library. It takes a
memory context and a type, and returns a pointer to a new area of
memory of the given type.
The returned pointer is itself a talloc context, so you can use it as
the context argument to more calls to talloc if you wish.
The returned pointer is a "child" of the supplied context. This means
that if you talloc_free() the context then the new child disappears as
well. Alternatively you can free just the child.
The context argument to talloc() can be NULL, in which case a new top
level context is created.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_size(const void *context, size_t size);
The function talloc_size() should be used when you don't have a
convenient type to pass to talloc(). Unlike talloc(), it is not type
safe (as it returns a void *), so you are on your own for type checking.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
int talloc_free(void *ptr);
The talloc_free() function frees a piece of talloc memory, and all its
children. You can call talloc_free() on any pointer returned by
talloc().
The return value of talloc_free() indicates success or failure, with 0
returned for success and -1 for failure. The only possible failure
condition is if the pointer had a destructor attached to it and the
destructor returned -1. See talloc_set_destructor() for details on
destructors.
If this pointer has an additional parent when talloc_free() is called
then the memory is not actually released, but instead the most
recently established parent is destroyed. See talloc_reference() for
details on establishing additional parents.
For more control on which parent is removed, see talloc_unlink()
talloc_free() operates recursively on its children.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
int talloc_free_children(void *ptr);
The talloc_free_children() walks along the list of all children of a
talloc context and talloc_free()s only the children, not the context
itself.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_reference(const void *context, const void *ptr);
The talloc_reference() function makes "context" an additional parent
of "ptr".
The return value of talloc_reference() is always the original pointer
"ptr", unless talloc ran out of memory in creating the reference in
which case it will return NULL (each additional reference consumes
around 48 bytes of memory on intel x86 platforms).
If "ptr" is NULL, then the function is a no-op, and simply returns NULL.
After creating a reference you can free it in one of the following
ways:
- you can talloc_free() any parent of the original pointer. That
will reduce the number of parents of this pointer by 1, and will
cause this pointer to be freed if it runs out of parents.
- you can talloc_free() the pointer itself. That will destroy the
most recently established parent to the pointer and leave the
pointer as a child of its current parent.
For more control on which parent to remove, see talloc_unlink()
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
int talloc_unlink(const void *context, const void *ptr);
The talloc_unlink() function removes a specific parent from ptr. The
context passed must either be a context used in talloc_reference()
with this pointer, or must be a direct parent of ptr.
Note that if the parent has already been removed using talloc_free()
then this function will fail and will return -1. Likewise, if "ptr"
is NULL, then the function will make no modifications and return -1.
Usually you can just use talloc_free() instead of talloc_unlink(), but
sometimes it is useful to have the additional control on which parent
is removed.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_set_destructor(const void *ptr, int (*destructor)(void *));
The function talloc_set_destructor() sets the "destructor" for the
pointer "ptr". A destructor is a function that is called when the
memory used by a pointer is about to be released. The destructor
receives the pointer as an argument, and should return 0 for success
and -1 for failure.
The destructor can do anything it wants to, including freeing other
pieces of memory. A common use for destructors is to clean up
operating system resources (such as open file descriptors) contained
in the structure the destructor is placed on.
You can only place one destructor on a pointer. If you need more than
one destructor then you can create a zero-length child of the pointer
and place an additional destructor on that.
To remove a destructor call talloc_set_destructor() with NULL for the
destructor.
If your destructor attempts to talloc_free() the pointer that it is
the destructor for then talloc_free() will return -1 and the free will
be ignored. This would be a pointless operation anyway, as the
destructor is only called when the memory is just about to go away.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_increase_ref_count(const void *ptr);
The talloc_increase_ref_count(ptr) function is exactly equivalent to:
talloc_reference(NULL, ptr);
You can use either syntax, depending on which you think is clearer in
your code.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_set_name(const void *ptr, const char *fmt, ...);
Each talloc pointer has a "name". The name is used principally for
debugging purposes, although it is also possible to set and get the
name on a pointer in as a way of "marking" pointers in your code.
The main use for names on pointer is for "talloc reports". See
talloc_report() and talloc_report_full() for details. Also see
talloc_enable_leak_report() and talloc_enable_leak_report_full().
The talloc_set_name() function allocates memory as a child of the
pointer. It is logically equivalent to:
talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...));
Note that multiple calls to talloc_set_name() will allocate more
memory without releasing the name. All of the memory is released when
the ptr is freed using talloc_free().
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_set_name_const(const void *ptr, const char *name);
The function talloc_set_name_const() is just like talloc_set_name(),
but it takes a string constant, and is much faster. It is extensively
used by the "auto naming" macros, such as talloc_p().
This function does not allocate any memory. It just copies the
supplied pointer into the internal representation of the talloc
ptr. This means you must not pass a name pointer to memory that will
disappear before the ptr is freed with talloc_free().
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_named(const void *context, size_t size, const char *fmt, ...);
The talloc_named() function creates a named talloc pointer. It is
equivalent to:
ptr = talloc_size(context, size);
talloc_set_name(ptr, fmt, ....);
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_named_const(const void *context, size_t size, const char *name);
This is equivalent to:
ptr = talloc_size(context, size);
talloc_set_name_const(ptr, name);
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
const char *talloc_get_name(const void *ptr);
This returns the current name for the given talloc pointer. See
talloc_set_name() for details.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_init(const char *fmt, ...);
This function creates a zero length named talloc context as a top
level context. It is equivalent to:
talloc_named(NULL, 0, fmt, ...);
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_new(void *ctx);
This is a utility macro that creates a new memory context hanging
off an exiting context, automatically naming it "talloc_new: __location__"
where __location__ is the source line it is called from. It is
particularly useful for creating a new temporary working context.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
(type *)talloc_realloc(const void *context, void *ptr, type, count);
The talloc_realloc() macro changes the size of a talloc
pointer. The "count" argument is the number of elements of type "type"
that you want the resulting pointer to hold.
talloc_realloc() has the following equivalences:
talloc_realloc(context, NULL, type, 1) ==> talloc(context, type);
talloc_realloc(context, NULL, type, N) ==> talloc_array(context, type, N);
talloc_realloc(context, ptr, type, 0) ==> talloc_free(ptr);
The "context" argument is only used if "ptr" is not NULL, otherwise it
is ignored.
talloc_realloc() returns the new pointer, or NULL on failure. The call
will fail either due to a lack of memory, or because the pointer has
more than one parent (see talloc_reference()).
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_realloc_size(const void *context, void *ptr, size_t size);
the talloc_realloc_size() function is useful when the type is not
known so the typesafe talloc_realloc() cannot be used.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_steal(const void *new_ctx, const void *ptr);
The talloc_steal() function changes the parent context of a talloc
pointer. It is typically used when the context that the pointer is
currently a child of is going to be freed and you wish to keep the
memory for a longer time.
The talloc_steal() function returns the pointer that you pass it. It
does not have any failure modes.
NOTE: It is possible to produce loops in the parent/child relationship
if you are not careful with talloc_steal(). No guarantees are provided
as to your sanity or the safety of your data if you do this.
talloc_steal (new_ctx, NULL) will return NULL with no sideeffects.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
off_t talloc_total_size(const void *ptr);
The talloc_total_size() function returns the total size in bytes used
by this pointer and all child pointers. Mostly useful for debugging.
Passing NULL is allowed, but it will only give a meaningful result if
talloc_enable_leak_report() or talloc_enable_leak_report_full() has
been called.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
off_t talloc_total_blocks(const void *ptr);
The talloc_total_blocks() function returns the total memory block
count used by this pointer and all child pointers. Mostly useful for
debugging.
Passing NULL is allowed, but it will only give a meaningful result if
talloc_enable_leak_report() or talloc_enable_leak_report_full() has
been called.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_report(const void *ptr, FILE *f);
The talloc_report() function prints a summary report of all memory
used by ptr. One line of report is printed for each immediate child of
ptr, showing the total memory and number of blocks used by that child.
You can pass NULL for the pointer, in which case a report is printed
for the top level memory context, but only if
talloc_enable_leak_report() or talloc_enable_leak_report_full() has
been called.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_report_full(const void *ptr, FILE *f);
This provides a more detailed report than talloc_report(). It will
recursively print the ensire tree of memory referenced by the
pointer. References in the tree are shown by giving the name of the
pointer that is referenced.
You can pass NULL for the pointer, in which case a report is printed
for the top level memory context, but only if
talloc_enable_leak_report() or talloc_enable_leak_report_full() has
been called.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_enable_leak_report(void);
This enables calling of talloc_report(NULL, stderr) when the program
exits. In Samba4 this is enabled by using the --leak-report command
line option.
For it to be useful, this function must be called before any other
talloc function as it establishes a "null context" that acts as the
top of the tree. If you don't call this function first then passing
NULL to talloc_report() or talloc_report_full() won't give you the
full tree printout.
Here is a typical talloc report:
talloc report on 'null_context' (total 267 bytes in 15 blocks)
libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks
libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks
iconv(UTF8,CP850) contains 42 bytes in 2 blocks
libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks
iconv(CP850,UTF8) contains 42 bytes in 2 blocks
iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks
iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_enable_leak_report_full(void);
This enables calling of talloc_report_full(NULL, stderr) when the
program exits. In Samba4 this is enabled by using the
--leak-report-full command line option.
For it to be useful, this function must be called before any other
talloc function as it establishes a "null context" that acts as the
top of the tree. If you don't call this function first then passing
NULL to talloc_report() or talloc_report_full() won't give you the
full tree printout.
Here is a typical full report:
full talloc report on 'root' (total 18 bytes in 8 blocks)
p1 contains 18 bytes in 7 blocks (ref 0)
r1 contains 13 bytes in 2 blocks (ref 0)
reference to: p2
p2 contains 1 bytes in 1 blocks (ref 1)
x3 contains 1 bytes in 1 blocks (ref 0)
x2 contains 1 bytes in 1 blocks (ref 0)
x1 contains 1 bytes in 1 blocks (ref 0)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void talloc_enable_null_tracking(void);
This enables tracking of the NULL memory context without enabling leak
reporting on exit. Useful for when you want to do your own leak
reporting call via talloc_report_null_full();
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
(type *)talloc_zero(const void *ctx, type);
The talloc_zero() macro is equivalent to:
ptr = talloc(ctx, type);
if (ptr) memset(ptr, 0, sizeof(type));
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_zero_size(const void *ctx, size_t size)
The talloc_zero_size() function is useful when you don't have a known type
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_memdup(const void *ctx, const void *p, size_t size);
The talloc_memdup() function is equivalent to:
ptr = talloc_size(ctx, size);
if (ptr) memcpy(ptr, p, size);
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
char *talloc_strdup(const void *ctx, const char *p);
The talloc_strdup() function is equivalent to:
ptr = talloc_size(ctx, strlen(p)+1);
if (ptr) memcpy(ptr, p, strlen(p)+1);
This functions sets the name of the new pointer to the passed
string. This is equivalent to:
talloc_set_name_const(ptr, ptr)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
char *talloc_strndup(const void *t, const char *p, size_t n);
The talloc_strndup() function is the talloc equivalent of the C
library function strndup()
This functions sets the name of the new pointer to the passed
string. This is equivalent to:
talloc_set_name_const(ptr, ptr)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
char *talloc_vasprintf(const void *t, const char *fmt, va_list ap);
The talloc_vasprintf() function is the talloc equivalent of the C
library function vasprintf()
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
char *talloc_asprintf(const void *t, const char *fmt, ...);
The talloc_asprintf() function is the talloc equivalent of the C
library function asprintf()
This functions sets the name of the new pointer to the passed
string. This is equivalent to:
talloc_set_name_const(ptr, ptr)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
char *talloc_asprintf_append(char *s, const char *fmt, ...);
The talloc_asprintf_append() function appends the given formatted
string to the given string.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
(type *)talloc_array(const void *ctx, type, uint_t count);
The talloc_array() macro is equivalent to:
(type *)talloc_size(ctx, sizeof(type) * count);
except that it provides integer overflow protection for the multiply,
returning NULL if the multiply overflows.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_array_size(const void *ctx, size_t size, uint_t count);
The talloc_array_size() function is useful when the type is not
known. It operates in the same way as talloc_array(), but takes a size
instead of a type.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_realloc_fn(const void *ctx, void *ptr, size_t size);
This is a non-macro version of talloc_realloc(), which is useful
as libraries sometimes want a ralloc function pointer. A realloc()
implementation encapsulates the functionality of malloc(), free() and
realloc() in one call, which is why it is useful to be able to pass
around a single function pointer.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_autofree_context(void);
This is a handy utility function that returns a talloc context
which will be automatically freed on program exit. This can be used
to reduce the noise in memory leak reports.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void *talloc_check_name(const void *ptr, const char *name);
This function checks if a pointer has the specified name. If it does
then the pointer is returned. It it doesn't then NULL is returned.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
(type *)talloc_get_type(const void *ptr, type);
This macro allows you to do type checking on talloc pointers. It is
particularly useful for void* private pointers. It is equivalent to
this:
(type *)talloc_check_name(ptr, #type)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
talloc_set_type(const void *ptr, type);
This macro allows you to force the name of a pointer to be a
particular type. This can be used in conjunction with
talloc_get_type() to do type checking on void* pointers.
It is equivalent to this:
talloc_set_name_const(ptr, #type)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
talloc_get_size(const void *ctx);
This function lets you know the amount of memory alloced so far by
this context. It does NOT account for subcontext memory.
This can be used to calculate the size of an array.
+4
View File
@@ -0,0 +1,4 @@
#! /bin/sh
# Create an entry, read it.
[ "`echo -e 'write /test create contents\nread /test' | ./xs_test 2>&1`" = "contents" ]
+31
View File
@@ -0,0 +1,31 @@
#! /bin/sh
# Root directory has nothing in it.
[ "`echo -e 'dir /' | ./xs_test 2>&1`" = "" ]
# Create a file.
[ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
# Directory shows it.
[ "`echo -e 'dir /' | ./xs_test 2>&1`" = "test" ]
# Make a new directory.
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
# Check it's there.
DIR="`echo -e 'dir /' | ./xs_test 2>&1`"
[ "$DIR" = "test
dir" ] || [ "$DIR" = "dir
test" ]
# Check it's empty.
[ "`echo -e 'dir /dir' | ./xs_test 2>&1`" = "" ]
# Create a file, check it exists.
[ "`echo -e 'write /dir/test2 create contents2' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'dir /dir' | ./xs_test 2>&1`" = "test2" ]
[ "`echo -e 'read /dir/test2' | ./xs_test 2>&1`" = "contents2" ]
# Creating dir over the top should fail.
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "FATAL: mkdir: File exists" ]
[ "`echo -e 'mkdir /dir/test2' | ./xs_test 2>&1`" = "FATAL: mkdir: File exists" ]
+17
View File
@@ -0,0 +1,17 @@
#! /bin/sh
# Write without create fails.
[ "`echo -e 'write /test none contents' | ./xs_test 2>&1`" = "FATAL: write: No such file or directory" ]
# Exclusive write succeeds
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents" ]
# Exclusive write fails to overwrite.
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "FATAL: write: File exists" ]
# Non-exclusive overwrite succeeds.
[ "`echo -e 'write /test none contents2' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents2" ]
[ "`echo -e 'write /test create contents3' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents3" ]
+18
View File
@@ -0,0 +1,18 @@
#! /bin/sh
# Remove non-existant fails.
[ "`echo -e 'rm /test' | ./xs_test 2>&1`" = "FATAL: rm: No such file or directory" ]
[ "`echo -e 'rm /dir/test' | ./xs_test 2>&1`" = "FATAL: rm: No such file or directory" ]
# Create file and remove it
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'rm /test' | ./xs_test 2>&1`" = "" ]
# Create directory and remove it.
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'rm /dir' | ./xs_test 2>&1`" = "" ]
# Create directory, create file, remove all.
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'write /dir/test excl contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'rm /dir' | ./xs_test 2>&1`" = "" ]
@@ -0,0 +1,49 @@
#! /bin/sh
# Fail to get perms on non-existent file.
[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: No such file or directory" ]
[ "`echo -e 'getperm /dir/test' | ./xs_test 2>&1`" = "FATAL: getperm: No such file or directory" ]
# Create file: we own it, noone has access.
[ "`echo -e 'write /test excl contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "0 NONE" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
# Grant everyone read access to file.
[ "`echo -e 'setperm /test 0 READ' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents" ]
[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
# Grant everyone write access to file.
[ "`echo -e 'setperm /test 0 WRITE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 1\nwrite /test none contents2' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents2" ]
# Grant everyone both read and write access.
[ "`echo -e 'setperm /test 0 READ/WRITE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "0 READ/WRITE" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents2" ]
[ "`echo -e 'setid 1\nwrite /test none contents3' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents3" ]
# Change so that user 1 owns it, noone else can do anything.
[ "`echo -e 'setperm /test 1 NONE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /test' | ./xs_test 2>&1`" = "1 NONE" ]
[ "`echo -e 'setid 1\nread /test' | ./xs_test 2>&1`" = "contents3" ]
[ "`echo -e 'setid 1\nwrite /test none contents4' | ./xs_test 2>&1`" = "" ]
# User 2 can do nothing.
[ "`echo -e 'setid 2\nsetperm /test 2 NONE' | ./xs_test 2>&1`" = "FATAL: setperm: Permission denied" ]
[ "`echo -e 'setid 2\ngetperm /test' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
[ "`echo -e 'setid 2\nread /test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /test none contents4' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
# Tools can always access things.
[ "`echo -e 'getperm /test' | ./xs_test 2>&1`" = "1 NONE" ]
[ "`echo -e 'read /test' | ./xs_test 2>&1`" = "contents4" ]
[ "`echo -e 'write /test none contents5' | ./xs_test 2>&1`" = "" ]
@@ -0,0 +1,61 @@
#! /bin/sh
# Root directory: owned by tool, everyone has read access.
[ "`echo -e 'getperm /' | ./xs_test 2>&1`" = "0 READ" ]
# Create directory: we own it, noone has access.
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "0 NONE" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ]
[ "`echo -e 'setid 1\nread /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
# Grant everyone read access to directoy.
[ "`echo -e 'setperm /dir 0 READ' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\nwrite /dir/test create contents2' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
# Grant everyone write access to directory.
[ "`echo -e 'setperm /dir 0 WRITE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ]
[ "`echo -e 'setid 1\nwrite /dir/test create contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'read /dir/test' | ./xs_test 2>&1`" = "contents" ]
# Grant everyone both read and write access.
[ "`echo -e 'setperm /dir 0 READ/WRITE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "0 READ/WRITE" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1`" = "test" ]
[ "`echo -e 'setid 1\nwrite /dir/test2 create contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\nread /dir/test2' | ./xs_test 2>&1`" = "contents" ]
# Change so that user 1 owns it, noone else can do anything.
[ "`echo -e 'setperm /dir 1 NONE' | ./xs_test 2>&1`" = "" ]
[ "`echo -e 'setid 1\ngetperm /dir' | ./xs_test 2>&1`" = "1 NONE" ]
[ "`echo -e 'setid 1\ndir /dir' | ./xs_test 2>&1 | sort`" = "test
test2" ]
[ "`echo -e 'setid 1\nwrite /dir/test3 create contents' | ./xs_test 2>&1`" = "" ]
# User 2 can do nothing. Can't even tell if file exists.
[ "`echo -e 'setid 2\nsetperm /dir 2 NONE' | ./xs_test 2>&1`" = "FATAL: setperm: Permission denied" ]
[ "`echo -e 'setid 2\ngetperm /dir' | ./xs_test 2>&1`" = "FATAL: getperm: Permission denied" ]
[ "`echo -e 'setid 2\ndir /dir' | ./xs_test 2>&1`" = "FATAL: dir: Permission denied" ]
[ "`echo -e 'setid 2\nread /dir/test' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 2\nread /dir/test2' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 2\nread /dir/test3' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 2\nread /dir/test4' | ./xs_test 2>&1`" = "FATAL: read: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /dir/test none contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /dir/test create contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /dir/test excl contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /dir/test4 none contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /dir/test4 create contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
[ "`echo -e 'setid 2\nwrite /dir/test4 excl contents' | ./xs_test 2>&1`" = "FATAL: write: Permission denied" ]
# Tools can always access things.
[ "`echo -e 'getperm /dir' | ./xs_test 2>&1`" = "1 NONE" ]
[ "`echo -e 'dir /dir' | ./xs_test 2>&1 | sort`" = "test
test2
test3" ]
[ "`echo -e 'write /dir/test4 create contents' | ./xs_test 2>&1`" = "" ]
+32
View File
@@ -0,0 +1,32 @@
#! /bin/sh
# Watch something, write to it, check watch has fired.
[ "`echo -e 'write /test create contents' | ./xs_test 2>&1`" = "" ]
[ "`echo -e '1 watch /test 100\n2 write /test create contents2\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "1:/test" ]
# Check that reads don't set it off.
[ "`echo -e '1 watch /test 100\n2 read /test\n1 waitwatch' | ./xs_test 2>&1`" = "2:contents2
1:waitwatch timeout" ]
# mkdir, setperm and rm should (also /tests watching dirs)
[ "`echo -e 'mkdir /dir' | ./xs_test 2>&1`" = "" ]
[ "`echo -e '1 watch /dir 100\n2 mkdir /dir/newdir\n1 waitwatch\n1 ackwatch\n2 setperm /dir/newdir 0 READ\n1 waitwatch\n1 ackwatch\n2 rm /dir/newdir\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "1:/dir/newdir
1:/dir/newdir
1:/dir/newdir" ]
# ignore watches while doing commands, should work.
[ "`echo -e 'watch /dir 100\nwrite /dir/test create contents\nread /dir/test\nwaitwatch\nackwatch' | ./xs_test 2>&1`" = "contents
/dir/test" ]
# watch priority /test.
[ "`echo -e '1 watch /dir 1\n3 watch /dir 3\n2 watch /dir 2\nwrite /dir/test create contents\n3 waitwatch\n3 ackwatch\n2 waitwatch\n2 ackwatch\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "3:/dir/test
2:/dir/test
1:/dir/test" ]
# If one dies (without acking), the other should still get ack.
[ "`echo -e '1 watch /dir 0\n2 watch /dir 1\nwrite /dir/test create contents\n2 waitwatch\n2 close\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "2:/dir/test
1:/dir/test" ]
# If one dies (without reading at all), the other should still get ack.
[ "`echo -e '1 watch /dir 0\n2 watch /dir 1\nwrite /dir/test create contents\n2 close\n1 waitwatch\n1 ackwatch' | ./xs_test 2>&1`" = "1:/dir/test" ]
+54
View File
@@ -0,0 +1,54 @@
#! /bin/sh
# Test transactions.
# Simple transaction: create a file inside transaction.
[ "`echo -e '1 start /
1 write /entry1 create contents
2 dir /
1 dir /
1 commit
2 read /entry1' | ./xs_test`" = "1:entry1
2:contents" ]
echo rm /entry1 | ./xs_test
# Create a file and abort transaction.
[ "`echo -e '1 start /
1 write /entry1 create contents
2 dir /
1 dir /
1 abort
2 dir /' | ./xs_test`" = "1:entry1" ]
echo write /entry1 create contents | ./xs_test
# Delete in transaction, commit
[ "`echo -e '1 start /
1 rm /entry1
2 dir /
1 dir /
1 commit
2 dir /' | ./xs_test`" = "2:entry1" ]
# Delete in transaction, abort.
echo write /entry1 create contents | ./xs_test
[ "`echo -e '1 start /
1 rm /entry1
2 dir /
1 dir /
1 abort
2 dir /' | ./xs_test`" = "2:entry1
2:entry1" ]
# Transactions can take as long as the want...
[ "`echo -e 'start /
sleep 1
rm /entry1
commit
dir /' | ./xs_test`" = "" ]
# ... as long as noone is waiting.
[ "`echo -e '1 start /
2 mkdir /dir
1 mkdir /dir
1 dir /
1 commit' | ./xs_test 2>&1`" = "1:dir
FATAL: 1: commit: Connection timed out" ]
+15
View File
@@ -0,0 +1,15 @@
#! /bin/sh
# Test domain communication.
# Create a domain, write an entry.
[ "`echo -e 'introduce 1 100 7 /my/home
1 write /entry1 create contents
dir /' | ./xs_test 2>&1`" = "handle is 1
entry1" ]
# Release that domain.
[ "`echo -e 'release 1' | ./xs_test`" = "" ]
# Introduce and release by same connection.
[ "`echo -e 'introduce 1 100 7 /my/home
release 1' | ./xs_test 2>&1`" = "handle is 1" ]
+44
View File
@@ -0,0 +1,44 @@
#! /bin/sh
set -e
set -m
run_test()
{
rm -rf $XENSTORED_ROOTDIR
mkdir $XENSTORED_ROOTDIR
# Weird failures with this.
if type valgrind >/dev/null 2>&1; then
valgrind -q --logfile-fd=3 ./xenstored_test --output-pid --no-fork 3>testsuite/tmp/vgout > /tmp/pid &
while [ ! -s /tmp/pid ]; do sleep 0; done
PID=`cat /tmp/pid`
rm /tmp/pid
else
PID=`./xenstored_test --output-pid`
fi
if sh -e $2 $1; then
if [ -s testsuite/tmp/vgout ]; then
kill $PID
echo VALGRIND errors:
cat testsuite/tmp/vgout
return 1
fi
echo shutdown | ./xs_test
return 0
else
# In case daemon is wedged.
kill $PID
sleep 1
return 1
fi
}
for f in testsuite/[0-9]*.sh; do
if run_test $f; then
echo Test $f passed...
else
echo Test $f failed, running verbosely...
run_test $f -x
exit 1
fi
done
+143
View File
@@ -0,0 +1,143 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>
#include "utils.h"
void xprintf(const char *fmt, ...)
{
static FILE *out = NULL;
va_list args;
if (!out)
out = fopen("/dev/console", "w");
if (!out)
out = stderr;
va_start(args, fmt);
vfprintf(out, fmt, args);
va_end(args);
fflush(out);
}
void barf(const char *fmt, ...)
{
char *str;
va_list arglist;
xprintf("FATAL: ");
va_start(arglist, fmt);
vasprintf(&str, fmt, arglist);
va_end(arglist);
xprintf("%s\n", str);
free(str);
exit(1);
}
void barf_perror(const char *fmt, ...)
{
char *str;
int err = errno;
va_list arglist;
xprintf("FATAL: ");
va_start(arglist, fmt);
vasprintf(&str, fmt, arglist);
va_end(arglist);
xprintf("%s: %s\n", str, strerror(err));
free(str);
exit(1);
}
void *_realloc_array(void *ptr, size_t size, size_t num)
{
if (num >= SIZE_MAX/size)
return NULL;
return realloc_nofail(ptr, size * num);
}
void *realloc_nofail(void *ptr, size_t size)
{
ptr = realloc(ptr, size);
if (ptr)
return ptr;
barf("realloc of %zu failed", size);
}
void *malloc_nofail(size_t size)
{
void *ptr = malloc(size);
if (ptr)
return ptr;
barf("malloc of %zu failed", size);
}
/* Stevens. */
void daemonize(void)
{
pid_t pid;
/* Separate from our parent via fork, so init inherits us. */
if ((pid = fork()) < 0)
barf_perror("Failed to fork daemon");
if (pid != 0)
exit(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Session leader so ^C doesn't whack us. */
setsid();
/* Move off any mount points we might be in. */
chdir("/");
/* Discard our parent's old-fashioned umask prejudices. */
umask(0);
}
/* This version adds one byte (for nul term) */
void *grab_file(const char *filename, unsigned long *size)
{
unsigned int max = 16384;
int ret, fd;
void *buffer;
if (streq(filename, "-"))
fd = dup(STDIN_FILENO);
else
fd = open(filename, O_RDONLY, 0);
if (fd < 0)
return NULL;
buffer = malloc(max+1);
*size = 0;
while ((ret = read(fd, buffer + *size, max - *size)) > 0) {
*size += ret;
if (*size == max)
buffer = realloc(buffer, max *= 2 + 1);
}
if (ret < 0) {
free(buffer);
buffer = NULL;
} else
((char *)buffer)[*size] = '\0';
close(fd);
return buffer;
}
void release_file(void *data, unsigned long size __attribute__((unused)))
{
free(data);
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef _UTILS_H
#define _UTILS_H
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
/* Is A == B ? */
#define streq(a,b) (strcmp((a),(b)) == 0)
/* Does A start with B ? */
#define strstarts(a,b) (strncmp((a),(b),strlen(b)) == 0)
/* Does A end in B ? */
static inline bool strends(const char *a, const char *b)
{
if (strlen(a) < strlen(b))
return false;
return streq(a + strlen(a) - strlen(b), b);
}
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define ___stringify(x) #x
#define __stringify(x) ___stringify(x)
/* Convenient wrappers for malloc and realloc. Use them. */
#define new(type) ((type *)malloc_nofail(sizeof(type)))
#define new_array(type, num) realloc_array((type *)0, (num))
#define realloc_array(ptr, num) ((__typeof__(ptr))_realloc_array((ptr), sizeof((*ptr)), (num)))
void *malloc_nofail(size_t size);
void *realloc_nofail(void *ptr, size_t size);
void *_realloc_array(void *ptr, size_t size, size_t num);
void barf(const char *fmt, ...) __attribute__((noreturn));
void barf_perror(const char *fmt, ...) __attribute__((noreturn));
/* This version adds one byte (for nul term) */
void *grab_file(const char *filename, unsigned long *size);
void release_file(void *data, unsigned long size);
/* For writing daemons, based on Stevens. */
void daemonize(void);
/* Signal handling: returns fd to listen on. */
int signal_to_fd(int signal);
void close_signal(int fd);
void xprintf(const char *fmt, ...);
#define eprintf(_fmt, _args...) xprintf("[ERR] %s" _fmt, __FUNCTION__, ##_args)
#define iprintf(_fmt, _args...) xprintf("[INF] %s" _fmt, __FUNCTION__, ##_args)
#ifdef DEBUG
#define dprintf(_fmt, _args...) xprintf("[DBG] %s" _fmt, __FUNCTION__, ##_args)
#else
#define dprintf(_fmt, _args...) ((void)0)
#endif
#endif /* _UTILS_H */
+81
View File
@@ -0,0 +1,81 @@
/*
Simple prototyle Xen Store Daemon providing simple tree-like database.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#ifndef _XENSTORED_H
#define _XENSTORED_H
enum xsd_sockmsg_type
{
XS_DEBUG,
XS_SHUTDOWN,
XS_DIRECTORY,
XS_READ,
XS_GET_PERMS,
XS_WATCH,
XS_WATCH_ACK,
XS_UNWATCH,
XS_TRANSACTION_START,
XS_TRANSACTION_END,
XS_OP_READ_ONLY = XS_TRANSACTION_END,
XS_INTRODUCE,
XS_RELEASE,
XS_GETDOMAINPATH,
XS_WRITE,
XS_MKDIR,
XS_RM,
XS_SET_PERMS,
XS_WATCH_EVENT,
XS_ERROR,
};
#define XS_WRITE_NONE "NONE"
#define XS_WRITE_CREATE "CREATE"
#define XS_WRITE_CREATE_EXCL "CREATE|EXCL"
/* We hand errors as strings, for portability. */
struct xsd_errors
{
int errnum;
const char *errstring;
};
#define XSD_ERROR(x) { x, #x }
static struct xsd_errors xsd_errors[] __attribute__((unused)) = {
XSD_ERROR(EINVAL),
XSD_ERROR(EACCES),
XSD_ERROR(EEXIST),
XSD_ERROR(EISDIR),
XSD_ERROR(ENOENT),
XSD_ERROR(ENOMEM),
XSD_ERROR(ENOSPC),
XSD_ERROR(EIO),
XSD_ERROR(ENOTEMPTY),
XSD_ERROR(ENOSYS),
XSD_ERROR(EROFS),
XSD_ERROR(EBUSY),
XSD_ERROR(ETIMEDOUT),
XSD_ERROR(EISCONN),
};
struct xsd_sockmsg
{
u32 type;
u32 len; /* Length of data following this. */
/* Generally followed by nul-terminated string(s). */
};
#endif /* _XENSTORED_H */
File diff suppressed because it is too large. Load diff
+123
View File
@@ -0,0 +1,123 @@
/*
Internal interfaces for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#ifndef _XENSTORED_INTERNAL_H
#define _XENSTORED_INTERNAL_H
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include "xs_lib.h"
#include "xenstored.h"
#include "list.h"
struct buffered_data
{
/* Are we still doing the header? */
bool inhdr;
/* How far are we? */
unsigned int used;
union {
struct xsd_sockmsg msg;
char raw[sizeof(struct xsd_sockmsg)];
} hdr;
/* The actual data. */
char *buffer;
};
struct connection;
typedef int connwritefn_t(struct connection *, const void *, unsigned int);
typedef int connreadfn_t(struct connection *, void *, unsigned int);
struct connection
{
struct list_head list;
/* The file descriptor we came in on. */
int fd;
/* Who am I? 0 for socket connections. */
domid_t id;
/* Are we blocked waiting for a transaction to end? Contains node. */
char *blocked;
/* Our current event. If all used, we're waiting for ack. */
struct watch_event *event;
/* Buffered incoming data. */
struct buffered_data *in;
/* Buffered output data */
struct buffered_data *out;
/* If we had a watch fire outgoing when we needed to reply... */
struct buffered_data *waiting_reply;
/* My transaction, if any. */
struct transaction *transaction;
/* The domain I'm associated with, if any. */
struct domain *domain;
/* Methods for communicating over this connection: write can be NULL */
connwritefn_t *write;
connreadfn_t *read;
};
/* Return length of string (including nul) at this offset. */
unsigned int get_string(const struct buffered_data *data,
unsigned int offset);
/* Break input into vectors, return the number, fill in up to num of them. */
unsigned int get_strings(struct buffered_data *data,
char *vec[], unsigned int num);
/* Is child node a child or equal to parent node? */
bool is_child(const char *child, const char *parent);
/* Create a new buffer with lifetime of context. */
struct buffered_data *new_buffer(void *ctx);
bool send_reply(struct connection *conn, enum xsd_sockmsg_type type,
const void *data, unsigned int len);
/* Some routines (write, mkdir, etc) just need a non-error return */
bool send_ack(struct connection *conn, enum xsd_sockmsg_type type);
/* Send an error: error is usually "errno". */
bool send_error(struct connection *conn, int error);
/* Check permissions on this node. */
bool check_node_perms(struct connection *conn, const char *node,
enum xs_perm_type perm);
/* Path to this node outside transaction. */
char *node_dir_outside_transaction(const char *node);
/* Fail due to excessive corruption, capitalist pigdogs! */
void __attribute__((noreturn)) corrupt(struct connection *conn,
const char *fmt, ...);
struct connection *new_connection(connwritefn_t *write, connreadfn_t *read);
void handle_input(struct connection *conn);
void handle_output(struct connection *conn);
/* Convenient talloc-style destructor for paths. */
int destroy_path(void *path);
#endif /* _XENSTORED_INTERNAL_H */
+387
View File
@@ -0,0 +1,387 @@
/*
Domain communications for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <stdio.h>
#include <linux/ioctl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#define DEBUG
#include "utils.h"
#include "talloc.h"
#include "xenstored_core.h"
#include "xenstored_domain.h"
#include "xenstored_test.h"
static int *xc_handle;
static int eventchn_fd;
static unsigned int ringbuf_datasize;
struct domain
{
struct list_head list;
/* The id of this domain */
domid_t domid;
/* Event channel port */
u16 port;
/* Domain path in store. */
char *path;
/* Shared page. */
void *page;
/* Input and output ringbuffer heads. */
struct ringbuf_head *input, *output;
/* The connection associated with this. */
struct connection *conn;
};
static LIST_HEAD(domains);
void domain_set_conn(struct domain *domain, struct connection *conn)
{
domain->conn = conn;
}
struct ringbuf_head
{
u32 write; /* Next place to write to */
u32 read; /* Next place to read from */
u8 flags;
char buf[0];
} __attribute__((packed));
#define EVENTCHN_BIND _IO('E', 2)
#define EVENTCHN_UNBIND _IO('E', 3)
/* FIXME: Mark connection as broken (close it?) when this happens. */
static bool check_buffer(const struct ringbuf_head *h)
{
return (h->write < ringbuf_datasize && h->read < ringbuf_datasize);
}
/* We can't fill last byte: would look like empty buffer. */
static void *get_output_chunk(const struct ringbuf_head *h,
void *buf, u32 *len)
{
u32 read_mark;
if (h->read == 0)
read_mark = ringbuf_datasize - 1;
else
read_mark = h->read - 1;
/* Here to the end of buffer, unless they haven't read some out. */
*len = ringbuf_datasize - h->write;
if (read_mark >= h->write)
*len = read_mark - h->write;
return buf + h->write;
}
static const void *get_input_chunk(const struct ringbuf_head *h,
const void *buf, u32 *len)
{
/* Here to the end of buffer, unless they haven't written some. */
*len = ringbuf_datasize - h->read;
if (h->write >= h->read)
*len = h->write - h->read;
return buf + h->read;
}
static void update_output_chunk(struct ringbuf_head *h, u32 len)
{
h->write += len;
if (h->write == ringbuf_datasize)
h->write = 0;
}
static void update_input_chunk(struct ringbuf_head *h, u32 len)
{
h->read += len;
if (h->read == ringbuf_datasize)
h->read = 0;
}
static bool buffer_has_input(const struct ringbuf_head *h)
{
u32 len;
get_input_chunk(h, NULL, &len);
return (len != 0);
}
static bool buffer_has_output_room(const struct ringbuf_head *h)
{
u32 len;
get_output_chunk(h, NULL, &len);
return (len != 0);
}
static int writechn(struct connection *conn, const void *data, unsigned int len)
{
u32 avail;
void *dest;
struct ringbuf_head h;
/* Must read head once, and before anything else, and verified. */
h = *conn->domain->output;
mb();
if (!check_buffer(&h)) {
errno = EIO;
return -1;
}
dest = get_output_chunk(&h, conn->domain->output->buf, &avail);
if (avail < len)
len = avail;
memcpy(dest, data, len);
mb();
update_output_chunk(conn->domain->output, len);
/* FIXME: Probably not neccessary. */
mb();
xc_evtchn_send(*xc_handle, conn->domain->port);
return len;
}
static int readchn(struct connection *conn, void *data, unsigned int len)
{
u32 avail;
const void *src;
struct ringbuf_head h;
bool was_full;
/* Must read head once, and before anything else, and verified. */
h = *conn->domain->input;
mb();
if (!check_buffer(&h)) {
errno = EIO;
return -1;
}
src = get_input_chunk(&h, conn->domain->input->buf, &avail);
if (avail < len)
len = avail;
was_full = !buffer_has_output_room(&h);
memcpy(data, src, len);
mb();
update_input_chunk(conn->domain->input, len);
/* FIXME: Probably not neccessary. */
mb();
/* If it was full, tell them we've taken some. */
if (was_full)
xc_evtchn_send(*xc_handle, conn->domain->port);
return len;
}
static int destroy_domain(void *_domain)
{
struct domain *domain = _domain;
list_del(&domain->list);
if (domain->port &&
(ioctl(eventchn_fd, EVENTCHN_UNBIND, domain->port) != 0))
eprintf("> Unbinding port %i failed!\n", domain->port);
if(domain->page)
munmap(domain->page, getpagesize());
return 0;
}
static struct domain *find_domain(u16 port)
{
struct domain *i;
list_for_each_entry(i, &domains, list) {
if (i->port == port)
return i;
}
return NULL;
}
void handle_event(int event_fd)
{
u16 port;
struct domain *domain;
if (read(event_fd, &port, sizeof(port)) != sizeof(port))
barf_perror("Failed to read from event fd");
/* We have to handle *all* the data available before we ack:
* careful that handle_input/handle_output can destroy conn.
*/
while ((domain = find_domain(port)) != NULL) {
if (!domain->conn->blocked && buffer_has_input(domain->input))
handle_input(domain->conn);
else if (domain->conn->out
&& buffer_has_output_room(domain->output))
handle_output(domain->conn);
else
break;
}
#ifndef TESTING
if (write(event_fd, &port, sizeof(port)) != sizeof(port))
barf_perror("Failed to write to event fd");
#endif
}
/* domid, mfn, evtchn, path */
bool do_introduce(struct connection *conn, struct buffered_data *in)
{
struct domain *domain;
char *vec[4];
if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
/* Hang domain off "in" until we're finished. */
domain = talloc(in, struct domain);
domain->domid = atoi(vec[0]);
domain->port = atoi(vec[2]);
domain->path = talloc_strdup(domain, vec[3]);
talloc_set_destructor(domain, destroy_domain);
if (!domain->port || !domain->domid)
return send_error(conn, EINVAL);
domain->page = xc_map_foreign_range(*xc_handle, domain->domid,
getpagesize(),
PROT_READ|PROT_WRITE,
atol(vec[1]));
if (!domain->page)
return send_error(conn, errno);
/* One in each half of page. */
domain->input = domain->page;
domain->output = domain->page + getpagesize()/2;
/* Tell kernel we're interested in this event. */
if (ioctl(eventchn_fd, EVENTCHN_BIND, domain->port) != 0)
return send_error(conn, errno);
domain->conn = new_connection(writechn, readchn);
domain->conn->domain = domain;
talloc_steal(domain->conn, domain);
list_add(&domain->list, &domains);
return send_ack(conn, XS_INTRODUCE);
}
static struct domain *find_domain_by_domid(domid_t domid)
{
struct domain *i;
list_for_each_entry(i, &domains, list) {
if (i->domid == domid)
return i;
}
return NULL;
}
/* domid */
bool do_release(struct connection *conn, const char *domid_str)
{
struct domain *domain;
domid_t domid;
if (!domid_str)
return send_error(conn, EINVAL);
domid = atoi(domid_str);
if (!domid)
return send_error(conn, EINVAL);
domain = find_domain_by_domid(domid);
if (!domain)
return send_error(conn, ENOENT);
if (!domain->conn)
return send_error(conn, EINVAL);
talloc_free(domain->conn);
return send_ack(conn, XS_RELEASE);
}
bool do_get_domain_path(struct connection *conn, const char *domid_str)
{
struct domain *domain;
domid_t domid;
if (!domid_str)
return send_error(conn, EINVAL);
domid = atoi(domid_str);
if (domid == 0)
domain = conn->domain;
else
domain = find_domain_by_domid(domid);
if (!domain)
return send_error(conn, ENOENT);
return send_reply(conn, XS_GETDOMAINPATH, domain->path,
strlen(domain->path) + 1);
}
static int close_xc_handle(void *_handle)
{
xc_interface_close(*(int *)_handle);
return 0;
}
/* Returns the event channel handle. */
int domain_init(void)
{
/* The size of the ringbuffer: half a page minus head structure. */
ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
xc_handle = talloc(talloc_autofree_context(), int);
if (!xc_handle)
barf_perror("Failed to allocate domain handle");
*xc_handle = xc_interface_open();
if (*xc_handle < 0)
barf_perror("Failed to open connection to hypervisor");
talloc_set_destructor(xc_handle, close_xc_handle);
#ifdef TESTING
eventchn_fd = fake_open_eventchn();
#else
eventchn_fd = open("/dev/xen/evtchn", O_RDWR);
#endif
if (eventchn_fd < 0)
barf_perror("Failed to open connection to hypervisor");
return eventchn_fd;
}
+38
View File
@@ -0,0 +1,38 @@
/*
Domain communications for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#ifndef _XENSTORED_DOMAIN_H
#define _XENSTORED_DOMAIN_H
void handle_event(int event_fd);
/* domid, mfn, eventchn, path */
bool do_introduce(struct connection *conn, struct buffered_data *in);
/* domid */
bool do_release(struct connection *conn, const char *domid_str);
/* domid */
bool do_get_domain_path(struct connection *conn, const char *domid_str);
/* Returns the event channel handle */
int domain_init(void);
void domain_set_conn(struct domain *domain, struct connection *conn);
#endif /* _XENSTORED_DOMAIN_H */
+37
View File
@@ -0,0 +1,37 @@
/*
Testing replcements for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#ifndef _XENSTORED_TEST_H
#define _XENSTORED_TEST_H
#ifdef TESTING
bool test_write_all(int fd, void *contents, unsigned int len);
#define write_all test_write_all
int test_mkdir(const char *dir, int perms);
#define mkdir test_mkdir
int fake_open_eventchn(void);
void fake_block_events(void);
void fake_ack_event(void);
#define ioctl(a,b,c) 0
#endif
#endif /* _XENSTORED_INTERNAL_H */
+284
View File
@@ -0,0 +1,284 @@
/*
Transaction code for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
#include <stdarg.h>
#include <stdlib.h>
#include "talloc.h"
#include "list.h"
#include "xenstored_transaction.h"
#include "xenstored_watch.h"
#include "xs_lib.h"
#include "utils.h"
#include "xenstored_test.h"
struct changed_node
{
/* The list within this transaction. */
struct list_head list;
/* The name of the node. */
char *node;
};
struct transaction
{
/* Global list of transactions. */
struct list_head list;
/* My owner (conn->transaction == me). */
struct connection *conn;
/* Subtree this transaction covers */
char *node;
/* Base for this transaction. */
char *divert;
/* List of changed nodes. */
struct list_head changes;
/* Someone's waiting: time limit. */
struct timeval timeout;
/* We've timed out. */
bool destined_to_fail;
};
static LIST_HEAD(transactions);
bool within_transaction(struct transaction *trans, const char *node)
{
if (!trans)
return true;
return is_child(node, trans->node);
}
/* You are on notice: this transaction is blocking someone. */
static void start_transaction_timeout(struct transaction *trans)
{
if (timerisset(&trans->timeout))
return;
/* One second timeout. */
gettimeofday(&trans->timeout, NULL);
trans->timeout.tv_sec += 1;
}
struct transaction *transaction_covering_node(const char *node)
{
struct transaction *i;
list_for_each_entry(i, &transactions, list) {
if (i->destined_to_fail)
continue;
if (is_child(i->node, node) || is_child(node, i->node))
return i;
}
return NULL;
}
bool transaction_block(struct connection *conn, const char *node)
{
struct transaction *trans;
/* Transactions don't overlap, so we can't be blocked by
* others if we're in one. */
if (conn->transaction)
return false;
trans = transaction_covering_node(node);
if (trans) {
start_transaction_timeout(trans);
conn->blocked = talloc_strdup(conn, node);
return true;
}
return false;
}
/* Callers get a change node (which can fail) and only commit after they've
* finished. This way they don't have to unwind eg. a write. */
void add_change_node(struct transaction *trans, const char *node)
{
struct changed_node *i;
if (!trans)
return;
list_for_each_entry(i, &trans->changes, list)
if (streq(i->node, node))
return;
i = talloc(trans, struct changed_node);
i->node = talloc_strdup(i, node);
INIT_LIST_HEAD(&i->list);
list_add_tail(&i->list, &trans->changes);
}
char *node_dir_inside_transaction(struct transaction *trans, const char *node)
{
return talloc_asprintf(node, "%s%s", trans->divert,
node + strlen(trans->node));
}
void shortest_transaction_timeout(struct timeval *tv)
{
struct transaction *i;
list_for_each_entry(i, &transactions, list) {
if (!timerisset(&i->timeout))
continue;
if (!timerisset(tv) || timercmp(&i->timeout, tv, <))
*tv = i->timeout;
}
}
void check_transaction_timeout(void)
{
struct transaction *i;
struct timeval now;
gettimeofday(&now, NULL);
list_for_each_entry(i, &transactions, list) {
if (!timerisset(&i->timeout))
continue;
if (timercmp(&i->timeout, &now, <))
i->destined_to_fail = true;
}
}
/* FIXME: Eliminate all uses of this */
static bool do_command(const char *cmd)
{
int ret;
ret = system(cmd);
if (ret == -1)
return false;
if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
errno = EIO;
return false;
}
return true;
}
static int destroy_transaction(void *_transaction)
{
struct transaction *trans = _transaction;
list_del(&trans->list);
return destroy_path(trans->divert);
}
bool do_transaction_start(struct connection *conn, const char *node)
{
struct transaction *transaction;
char *dir, *cmd;
if (conn->transaction)
return send_error(conn, EBUSY);
if (!check_node_perms(conn, node, XS_PERM_READ))
return send_error(conn, errno);
if (transaction_block(conn, node))
return true;
dir = node_dir_outside_transaction(node);
/* Attach transaction to node for autofree until it's complete */
transaction = talloc(node, struct transaction);
transaction->node = talloc_strdup(transaction, node);
transaction->divert = talloc_asprintf(transaction, "%s/%p/",
xs_daemon_transactions(),
transaction);
cmd = talloc_asprintf(node, "cp -a %s %s", dir, transaction->divert);
if (!do_command(cmd))
corrupt(conn, "Creating transaction %s", transaction->divert);
talloc_steal(conn, transaction);
INIT_LIST_HEAD(&transaction->changes);
transaction->conn = conn;
timerclear(&transaction->timeout);
transaction->destined_to_fail = false;
list_add_tail(&transaction->list, &transactions);
conn->transaction = transaction;
talloc_set_destructor(transaction, destroy_transaction);
return send_ack(transaction->conn, XS_TRANSACTION_START);
}
static bool commit_transaction(struct transaction *trans)
{
char *tmp, *dir;
struct changed_node *i;
/* Move: orig -> .old, repl -> orig. Cleanup deletes .old. */
dir = node_dir_outside_transaction(trans->node);
tmp = talloc_asprintf(trans, "%s.old", dir);
if (rename(dir, tmp) != 0)
return false;
if (rename(trans->divert, dir) != 0)
corrupt(trans->conn, "Failed rename %s to %s",
trans->divert, dir);
trans->divert = tmp;
/* Fire off the watches for everything that changed. */
list_for_each_entry(i, &trans->changes, list)
fire_watches(NULL, i->node);
return true;
}
bool do_transaction_end(struct connection *conn, const char *arg)
{
if (!arg || (!streq(arg, "T") && !streq(arg, "F")))
return send_error(conn, EINVAL);
if (!conn->transaction)
return send_error(conn, ENOENT);
if (streq(arg, "T")) {
if (conn->transaction->destined_to_fail) {
send_error(conn, ETIMEDOUT);
goto failed;
}
if (!commit_transaction(conn->transaction)) {
send_error(conn, errno);
goto failed;
}
}
talloc_free(conn->transaction);
conn->transaction = NULL;
return send_ack(conn, XS_TRANSACTION_END);
failed:
talloc_free(conn->transaction);
conn->transaction = NULL;
return false;
}
+50
View File
@@ -0,0 +1,50 @@
/*
Transaction code for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#ifndef _XENSTORED_TRANSACTION_H
#define _XENSTORED_TRANSACTION_H
#include "xenstored_core.h"
struct transaction;
bool do_transaction_start(struct connection *conn, const char *node);
bool do_transaction_end(struct connection *conn, const char *arg);
/* Is node covered by this transaction? */
bool within_transaction(struct transaction *trans, const char *node);
/* If a write op on this node blocked by another connections' transaction,
* mark conn, setup transaction timeout and return true.
*/
bool transaction_block(struct connection *conn, const char *node);
/* Return transaction which covers this node. */
struct transaction *transaction_covering_node(const char *node);
/* Return directory of node within transaction t. */
char *node_dir_inside_transaction(struct transaction *t, const char *node);
/* This node was changed: can fail and longjmp. */
void add_change_node(struct transaction *trans, const char *node);
/* Get shortest timeout: leave tv unset if none. */
void shortest_transaction_timeout(struct timeval *tv);
/* Have any transactions timed out yet? */
void check_transaction_timeout(void);
#endif /* _XENSTORED_TRANSACTION_H */
+279
View File
@@ -0,0 +1,279 @@
/*
Watch code for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include "talloc.h"
#include "list.h"
#include "xenstored_watch.h"
#include "xs_lib.h"
#include "utils.h"
#include "xenstored_test.h"
/* We create this if anyone is interested "node", then we pass it from
* watch to watch as each connection acks it.
*/
struct watch_event
{
/* The watch we are firing for (watch->events) */
struct list_head list;
/* Watch we are currently attached to. */
struct watch *watch;
struct buffered_data *data;
};
struct watch
{
struct list_head list;
unsigned int priority;
/* Current outstanding events applying to this watch. */
struct list_head events;
char *node;
struct connection *conn;
};
static LIST_HEAD(watches);
static void reset_event(struct watch_event *event)
{
event->data->inhdr = true;
event->data->used = 0;
}
/* We received a non-ACK response: re-queue any watch we just sent. */
void reset_watch_event(struct connection *conn)
{
if (waiting_for_ack(conn))
reset_event(conn->event);
}
/* We're waiting if we have an event and we sent it all. */
bool waiting_for_ack(struct connection *conn)
{
if (!conn->event)
return false;
if (conn->event->data->inhdr)
return false;
return conn->event->data->used == conn->event->data->hdr.msg.len;
}
bool is_watch_event(struct connection *conn, struct buffered_data *out)
{
return (conn->event && out == conn->event->data);
}
/* Look through our watches: if any of them have an event, queue it. */
void queue_next_event(struct connection *conn)
{
struct watch *watch;
/* We had a reply queued already? Send it. */
if (conn->waiting_reply) {
conn->out = conn->waiting_reply;
conn->waiting_reply = NULL;
return;
}
/* If we're waiting for ack, don't queue more. */
if (waiting_for_ack(conn))
return;
/* Find a good event to send. */
if (!conn->event) {
list_for_each_entry(watch, &watches, list) {
if (watch->conn != conn)
continue;
conn->event = list_top(&watch->events,
struct watch_event, list);
if (conn->event)
break;
}
if (!conn->event)
return;
}
conn->out = conn->event->data;
}
/* Watch on DIR applies to DIR, DIR/FILE, but not DIRLONG. */
static bool watch_applies(const struct watch *watch, const char *node)
{
return is_child(node, watch->node);
}
static struct watch *find_watch(const char *node)
{
struct watch *watch;
list_for_each_entry(watch, &watches, list) {
if (watch_applies(watch, node))
return watch;
}
return NULL;
}
static struct watch *find_next_watch(struct watch *watch, const char *node)
{
list_for_each_entry_continue(watch, &watches, list) {
if (watch_applies(watch, node))
return watch;
}
return NULL;
}
/* FIXME: we fail to fire on out of memory. Should drop connections. */
void fire_watches(struct transaction *trans, const char *node)
{
struct watch *watch;
struct watch_event *event;
/* During transactions, don't fire watches. */
if (trans)
return;
watch = find_watch(node);
if (!watch)
return;
/* Create and fill in info about event. */
event = talloc(talloc_autofree_context(), struct watch_event);
event->data = new_buffer(event);
event->data->hdr.msg.type = XS_WATCH_EVENT;
event->data->hdr.msg.len = strlen(node) + 1;
event->data->buffer = talloc_strdup(event->data, node);
/* Tie event to this watch. */
event->watch = watch;
list_add(&event->list, &watch->events);
/* If connection not doing anything, queue this. */
if (!watch->conn->out)
queue_next_event(watch->conn);
}
/* We're done with this event: see if anyone else wants it. */
static void move_event_onwards(struct watch_event *event)
{
list_del(&event->list);
reset_event(event);
/* Remove from this watch, and find next watch to put this on. */
event->watch = find_next_watch(event->watch, event->data->buffer);
if (!event->watch) {
talloc_free(event);
return;
}
list_add(&event->list, &event->watch->events);
/* If connection not doing anything, queue this. */
if (!event->watch->conn->out)
queue_next_event(event->watch->conn);
}
static int destroy_watch(void *_watch)
{
struct watch *watch = _watch;
struct watch_event *event;
/* Forget about sending out or waiting for acks for this watch. */
if (watch->conn->event && watch->conn->event->watch == watch)
watch->conn->event = NULL;
/* If we have pending events, pass them on to others. */
while ((event = list_top(&watch->events, struct watch_event, list)))
move_event_onwards(event);
/* Remove from global list. */
list_del(&watch->list);
return 0;
}
/* We keep watches in priority order. */
static void insert_watch(struct watch *watch)
{
struct watch *i;
list_for_each_entry(i, &watches, list) {
if (i->priority <= watch->priority) {
list_add_tail(&watch->list, &i->list);
return;
}
}
list_add_tail(&watch->list, &watches);
}
bool do_watch(struct connection *conn, struct buffered_data *in)
{
struct watch *watch;
char *vec[2];
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return send_error(conn, EINVAL);
if (!check_node_perms(conn, vec[0], XS_PERM_READ))
return send_error(conn, errno);
watch = talloc(conn, struct watch);
watch->node = talloc_strdup(watch, vec[0]);
watch->conn = conn;
watch->priority = strtoul(vec[1], NULL, 0);
INIT_LIST_HEAD(&watch->events);
insert_watch(watch);
talloc_set_destructor(watch, destroy_watch);
return send_ack(conn, XS_WATCH);
}
bool do_watch_ack(struct connection *conn)
{
struct watch_event *event;
if (!waiting_for_ack(conn))
return send_error(conn, ENOENT);
/* Remove this watch event. */
event = conn->event;
conn->event = NULL;
move_event_onwards(event);
return send_ack(conn, XS_WATCH_ACK);
}
bool do_unwatch(struct connection *conn, const char *node)
{
struct watch *watch;
list_for_each_entry(watch, &watches, list) {
if (watch->conn == conn
&& streq(watch->node, node)) {
talloc_free(watch);
return send_ack(conn, XS_UNWATCH);
}
}
return send_error(conn, ENOENT);
}
+42
View File
@@ -0,0 +1,42 @@
/*
Watch code for Xen Store Daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#ifndef _XENSTORED_WATCH_H
#define _XENSTORED_WATCH_H
#include "xenstored_core.h"
bool do_watch(struct connection *conn, struct buffered_data *in);
bool do_watch_ack(struct connection *conn);
bool do_unwatch(struct connection *conn, const char *node);
/* Is this a watch event message for this connection? */
bool is_watch_event(struct connection *conn, struct buffered_data *out);
/* Look through our watches: if any of them have an event, queue it. */
void queue_next_event(struct connection *conn);
/* Is this connection waiting for a watch acknowledgement? */
bool waiting_for_ack(struct connection *conn);
/* Reset event if we were sending one */
void reset_watch_event(struct connection *conn);
/* Fire all watches. */
void fire_watches(struct transaction *trans, const char *node);
#endif /* _XENSTORED_WATCH_H */
+551
View File
@@ -0,0 +1,551 @@
/*
Xen Store Daemon interface providing simple tree-like database.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <signal.h>
#include <stdint.h>
#include <errno.h>
#include "xs.h"
#include "xenstored.h"
#include "xs_lib.h"
#include "utils.h"
struct xs_handle
{
int fd;
};
/* Get the socket from the store daemon handle.
*/
int xs_fileno(struct xs_handle *h)
{
return h->fd;
}
static struct xs_handle *get_socket(const char *connect_to)
{
struct sockaddr_un addr;
int sock, saved_errno;
struct xs_handle *h = NULL;
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0)
return NULL;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, connect_to);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
h = malloc(sizeof(*h));
if (h) {
h->fd = sock;
return h;
}
}
saved_errno = errno;
close(sock);
free(h);
errno = saved_errno;
return NULL;
}
struct xs_handle *xs_daemon_open(void)
{
return get_socket(xs_daemon_socket());
}
struct xs_handle *xs_daemon_open_readonly(void)
{
return get_socket(xs_daemon_socket_ro());
}
void xs_daemon_close(struct xs_handle *h)
{
if (h->fd >= 0)
close(h->fd);
free(h);
}
static bool read_all(int fd, void *data, unsigned int len)
{
while (len) {
int done;
done = read(fd, data, len);
if (done < 0) {
if (errno == EINTR)
continue;
return false;
}
if (done == 0) {
/* It closed fd on us? EBADF is appropriate. */
errno = EBADF;
return false;
}
data += done;
len -= done;
}
return true;
}
#ifdef XSTEST
#define read_all read_all_choice
#define write_all write_all_choice
#endif
static int get_error(const char *errorstring)
{
unsigned int i;
for (i = 0; !streq(errorstring, xsd_errors[i].errstring); i++)
if (i == ARRAY_SIZE(xsd_errors) - 1)
return EINVAL;
return xsd_errors[i].errnum;
}
static void *read_reply(int fd, enum xsd_sockmsg_type *type, unsigned int *len)
{
struct xsd_sockmsg msg;
void *ret;
int saved_errno;
if (!read_all(fd, &msg, sizeof(msg)))
return NULL;
ret = malloc(msg.len);
if (!ret)
return NULL;
if (!read_all(fd, ret, msg.len)) {
saved_errno = errno;
free(ret);
errno = saved_errno;
return NULL;
}
*type = msg.type;
if (len)
*len = msg.len;
return ret;
}
/* Send message to xs, get malloc'ed reply. NULL and set errno on error. */
static void *xs_talkv(struct xs_handle *h, enum xsd_sockmsg_type type,
const struct iovec *iovec,
unsigned int num_vecs,
unsigned int *len)
{
struct xsd_sockmsg msg;
void *ret = NULL;
int saved_errno;
unsigned int i;
struct sigaction ignorepipe, oldact;
msg.type = type;
msg.len = 0;
for (i = 0; i < num_vecs; i++)
msg.len += iovec[i].iov_len;
ignorepipe.sa_handler = SIG_IGN;
sigemptyset(&ignorepipe.sa_mask);
ignorepipe.sa_flags = 0;
sigaction(SIGPIPE, &ignorepipe, &oldact);
if (!write_all(h->fd, &msg, sizeof(msg)))
goto fail;
for (i = 0; i < num_vecs; i++)
if (!write_all(h->fd, iovec[i].iov_base, iovec[i].iov_len))
goto fail;
/* Watches can have fired before reply comes: daemon detects
* and re-transmits, so we can ignore this. */
do {
free(ret);
ret = read_reply(h->fd, &msg.type, len);
if (!ret)
goto fail;
} while (msg.type == XS_WATCH_EVENT);
sigaction(SIGPIPE, &oldact, NULL);
if (msg.type == XS_ERROR) {
saved_errno = get_error(ret);
free(ret);
errno = saved_errno;
return NULL;
}
assert(msg.type == type);
return ret;
fail:
/* We're in a bad state, so close fd. */
saved_errno = errno;
sigaction(SIGPIPE, &oldact, NULL);
close(h->fd);
h->fd = -1;
errno = saved_errno;
return NULL;
}
/* free(), but don't change errno. */
static void free_no_errno(void *p)
{
int saved_errno = errno;
free(p);
errno = saved_errno;
}
/* Simplified version of xs_talkv: single message. */
static void *xs_single(struct xs_handle *h, enum xsd_sockmsg_type type,
const char *string, unsigned int *len)
{
struct iovec iovec;
iovec.iov_base = (void *)string;
iovec.iov_len = strlen(string) + 1;
return xs_talkv(h, type, &iovec, 1, len);
}
static bool xs_bool(char *reply)
{
if (!reply)
return false;
free(reply);
return true;
}
char **xs_directory(struct xs_handle *h, const char *path, unsigned int *num)
{
char *strings, *p, **ret;
unsigned int len;
strings = xs_single(h, XS_DIRECTORY, path, &len);
if (!strings)
return NULL;
/* Count the strings. */
*num = count_strings(strings, len);
/* Transfer to one big alloc for easy freeing. */
ret = malloc(*num * sizeof(char *) + len);
if (!ret) {
free_no_errno(strings);
return NULL;
}
memcpy(&ret[*num], strings, len);
free_no_errno(strings);
strings = (char *)&ret[*num];
for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1)
ret[(*num)++] = p;
return ret;
}
/* Get the value of a single file.
* Returns a malloced value: call free() on it after use.
* len indicates length in bytes.
*/
void *xs_read(struct xs_handle *h, const char *path, unsigned int *len)
{
return xs_single(h, XS_READ, path, len);
}
/* Write the value of a single file.
* Returns false on failure. createflags can be 0, O_CREAT, or O_CREAT|O_EXCL.
*/
bool xs_write(struct xs_handle *h, const char *path,
const void *data, unsigned int len, int createflags)
{
const char *flags;
struct iovec iovec[3];
/* Format: Flags (as string), path, data. */
if (createflags == 0)
flags = XS_WRITE_NONE;
else if (createflags == O_CREAT)
flags = XS_WRITE_CREATE;
else if (createflags == (O_CREAT|O_EXCL))
flags = XS_WRITE_CREATE_EXCL;
else {
errno = EINVAL;
return false;
}
iovec[0].iov_base = (void *)path;
iovec[0].iov_len = strlen(path) + 1;
iovec[1].iov_base = (void *)flags;
iovec[1].iov_len = strlen(flags) + 1;
iovec[2].iov_base = (void *)data;
iovec[2].iov_len = len;
return xs_bool(xs_talkv(h, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL));
}
/* Create a new directory.
* Returns false on failure.
*/
bool xs_mkdir(struct xs_handle *h, const char *path)
{
return xs_bool(xs_single(h, XS_MKDIR, path, NULL));
}
/* Destroy a file or directory (directories must be empty).
* Returns false on failure.
*/
bool xs_rm(struct xs_handle *h, const char *path)
{
return xs_bool(xs_single(h, XS_RM, path, NULL));
}
/* Get permissions of node (first element is owner).
* Returns malloced array, or NULL: call free() after use.
*/
struct xs_permissions *xs_get_permissions(struct xs_handle *h,
const char *path,
unsigned int *num)
{
char *strings;
unsigned int len;
struct xs_permissions *ret;
strings = xs_single(h, XS_GET_PERMS, path, &len);
if (!strings)
return NULL;
/* Count the strings: each one perms then domid. */
*num = count_strings(strings, len);
/* Transfer to one big alloc for easy freeing. */
ret = malloc(*num * sizeof(struct xs_permissions));
if (!ret) {
free_no_errno(strings);
return NULL;
}
if (!strings_to_perms(ret, *num, strings)) {
free_no_errno(ret);
ret = NULL;
}
free(strings);
return ret;
}
/* Set permissions of node (must be owner).
* Returns false on failure.
*/
bool xs_set_permissions(struct xs_handle *h, const char *path,
struct xs_permissions *perms,
unsigned int num_perms)
{
unsigned int i;
struct iovec iov[1+num_perms];
iov[0].iov_base = (void *)path;
iov[0].iov_len = strlen(path) + 1;
for (i = 0; i < num_perms; i++) {
char buffer[MAX_STRLEN(domid_t)+1];
if (!perm_to_string(&perms[i], buffer))
goto unwind;
iov[i+1].iov_base = strdup(buffer);
iov[i+1].iov_len = strlen(buffer) + 1;
if (!iov[i+1].iov_base)
goto unwind;
}
if (!xs_bool(xs_talkv(h, XS_SET_PERMS, iov, 1+num_perms, NULL)))
goto unwind;
for (i = 0; i < num_perms; i++)
free(iov[i+1].iov_base);
return true;
unwind:
num_perms = i;
for (i = 0; i < num_perms; i++)
free_no_errno(iov[i+1].iov_base);
return false;
}
/* Watch a node for changes (poll on fd to detect, or call read_watch()).
* When the node (or any child) changes, fd will become readable.
* Priority indicates order if multiple watchers: higher is first.
* Returns false on failure.
*/
bool xs_watch(struct xs_handle *h, const char *path, unsigned int priority)
{
char prio[MAX_STRLEN(priority)];
struct iovec iov[2];
sprintf(prio, "%u", priority);
iov[0].iov_base = (void *)path;
iov[0].iov_len = strlen(path) + 1;
iov[1].iov_base = prio;
iov[1].iov_len = strlen(prio) + 1;
return xs_bool(xs_talkv(h, XS_WATCH, iov, ARRAY_SIZE(iov), NULL));
}
/* Find out what node change was on (will block if nothing pending).
* Returns malloced path, or NULL: call free() after use.
*/
char *xs_read_watch(struct xs_handle *h)
{
struct xsd_sockmsg msg;
char *path;
if (!read_all(h->fd, &msg, sizeof(msg)))
return NULL;
assert(msg.type == XS_WATCH_EVENT);
path = malloc(msg.len);
if (!path)
return NULL;
if (!read_all(h->fd, path, msg.len)) {
free_no_errno(path);
return NULL;
}
return path;
}
/* Acknowledge watch on node. Watches must be acknowledged before
* any other watches can be read.
* Returns false on failure.
*/
bool xs_acknowledge_watch(struct xs_handle *h)
{
return xs_bool(xs_single(h, XS_WATCH_ACK, "OK", NULL));
}
/* Remove a watch on a node.
* Returns false on failure (no watch on that node).
*/
bool xs_unwatch(struct xs_handle *h, const char *path)
{
return xs_bool(xs_single(h, XS_UNWATCH, path, NULL));
}
/* Start a transaction: changes by others will not be seen during this
* transaction, and changes will not be visible to others until end.
* Transaction only applies to the given subtree.
* You can only have one transaction at any time.
* Returns false on failure.
*/
bool xs_transaction_start(struct xs_handle *h, const char *subtree)
{
return xs_bool(xs_single(h, XS_TRANSACTION_START, subtree, NULL));
}
/* End a transaction.
* If abandon is true, transaction is discarded instead of committed.
* Returns false on failure, which indicates an error: transactions will
* not fail spuriously.
*/
bool xs_transaction_end(struct xs_handle *h, bool abort)
{
char abortstr[2];
if (abort)
strcpy(abortstr, "F");
else
strcpy(abortstr, "T");
return xs_bool(xs_single(h, XS_TRANSACTION_END, abortstr, NULL));
}
/* Introduce a new domain.
* This tells the store daemon about a shared memory page and event channel
* associated with a domain: the domain uses these to communicate.
*/
bool xs_introduce_domain(struct xs_handle *h,
domid_t domid,
unsigned long mfn,
unsigned int eventchn,
const char *path)
{
char domid_str[MAX_STRLEN(domid)];
char mfn_str[MAX_STRLEN(mfn)];
char eventchn_str[MAX_STRLEN(eventchn)];
struct iovec iov[4];
sprintf(domid_str, "%u", domid);
sprintf(mfn_str, "%lu", mfn);
sprintf(eventchn_str, "%u", eventchn);
iov[0].iov_base = domid_str;
iov[0].iov_len = strlen(domid_str) + 1;
iov[1].iov_base = mfn_str;
iov[1].iov_len = strlen(mfn_str) + 1;
iov[2].iov_base = eventchn_str;
iov[2].iov_len = strlen(eventchn_str) + 1;
iov[3].iov_base = (char *)path;
iov[3].iov_len = strlen(path) + 1;
return xs_bool(xs_talkv(h, XS_INTRODUCE, iov, ARRAY_SIZE(iov), NULL));
}
bool xs_release_domain(struct xs_handle *h,
domid_t domid)
{
char domid_str[MAX_STRLEN(domid)];
sprintf(domid_str, "%u", domid);
return xs_bool(xs_single(h, XS_RELEASE, domid_str, NULL));
}
bool xs_shutdown(struct xs_handle *h)
{
bool ret = xs_bool(xs_single(h, XS_SHUTDOWN, "", NULL));
if (ret) {
char c;
/* Wait for it to actually shutdown. */
read(h->fd, &c, 1);
}
return ret;
}
/* Only useful for DEBUG versions */
char *xs_debug_command(struct xs_handle *h, const char *cmd,
void *data, unsigned int len)
{
struct iovec iov[2];
iov[0].iov_base = (void *)cmd;
iov[0].iov_len = strlen(cmd) + 1;
iov[1].iov_base = data;
iov[1].iov_len = len;
return xs_talkv(h, XS_DEBUG, iov, ARRAY_SIZE(iov), NULL);
}
+146
View File
@@ -0,0 +1,146 @@
#ifndef _XS_H
#define _XS_H
/*
Xen Store Daemon providing simple tree-like database.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
/* On failure, these routines set errno. */
#include "xs_lib.h"
struct xs_handle;
/* Connect to the xs daemon.
* Returns a handle or NULL.
*/
struct xs_handle *xs_daemon_open(void);
/* Connect to the xs daemon (readonly for non-root clients).
* Returns a handle or NULL.
*/
struct xs_handle *xs_daemon_open_readonly(void);
/* Close the connection to the xs daemon. */
void xs_daemon_close(struct xs_handle *);
/* Get contents of a directory.
* Returns a malloced array: call free() on it after use.
* Num indicates size.
*/
char **xs_directory(struct xs_handle *h, const char *path, unsigned int *num);
/* Get the value of a single file.
* Returns a malloced value: call free() on it after use.
* len indicates length in bytes.
*/
void *xs_read(struct xs_handle *h, const char *path, unsigned int *len);
/* Write the value of a single file.
* Returns false on failure. createflags can be 0, O_CREAT, or O_CREAT|O_EXCL.
*/
bool xs_write(struct xs_handle *h, const char *path, const void *data, unsigned int len,
int createflags);
/* Create a new directory.
* Returns false on failure.
*/
bool xs_mkdir(struct xs_handle *h, const char *path);
/* Destroy a file or directory (and children).
* Returns false on failure.
*/
bool xs_rm(struct xs_handle *h, const char *path);
/* Get permissions of node (first element is owner, first perms is "other").
* Returns malloced array, or NULL: call free() after use.
*/
struct xs_permissions *xs_get_permissions(struct xs_handle *h,
const char *path,
unsigned int *num);
/* Set permissions of node (must be owner).
* Returns false on failure.
*/
bool xs_set_permissions(struct xs_handle *h,
const char *path,
struct xs_permissions *perms,
unsigned int num_perms);
/* Watch a node for changes (poll on fd to detect, or call read_watch()).
* When the node (or any child) changes, fd will become readable.
* Priority indicates order if multiple watchers: higher is first.
* Returns false on failure.
*/
bool xs_watch(struct xs_handle *h, const char *path, unsigned int priority);
/* Return the FD to poll on to see if a watch has fired. */
int xs_fileno(struct xs_handle *h);
/* Find out what node change was on (will block if nothing pending).
* Returns malloced path, or NULL: call free() after use.
*/
char *xs_read_watch(struct xs_handle *h);
/* Acknowledge watch on node. Watches must be acknowledged before
* any other watches can be read.
* Returns false on failure.
*/
bool xs_acknowledge_watch(struct xs_handle *h);
/* Remove a watch on a node.
* Returns false on failure (no watch on that node).
*/
bool xs_unwatch(struct xs_handle *h, const char *path);
/* Start a transaction: changes by others will not be seen during this
* transaction, and changes will not be visible to others until end.
* Transaction only applies to the given subtree.
* You can only have one transaction at any time.
* Returns false on failure.
*/
bool xs_transaction_start(struct xs_handle *h, const char *subtree);
/* End a transaction.
* If abandon is true, transaction is discarded instead of committed.
* Returns false on failure, which indicates an error: transactions will
* not fail spuriously.
*/
bool xs_transaction_end(struct xs_handle *h, bool abort);
/* Introduce a new domain.
* This tells the store daemon about a shared memory page, event channel
* and store path associated with a domain: the domain uses these to communicate.
*/
bool xs_introduce_domain(struct xs_handle *h,
domid_t domid,
unsigned long mfn,
unsigned int eventchn,
const char *path);
/* Release a domain.
* Tells the store domain to release the memory page to the domain.
*/
bool xs_release_domain(struct xs_handle *h, domid_t domid);
/* Only useful for DEBUG versions */
char *xs_debug_command(struct xs_handle *h, const char *cmd,
void *data, unsigned int len);
/* Shut down the daemon. */
bool xs_shutdown(struct xs_handle *h);
#endif /* _XS_H */
+141
View File
@@ -0,0 +1,141 @@
#include "xs_lib.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/* Common routines for the Xen store daemon and client library. */
static const char *xs_daemon_rootdir(void)
{
char *s = getenv("XENSTORED_ROOTDIR");
return (s ? s : "/var/lib/xenstored");
}
static const char *xs_daemon_rundir(void)
{
char *s = getenv("XENSTORED_RUNDIR");
return (s ? s : "/var/run/xenstored");
}
const char *xs_daemon_socket(void)
{
static char buf[PATH_MAX];
sprintf(buf, "%s/socket", xs_daemon_rundir());
return buf;
}
const char *xs_daemon_socket_ro(void)
{
static char buf[PATH_MAX];
sprintf(buf, "%s/socket_ro", xs_daemon_rundir());
return buf;
}
const char *xs_daemon_store(void)
{
static char buf[PATH_MAX];
sprintf(buf, "%s/store", xs_daemon_rootdir());
return buf;
}
const char *xs_daemon_transactions(void)
{
static char buf[PATH_MAX];
sprintf(buf, "%s/transactions", xs_daemon_rootdir());
return buf;
}
/* Simple routines for writing to sockets, etc. */
bool write_all(int fd, const void *data, unsigned int len)
{
while (len) {
int done;
done = write(fd, data, len);
if (done < 0 && errno == EINTR)
continue;
if (done <= 0)
return false;
data += done;
len -= done;
}
return true;
}
/* Convert strings to permissions. False if a problem. */
bool strings_to_perms(struct xs_permissions *perms, unsigned int num,
const char *strings)
{
const char *p;
char *end;
unsigned int i;
for (p = strings, i = 0; i < num; i++) {
/* "r", "w", or "b" for both. */
switch (*p) {
case 'r':
perms[i].perms = XS_PERM_READ;
break;
case 'w':
perms[i].perms = XS_PERM_WRITE;
break;
case 'b':
perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
break;
case 'n':
perms[i].perms = XS_PERM_NONE;
break;
default:
errno = EINVAL;
return false;
}
p++;
perms[i].id = strtol(p, &end, 0);
if (*end || !*p) {
errno = EINVAL;
return false;
}
p = end + 1;
}
return true;
}
/* Convert permissions to a string (up to len MAX_STRLEN(domid_t)+1). */
bool perm_to_string(const struct xs_permissions *perm, char *buffer)
{
switch (perm->perms) {
case XS_PERM_WRITE:
*buffer = 'w';
break;
case XS_PERM_READ:
*buffer = 'r';
break;
case XS_PERM_READ|XS_PERM_WRITE:
*buffer = 'b';
break;
case XS_PERM_NONE:
*buffer = 'n';
break;
default:
errno = EINVAL;
return false;
}
sprintf(buffer+1, "%i", (int)perm->id);
return true;
}
/* Given a string and a length, count how many strings (nul terms). */
unsigned int count_strings(const char *strings, unsigned int len)
{
unsigned int num;
const char *p;
for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1)
num++;
return num;
}
+63
View File
@@ -0,0 +1,63 @@
#ifndef _XR_LIB_H
#define _XR_LIB_H
/*
Common routines between Xen store user library and daemon.
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <stdbool.h>
#include <limits.h>
#include <xc.h>
/* Bitmask of permissions. */
enum xs_perm_type {
XS_PERM_NONE = 0,
XS_PERM_READ = 1,
XS_PERM_WRITE = 2,
/* Internal use. */
XS_PERM_CREATE = 4,
XS_PERM_OWNER = 8,
};
struct xs_permissions
{
domid_t id;
enum xs_perm_type perms;
};
/* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
#define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
/* Path for various daemon things: env vars can override. */
const char *xs_daemon_socket(void);
const char *xs_daemon_socket_ro(void);
const char *xs_daemon_store(void);
const char *xs_daemon_transactions(void);
/* Simple write function: loops for you. */
bool write_all(int fd, const void *data, unsigned int len);
/* Convert strings to permissions. False if a problem. */
bool strings_to_perms(struct xs_permissions *perms, unsigned int num,
const char *strings);
/* Convert permissions to a string (up to len MAX_STRLEN(domid_t)+1). */
bool perm_to_string(const struct xs_permissions *perm, char *buffer);
/* Given a string and a length, count how many strings (nul terms). */
unsigned int count_strings(const char *strings, unsigned int len);
#endif /* _XS_LIB_H */
File diff suppressed because it is too large. Load diff
+207
View File
@@ -0,0 +1,207 @@
/* Stress test for Xen Store: multiple people hammering transactions */
#include "xs.h"
#include "utils.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define NUM_HANDLES 2
#define DIR_FANOUT 3
#define DIR_DEPTH 3
/* How often to print progress */
static int print;
/* Layout looks like /<num>/<num>/count. */
static void work(unsigned int cycles, unsigned int childnum)
{
unsigned int i;
struct xs_handle *handles[NUM_HANDLES];
char id;
if (childnum < 10)
id = '0' + childnum;
else
id = 'A' + childnum - 10;
for (i = 0; i < NUM_HANDLES; i++) {
handles[i] = xs_daemon_open();
if (!handles[i])
barf_perror("Opening handle %i", i);
}
srandom(childnum);
for (i = 0; i < cycles; i++) {
unsigned int lockdepth, j, len;
char file[100] = "", lockdir[100];
char *contents, tmp[100];
struct xs_handle *h = handles[random() % NUM_HANDLES];
lockdepth = random() % DIR_DEPTH;
for (j = 0; j < DIR_DEPTH; j++) {
if (j == lockdepth)
strcpy(lockdir, file);
sprintf(file + strlen(file), "/%li",
random()%DIR_FANOUT);
}
if (streq(lockdir, ""))
strcpy(lockdir, "/");
if (!xs_transaction_start(h, lockdir))
barf_perror("%i: starting transaction %i on %s",
childnum, i, lockdir);
sprintf(file + strlen(file), "/count");
contents = xs_read(h, file, &len);
if (!contents)
barf_perror("%i: can't read %s iter %i",
childnum, file, i);
sprintf(tmp, "%i", atoi(contents) + 1);
if (!xs_write(h, file, tmp, strlen(tmp)+1, 0))
barf_perror("%i: can't write %s iter %i",
childnum, file, i);
/* Abandon 1 in 10 */
if (random() % 10 == 0) {
if (!xs_transaction_end(h, true))
barf_perror("%i: can't abort transact %s",
childnum, lockdir);
i--;
} else {
if (!xs_transaction_end(h, false))
barf_perror("%i: can't commit transact %s",
childnum, lockdir);
/* Offset when we print . so kids don't all
* print at once. */
if ((i + print/(childnum+1)) % print == 0)
write(STDOUT_FILENO, &id, 1);
}
}
}
static void create_dirs(struct xs_handle *h, const char *base, int togo)
{
unsigned int i;
char filename[100];
if (togo == 0) {
sprintf(filename, "%s/count", base);
if (!xs_write(h, filename, "0", 2, O_EXCL|O_CREAT))
barf_perror("Writing to %s", filename);
return;
}
for (i = 0; i < DIR_FANOUT; i++) {
sprintf(filename, "%s/%i", base, i);
if (!xs_mkdir(h, filename))
barf_perror("xs_mkdir %s", filename);
create_dirs(h, filename, togo-1);
}
}
static unsigned int add_count(struct xs_handle *h, const char *base, int togo)
{
unsigned int i, count;
char filename[100];
if (togo == 0) {
char *answer;
unsigned int len;
sprintf(filename, "%s/count", base);
answer = xs_read(h, filename, &len);
if (!answer)
barf_perror("Reading %s", filename);
count = atoi(answer);
free(answer);
return count;
}
count = 0;
for (i = 0; i < DIR_FANOUT; i++) {
sprintf(filename, "%s/%i", base, i);
count += add_count(h, filename, togo-1);
}
return count;
}
static void setup(void)
{
struct xs_handle *h;
/* Do setup. */
h = xs_daemon_open();
if (!h)
barf_perror("Contacting daemon");
create_dirs(h, "", DIR_DEPTH);
xs_daemon_close(h);
}
static unsigned int tally_counts(void)
{
struct xs_handle *h;
unsigned int ret;
h = xs_daemon_open();
if (!h)
barf_perror("Contacting daemon");
ret = add_count(h, "", DIR_DEPTH);
xs_daemon_close(h);
return ret;
}
int main(int argc, char *argv[])
{
unsigned int i;
bool failed = false;
int kids[10];
if (argc != 2)
barf("Usage: xs_stress <iterations>");
printf("Setting up directories...\n");
setup();
print = atoi(argv[1]) / 76;
if (!print)
print = 1;
printf("Running %i children...\n", ARRAY_SIZE(kids));
for (i = 0; i < ARRAY_SIZE(kids); i++) {
kids[i] = fork();
if (kids[i] == -1)
barf_perror("fork");
if (kids[i] == 0) {
work(atoi(argv[1]) / ARRAY_SIZE(kids), i);
exit(0);
}
}
for (i = 0; i < ARRAY_SIZE(kids); i++) {
int status;
if (waitpid(kids[i], &status, 0) == -1)
barf_perror("waitpid");
if (!WIFEXITED(status))
barf("Kid %i died via signal %i\n",
i, WTERMSIG(status));
if (WEXITSTATUS(status) != 0) {
printf("Child %i exited %i\n", i, WEXITSTATUS(status));
failed = true;
}
}
if (failed)
exit(1);
printf("\nCounting results...\n");
i = tally_counts();
if (i != (unsigned)atoi(argv[1]))
barf("Total counts %i not %s", i, atoi(argv[1]));
printf("Success!\n");
exit(0);
}
+647
View File
@@ -0,0 +1,647 @@
/*
Xen Store Daemon Test tool
Copyright (C) 2005 Rusty Russell IBM Corporation
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 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "utils.h"
#include "xs_lib.h"
#define XSTEST
static struct xs_handle *handles[10] = { NULL };
struct ringbuf_head
{
uint32_t write; /* Next place to write to */
uint32_t read; /* Next place to read from */
uint8_t flags;
char buf[0];
} __attribute__((packed));
static struct ringbuf_head *out, *in;
static unsigned int ringbuf_datasize;
static int daemon_pid;
/* FIXME: Mark connection as broken (close it?) when this happens. */
static bool check_buffer(const struct ringbuf_head *h)
{
return (h->write < ringbuf_datasize && h->read < ringbuf_datasize);
}
/* We can't fill last byte: would look like empty buffer. */
static void *get_output_chunk(const struct ringbuf_head *h,
void *buf, uint32_t *len)
{
uint32_t read_mark;
if (h->read == 0)
read_mark = ringbuf_datasize - 1;
else
read_mark = h->read - 1;
/* Here to the end of buffer, unless they haven't read some out. */
*len = ringbuf_datasize - h->write;
if (read_mark >= h->write)
*len = read_mark - h->write;
return buf + h->write;
}
static const void *get_input_chunk(const struct ringbuf_head *h,
const void *buf, uint32_t *len)
{
/* Here to the end of buffer, unless they haven't written some. */
*len = ringbuf_datasize - h->read;
if (h->write >= h->read)
*len = h->write - h->read;
return buf + h->read;
}
static void update_output_chunk(struct ringbuf_head *h, uint32_t len)
{
h->write += len;
if (h->write == ringbuf_datasize)
h->write = 0;
}
static void update_input_chunk(struct ringbuf_head *h, uint32_t len)
{
h->read += len;
if (h->read == ringbuf_datasize)
h->read = 0;
}
/* FIXME: We spin, and we're sloppy. */
static bool read_all_shmem(int fd __attribute__((unused)),
void *data, unsigned int len)
{
unsigned int avail;
if (!check_buffer(in))
barf("Corrupt buffer");
while (len) {
const void *src = get_input_chunk(in, in->buf, &avail);
if (avail > len)
avail = len;
memcpy(data, src, avail);
data += avail;
len -= avail;
update_input_chunk(in, avail);
}
/* Tell other end we read something. */
kill(daemon_pid, SIGUSR2);
return true;
}
static bool write_all_shmem(int fd __attribute__((unused)),
const void *data, unsigned int len)
{
uint32_t avail;
if (!check_buffer(out))
barf("Corrupt buffer");
while (len) {
void *dst = get_output_chunk(out, out->buf, &avail);
if (avail > len)
avail = len;
memcpy(dst, data, avail);
data += avail;
len -= avail;
update_output_chunk(out, avail);
}
/* Tell other end we wrote something. */
kill(daemon_pid, SIGUSR2);
return true;
}
static bool read_all(int fd, void *data, unsigned int len);
static bool read_all_choice(int fd, void *data, unsigned int len)
{
if (fd == -2)
return read_all_shmem(fd, data, len);
return read_all(fd, data, len);
}
static bool write_all_choice(int fd, const void *data, unsigned int len)
{
if (fd == -2)
return write_all_shmem(fd, data, len);
return write_all(fd, data, len);
}
/* We want access to internal functions. */
#include "xs.c"
static void __attribute__((noreturn)) usage(void)
{
barf("Usage:\n"
" xs_test [--readonly] [--notimeout]\n"
"Reads commands from stdin, one per line:"
" dir <path>\n"
" read <path>\n"
" write <path> <flags> <value>...\n"
" setid <id>\n"
" mkdir <path>\n"
" rm <path>\n"
" getperm <path>\n"
" setperm <path> <id> <flags> ...\n"
" shutdown\n"
" watch <path> <prio>\n"
" waitwatch\n"
" ackwatch\n"
" unwatch <path>\n"
" close\n"
" start <node>\n"
" abort\n"
" introduce <domid> <mfn> <eventchn>\n"
" commit\n"
" sleep <seconds>\n"
" dump\n");
}
static char *arg(char *line, unsigned int num)
{
static char *args[10];
unsigned int i, len = 0;
for (i = 0; i <= num; i++) {
line += len;
line += strspn(line, " \t\n");
len = strcspn(line, " \t\n");
if (!len)
barf("Can't get arg %u", num);
}
free(args[num]);
args[num] = malloc(len + 1);
memcpy(args[num], line, len);
args[num][len] = '\0';
return args[num];
}
static char *command;
static void __attribute__((noreturn)) failed(int handle)
{
if (handle)
barf_perror("%i: %s", handle, command);
barf_perror("%s", command);
}
static void do_dir(unsigned int handle, char *path)
{
char **entries;
unsigned int i, num;
entries = xs_directory(handles[handle], path, &num);
if (!entries)
failed(handle);
for (i = 0; i < num; i++)
if (handle)
printf("%i:%s\n", handle, entries[i]);
else
printf("%s\n", entries[i]);
free(entries);
}
static void do_read(unsigned int handle, char *path)
{
char *value;
unsigned int len;
value = xs_read(handles[handle], path, &len);
if (!value)
failed(handle);
if (handle)
printf("%i:%.*s\n", handle, len, value);
else
printf("%.*s\n", len, value);
}
static void do_write(unsigned int handle, char *path, char *flags, char *data)
{
int f;
if (streq(flags, "none"))
f = 0;
else if (streq(flags, "create"))
f = O_CREAT;
else if (streq(flags, "excl"))
f = O_CREAT | O_EXCL;
else if (streq(flags, "crap"))
f = 100;
else
barf("write flags 'none', 'create' or 'excl' only");
if (!xs_write(handles[handle], path, data, strlen(data)+1, f))
failed(handle);
}
static void do_setid(unsigned int handle, char *id)
{
if (!xs_bool(xs_debug_command(handles[handle], "setid", id,
strlen(id)+1)))
failed(handle);
}
static void do_mkdir(unsigned int handle, char *path)
{
if (!xs_mkdir(handles[handle], path))
failed(handle);
}
static void do_rm(unsigned int handle, char *path)
{
if (!xs_rm(handles[handle], path))
failed(handle);
}
static void do_getperm(unsigned int handle, char *path)
{
unsigned int i, num;
struct xs_permissions *perms;
perms = xs_get_permissions(handles[handle], path, &num);
if (!perms)
failed(handle);
for (i = 0; i < num; i++) {
char *permstring;
switch (perms[i].perms) {
case XS_PERM_NONE:
permstring = "NONE";
break;
case XS_PERM_WRITE:
permstring = "WRITE";
break;
case XS_PERM_READ:
permstring = "READ";
break;
case XS_PERM_READ|XS_PERM_WRITE:
permstring = "READ/WRITE";
break;
default:
barf("bad perm value %i", perms[i].perms);
}
if (handle)
printf("%i:%i %s\n", handle, perms[i].id, permstring);
else
printf("%i %s\n", perms[i].id, permstring);
}
free(perms);
}
static void do_setperm(unsigned int handle, char *path, char *line)
{
unsigned int i;
struct xs_permissions perms[100];
strtok(line, " \t\n");
strtok(NULL, " \t\n");
for (i = 0; ; i++) {
char *arg = strtok(NULL, " \t\n");
if (!arg)
break;
perms[i].id = atoi(arg);
arg = strtok(NULL, " \t\n");
if (!arg)
break;
if (streq(arg, "WRITE"))
perms[i].perms = XS_PERM_WRITE;
else if (streq(arg, "READ"))
perms[i].perms = XS_PERM_READ;
else if (streq(arg, "READ/WRITE"))
perms[i].perms = XS_PERM_READ|XS_PERM_WRITE;
else if (streq(arg, "NONE"))
perms[i].perms = XS_PERM_NONE;
else
barf("bad flags %s\n", arg);
}
if (!xs_set_permissions(handles[handle], path, perms, i))
failed(handle);
}
static void do_shutdown(unsigned int handle)
{
if (!xs_shutdown(handles[handle]))
failed(handle);
}
static void do_watch(unsigned int handle, const char *node, const char *pri)
{
if (!xs_watch(handles[handle], node, atoi(pri)))
failed(handle);
}
static void do_waitwatch(unsigned int handle)
{
char *node;
node = xs_read_watch(handles[handle]);
if (!node)
failed(handle);
if (handle)
printf("%i:%s\n", handle, node);
else
printf("%s\n", node);
free(node);
}
static void do_ackwatch(unsigned int handle)
{
if (!xs_acknowledge_watch(handles[handle]))
failed(handle);
}
static void do_unwatch(unsigned int handle, const char *node)
{
if (!xs_unwatch(handles[handle], node))
failed(handle);
}
static void do_start(unsigned int handle, const char *node)
{
if (!xs_transaction_start(handles[handle], node))
failed(handle);
}
static void do_end(unsigned int handle, bool abort)
{
if (!xs_transaction_end(handles[handle], abort))
failed(handle);
}
static void do_introduce(unsigned int handle,
const char *domid,
const char *mfn,
const char *eventchn,
const char *path)
{
unsigned int i;
int fd;
/* We poll, so ignore signal */
signal(SIGUSR2, SIG_IGN);
for (i = 0; i < ARRAY_SIZE(handles); i++)
if (!handles[i])
break;
fd = open("/tmp/xcmap", O_RDWR);
/* Set in and out pointers. */
out = mmap(NULL, getpagesize(), PROT_WRITE|PROT_READ, MAP_SHARED,fd,0);
if (out == MAP_FAILED)
barf_perror("Failed to map /tmp/xcmap page");
in = (void *)out + getpagesize() / 2;
close(fd);
/* Tell them the event channel and our PID. */
*(int *)((void *)out + 32) = getpid();
*(u16 *)((void *)out + 36) = atoi(eventchn);
/* Create new handle. */
handles[i] = new(struct xs_handle);
handles[i]->fd = -2;
if (!xs_introduce_domain(handles[handle], atoi(domid),
atol(mfn), atoi(eventchn), path))
failed(handle);
printf("handle is %i\n", i);
/* Read in daemon pid. */
daemon_pid = *(int *)((void *)out + 32);
}
static void do_release(unsigned int handle, const char *domid)
{
if (!xs_release_domain(handles[handle], atoi(domid)))
failed(handle);
}
static int strptrcmp(const void *a, const void *b)
{
return strcmp(*(char **)a, *(char **)b);
}
static void sort_dir(char **dir, unsigned int num)
{
qsort(dir, num, sizeof(char *), strptrcmp);
}
static void dump_dir(unsigned int handle,
const char *node,
char **dir,
unsigned int numdirs,
unsigned int depth)
{
unsigned int i;
char spacing[depth+1];
memset(spacing, ' ', depth);
spacing[depth] = '\0';
sort_dir(dir, numdirs);
for (i = 0; i < numdirs; i++) {
struct xs_permissions *perms;
unsigned int j, numperms;
unsigned int len;
char *contents;
unsigned int subnum;
char **subdirs;
char subnode[strlen(node) + 1 + strlen(dir[i]) + 1];
sprintf(subnode, "%s/%s", node, dir[i]);
perms = xs_get_permissions(handles[handle], subnode,&numperms);
if (!perms)
failed(handle);
printf("%s%s: ", spacing, dir[i]);
for (j = 0; j < numperms; j++) {
char buffer[100];
if (!perm_to_string(&perms[j], buffer))
barf("perm to string");
printf("%s ", buffer);
}
free(perms);
printf("\n");
/* Even directories can have contents. */
contents = xs_read(handles[handle], subnode, &len);
if (!contents) {
if (errno != EISDIR)
failed(handle);
} else {
printf(" %s(%.*s)\n", spacing, len, contents);
free(contents);
}
/* Every node is a directory. */
subdirs = xs_directory(handles[handle], subnode, &subnum);
if (!subdirs)
failed(handle);
dump_dir(handle, subnode, subdirs, subnum, depth+1);
free(subdirs);
}
}
static void dump(int handle)
{
char **subdirs;
unsigned int subnum;
subdirs = xs_directory(handles[handle], "/", &subnum);
if (!subdirs)
failed(handle);
dump_dir(handle, "", subdirs, subnum, 0);
free(subdirs);
}
int main(int argc, char *argv[])
{
char line[1024];
bool readonly = false, timeout = true;
int handle;
static void alarmed(int sig __attribute__((unused)))
{
if (handle) {
char handlename[10];
sprintf(handlename, "%u:", handle);
write(STDOUT_FILENO, handlename, strlen(handlename));
}
write(STDOUT_FILENO, command, strlen(command));
write(STDOUT_FILENO, " timeout\n", strlen(" timeout\n"));
exit(1);
}
if (argc > 1 && streq(argv[1], "--readonly")) {
readonly = true;
argc--;
argv++;
}
if (argc > 1 && streq(argv[1], "--notimeout")) {
timeout = false;
argc--;
argv++;
}
if (argc != 1)
usage();
/* The size of the ringbuffer: half a page minus head structure. */
ringbuf_datasize = getpagesize() / 2 - sizeof(struct ringbuf_head);
signal(SIGALRM, alarmed);
while (fgets(line, sizeof(line), stdin)) {
char *endp;
if (strspn(line, " \n") == strlen(line))
continue;
if (strstarts(line, "#"))
continue;
handle = strtoul(line, &endp, 10);
if (endp != line)
memmove(line, endp+1, strlen(endp));
else
handle = 0;
if (!handles[handle]) {
if (readonly)
handles[handle] = xs_daemon_open_readonly();
else
handles[handle] = xs_daemon_open();
if (!handles[handle])
barf_perror("Opening connection to daemon");
}
command = arg(line, 0);
if (timeout)
alarm(5);
if (streq(command, "dir"))
do_dir(handle, arg(line, 1));
else if (streq(command, "read"))
do_read(handle, arg(line, 1));
else if (streq(command, "write"))
do_write(handle,
arg(line, 1), arg(line, 2), arg(line, 3));
else if (streq(command, "setid"))
do_setid(handle, arg(line, 1));
else if (streq(command, "mkdir"))
do_mkdir(handle, arg(line, 1));
else if (streq(command, "rm"))
do_rm(handle, arg(line, 1));
else if (streq(command, "getperm"))
do_getperm(handle, arg(line, 1));
else if (streq(command, "setperm"))
do_setperm(handle, arg(line, 1), line);
else if (streq(command, "shutdown"))
do_shutdown(handle);
else if (streq(command, "watch"))
do_watch(handle, arg(line, 1), arg(line, 2));
else if (streq(command, "waitwatch"))
do_waitwatch(handle);
else if (streq(command, "ackwatch"))
do_ackwatch(handle);
else if (streq(command, "unwatch"))
do_unwatch(handle, arg(line, 1));
else if (streq(command, "close")) {
xs_daemon_close(handles[handle]);
handles[handle] = NULL;
} else if (streq(command, "start"))
do_start(handle, arg(line, 1));
else if (streq(command, "commit"))
do_end(handle, false);
else if (streq(command, "abort"))
do_end(handle, true);
else if (streq(command, "introduce"))
do_introduce(handle, arg(line, 1), arg(line, 2),
arg(line, 3), arg(line, 4));
else if (streq(command, "release"))
do_release(handle, arg(line, 1));
else if (streq(command, "dump"))
dump(handle);
else if (streq(command, "sleep"))
sleep(atoi(arg(line, 1)));
else
barf("Unknown command %s", command);
fflush(stdout);
alarm(0);
}
return 0;
}
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.