diff options
Diffstat (limited to 'ctdb/lib/util')
-rw-r--r-- | ctdb/lib/util/db_wrap.c | 103 | ||||
-rw-r--r-- | ctdb/lib/util/db_wrap.h | 36 | ||||
-rw-r--r-- | ctdb/lib/util/debug.c | 136 | ||||
-rw-r--r-- | ctdb/lib/util/debug.h | 27 | ||||
-rw-r--r-- | ctdb/lib/util/dlinklist.h | 181 | ||||
-rw-r--r-- | ctdb/lib/util/fault.c | 238 | ||||
-rw-r--r-- | ctdb/lib/util/fault.m4 | 15 | ||||
-rw-r--r-- | ctdb/lib/util/idtree.c | 387 | ||||
-rw-r--r-- | ctdb/lib/util/signal.c | 144 | ||||
-rw-r--r-- | ctdb/lib/util/signal.m4 | 1 | ||||
-rw-r--r-- | ctdb/lib/util/strlist.c | 52 | ||||
-rw-r--r-- | ctdb/lib/util/substitute.c | 167 | ||||
-rw-r--r-- | ctdb/lib/util/util.c | 52 | ||||
-rw-r--r-- | ctdb/lib/util/util.h | 651 | ||||
-rw-r--r-- | ctdb/lib/util/util_file.c | 132 | ||||
-rw-r--r-- | ctdb/lib/util/util_time.c | 108 |
16 files changed, 2430 insertions, 0 deletions
diff --git a/ctdb/lib/util/db_wrap.c b/ctdb/lib/util/db_wrap.c new file mode 100644 index 00000000000..1b2bf7e2281 --- /dev/null +++ b/ctdb/lib/util/db_wrap.c @@ -0,0 +1,103 @@ +/* + Unix SMB/CIFS implementation. + + database wrap functions + + Copyright (C) Andrew Tridgell 2004 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + the stupidity of the unix fcntl locking design forces us to never + allow a database file to be opened twice in the same process. These + wrappers provide convenient access to a tdb or ldb, taking advantage + of talloc destructors to ensure that only a single open is done +*/ + +#include "includes.h" +#include "lib/util/dlinklist.h" +#include "tdb.h" +#include "db_wrap.h" + +static struct tdb_wrap *tdb_list; + + + +/* destroy the last connection to a tdb */ +static int tdb_wrap_destructor(struct tdb_wrap *w) +{ + tdb_close(w->tdb); + DLIST_REMOVE(tdb_list, w); + return 0; +} + +static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ + if (level <= TDB_DEBUG_ERROR) { + va_list ap; + this_log_level = level; + char newfmt[strlen(tdb_name(tdb)) + 1 + strlen(fmt) + 1]; + sprintf(newfmt, "%s:%s", tdb_name(tdb), fmt); + va_start(ap, fmt); + do_debug_v(newfmt, ap); + va_end(ap); + } +} + + +/* + wrapped connection to a tdb database + to close just talloc_free() the tdb_wrap pointer + */ +struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx, + const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + struct tdb_wrap *w; + struct tdb_logging_context log_ctx; + + log_ctx.log_fn = log_fn; + log_ctx.log_private = NULL; + + for (w=tdb_list;w;w=w->next) { + if (strcmp(name, w->name) == 0) { + return talloc_reference(mem_ctx, w); + } + } + + w = talloc(mem_ctx, struct tdb_wrap); + if (w == NULL) { + return NULL; + } + + w->name = talloc_strdup(w, name); + if (w->name == NULL) { + talloc_free(w); + return NULL; + } + + w->tdb = tdb_open_ex(name, hash_size, tdb_flags, + open_flags, mode, &log_ctx, NULL); + if (w->tdb == NULL) { + talloc_free(w); + return NULL; + } + + talloc_set_destructor(w, tdb_wrap_destructor); + + DLIST_ADD(tdb_list, w); + + return w; +} diff --git a/ctdb/lib/util/db_wrap.h b/ctdb/lib/util/db_wrap.h new file mode 100644 index 00000000000..5ae5fd10ae3 --- /dev/null +++ b/ctdb/lib/util/db_wrap.h @@ -0,0 +1,36 @@ +/* + Unix SMB/CIFS implementation. + + database wrap headers + + Copyright (C) Andrew Tridgell 2004 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _DB_WRAP_H +#define _DB_WRAP_H + +struct tdb_wrap { + struct tdb_context *tdb; + + const char *name; + struct tdb_wrap *next, *prev; +}; + +struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx, + const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode); + +#endif /* _DB_WRAP_H */ diff --git a/ctdb/lib/util/debug.c b/ctdb/lib/util/debug.c new file mode 100644 index 00000000000..e9365d88f63 --- /dev/null +++ b/ctdb/lib/util/debug.c @@ -0,0 +1,136 @@ +/* + Unix SMB/CIFS implementation. + ctdb debug functions + Copyright (C) Volker Lendecke 2007 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/time.h" +#include <unistd.h> +#include <ctype.h> + +static void _do_debug_v(const char *format, va_list ap) +{ + struct timeval t; + char *s = NULL; + struct tm *tm; + char tbuf[100]; + int ret; + + ret = vasprintf(&s, format, ap); + if (ret == -1) { + fprintf(stderr, "vasprintf failed in _do_debug_v, cannot print debug message.\n"); + fflush(stderr); + return; + } + + t = timeval_current(); + tm = localtime(&t.tv_sec); + + strftime(tbuf,sizeof(tbuf)-1,"%Y/%m/%d %H:%M:%S", tm); + + fprintf(stderr, "%s.%06u [%s%5u]: %s", tbuf, (unsigned)t.tv_usec, + debug_extra, (unsigned)getpid(), s); + fflush(stderr); + free(s); +} + +/* default logging function */ +void (*do_debug_v)(const char *, va_list ap) = _do_debug_v; +const char *debug_extra = ""; + +void do_debug(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + do_debug_v(format, ap); + va_end(ap); +} + + +static void _do_debug_add_v(const char *format, va_list ap) +{ + char *s = NULL; + int ret; + + ret = vasprintf(&s, format, ap); + if (ret == -1) { + fprintf(stderr, "vasprintf failed in _do_debug_add_v, cannot print debug message.\n"); + fflush(stderr); + return; + } + + fprintf(stderr, "%s", s); + fflush(stderr); + free(s); +} + +/* default logging function */ +void (*do_debug_add_v)(const char *, va_list ap) = _do_debug_add_v; + +void do_debug_add(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + do_debug_add_v(format, ap); + va_end(ap); +} + +static void print_asc(int level, const uint8_t *buf, size_t len) +{ + int i; + for (i=0;i<len;i++) { + DEBUGADD(level,("%c", isprint(buf[i])?buf[i]:'.')); + } +} + +void dump_data(int level, const uint8_t *buf, size_t len) +{ + int i=0; + + if (len<=0) return; + + if (!DEBUGLVL(level)) return; + + DEBUG(level, (__location__ " dump data of size %i:\n", (int)len)); + DEBUGADD(level,("[%03X] ",i)); + for (i=0;i<len;) { + DEBUGADD(level,("%02X ",(int)buf[i])); + i++; + if (i%8 == 0) DEBUGADD(level,(" ")); + if (i%16 == 0) { + print_asc(level,&buf[i-16],8); DEBUGADD(level,(" ")); + print_asc(level,&buf[i-8],8); DEBUGADD(level,("\n")); + if (i<len) DEBUGADD(level,("[%03X] ",i)); + } + } + if (i%16) { + int n; + n = 16 - (i%16); + DEBUGADD(level,(" ")); + if (n>8) DEBUGADD(level,(" ")); + while (n--) DEBUGADD(level,(" ")); + n = MIN(8,i%16); + print_asc(level,&buf[i-(i%16)],n); DEBUGADD(level,( " " )); + n = (i%16) - n; + if (n>0) print_asc(level,&buf[i-n],n); + DEBUGADD(level,("\n")); + } + DEBUG(level, (__location__ " dump data of size %i finished\n", (int)len)); +} + diff --git a/ctdb/lib/util/debug.h b/ctdb/lib/util/debug.h new file mode 100644 index 00000000000..27490a3dbf9 --- /dev/null +++ b/ctdb/lib/util/debug.h @@ -0,0 +1,27 @@ +/* + Unix SMB/CIFS implementation. + ctdb debug functions + Copyright (C) Volker Lendecke 2007 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +void (*do_debug_v)(const char *, va_list ap); +const char *debug_extra; +void (*do_debug_add_v)(const char *, va_list ap); +void log_ringbuffer(const char *format, ...); +void do_debug(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +void do_debug_add(const char *format, ...) PRINTF_ATTRIBUTE(1, 2); +void dump_data(int level, const uint8_t *buf1, size_t len); + diff --git a/ctdb/lib/util/dlinklist.h b/ctdb/lib/util/dlinklist.h new file mode 100644 index 00000000000..6d525f90364 --- /dev/null +++ b/ctdb/lib/util/dlinklist.h @@ -0,0 +1,181 @@ +/* + Unix SMB/CIFS implementation. + some simple double linked list macros + + Copyright (C) Andrew Tridgell 1998-2010 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +/* To use these macros you must have a structure containing a next and + prev pointer */ + +#ifndef _DLINKLIST_H +#define _DLINKLIST_H + +/* + February 2010 - changed list format to have a prev pointer from the + list head. This makes DLIST_ADD_END() O(1) even though we only have + one list pointer. + + The scheme is as follows: + + 1) with no entries in the list: + list_head == NULL + + 2) with 1 entry in the list: + list_head->next == NULL + list_head->prev == list_head + + 3) with 2 entries in the list: + list_head->next == element2 + list_head->prev == element2 + element2->prev == list_head + element2->next == NULL + + 4) with N entries in the list: + list_head->next == element2 + list_head->prev == elementN + elementN->prev == element{N-1} + elementN->next == NULL + + This allows us to find the tail of the list by using + list_head->prev, which means we can add to the end of the list in + O(1) time + + + Note that the 'type' arguments below are no longer needed, but + are kept for now to prevent an incompatible argument change + */ + + +/* + add an element at the front of a list +*/ +#define DLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (p)->prev = (list) = (p); \ + (p)->next = NULL; \ + } else { \ + (p)->prev = (list)->prev; \ + (list)->prev = (p); \ + (p)->next = (list); \ + (list) = (p); \ + } \ +} while (0) + +/* + remove an element from a list + Note that the element doesn't have to be in the list. If it + isn't then this is a no-op +*/ +#define DLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + (list) = (p)->next; \ + } else if ((list) && (p) == (list)->prev) { \ + (p)->prev->next = NULL; \ + (list)->prev = (p)->prev; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) != (list)) (p)->next = (p)->prev = NULL; \ +} while (0) + +/* + find the head of the list given any element in it. + Note that this costs O(N), so you should avoid this macro + if at all possible! +*/ +#define DLIST_HEAD(p, result_head) \ +do { \ + (result_head) = (p); \ + while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \ +} while(0) + +/* return the last element in the list */ +#define DLIST_TAIL(list) ((list)?(list)->prev:NULL) + +/* return the previous element in the list. */ +#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL) + +/* insert 'p' after the given element 'el' in a list. If el is NULL then + this is the same as a DLIST_ADD() */ +#define DLIST_ADD_AFTER(list, p, el) \ +do { \ + if (!(list) || !(el)) { \ + DLIST_ADD(list, p); \ + } else { \ + (p)->prev = (el); \ + (p)->next = (el)->next; \ + (el)->next = (p); \ + if ((p)->next) (p)->next->prev = (p); \ + if ((list)->prev == (el)) (list)->prev = (p); \ + }\ +} while (0) + + +/* + add to the end of a list. + Note that 'type' is ignored +*/ +#define DLIST_ADD_END(list, p, type) \ +do { \ + if (!(list)) { \ + DLIST_ADD(list, p); \ + } else { \ + DLIST_ADD_AFTER(list, p, (list)->prev); \ + } \ +} while (0) + +/* promote an element to the from of a list */ +#define DLIST_PROMOTE(list, p) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD(list, p); \ +} while (0) + +/* + demote an element to the end of a list. + Note that 'type' is ignored +*/ +#define DLIST_DEMOTE(list, p, type) \ +do { \ + DLIST_REMOVE(list, p); \ + DLIST_ADD_END(list, p, NULL); \ +} while (0) + +/* + concatenate two lists - putting all elements of the 2nd list at the + end of the first list. + Note that 'type' is ignored +*/ +#define DLIST_CONCATENATE(list1, list2, type) \ +do { \ + if (!(list1)) { \ + (list1) = (list2); \ + } else { \ + (list1)->prev->next = (list2); \ + if (list2) { \ + void *_tmplist = (void *)(list1)->prev; \ + (list1)->prev = (list2)->prev; \ + (list2)->prev = _tmplist; \ + } \ + } \ +} while (0) + +#endif /* _DLINKLIST_H */ diff --git a/ctdb/lib/util/fault.c b/ctdb/lib/util/fault.c new file mode 100644 index 00000000000..3dddd0ecb28 --- /dev/null +++ b/ctdb/lib/util/fault.c @@ -0,0 +1,238 @@ +/* + Unix SMB/CIFS implementation. + Critical Fault handling + Copyright (C) Andrew Tridgell 1992-1998 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/wait.h" +#include "system/filesys.h" + +/** + * @file + * @brief Fault handling + */ + +/* the registered fault handler */ +static struct { + const char *name; + void (*fault_handler)(int sig); +} fault_handlers; + +static const char *progname; + +#ifdef HAVE_BACKTRACE +#include <execinfo.h> +#elif HAVE_LIBEXC_H +#include <libexc.h> +#endif + +/** + * Write backtrace to debug log + */ +_PUBLIC_ void call_backtrace(void) +{ +#ifdef HAVE_BACKTRACE +#ifndef BACKTRACE_STACK_SIZE +#define BACKTRACE_STACK_SIZE 64 +#endif + void *backtrace_stack[BACKTRACE_STACK_SIZE]; + size_t backtrace_size; + char **backtrace_strings; + + /* get the backtrace (stack frames) */ + backtrace_size = backtrace(backtrace_stack,BACKTRACE_STACK_SIZE); + backtrace_strings = backtrace_symbols(backtrace_stack, backtrace_size); + + DEBUG(0, ("BACKTRACE: %lu stack frames:\n", + (unsigned long)backtrace_size)); + + if (backtrace_strings) { + int i; + + for (i = 0; i < backtrace_size; i++) + DEBUGADD(0, (" #%u %s\n", i, backtrace_strings[i])); + + /* Leak the backtrace_strings, rather than risk what free() might do */ + } + +#elif HAVE_LIBEXC + +#define NAMESIZE 32 /* Arbitrary */ +#ifndef BACKTRACE_STACK_SIZE +#define BACKTRACE_STACK_SIZE 64 +#endif + + /* The IRIX libexc library provides an API for unwinding the stack. See + * libexc(3) for details. Apparantly trace_back_stack leaks memory, but + * since we are about to abort anyway, it hardly matters. + * + * Note that if we paniced due to a SIGSEGV or SIGBUS (or similar) this + * will fail with a nasty message upon failing to open the /proc entry. + */ + { + uint64_t addrs[BACKTRACE_STACK_SIZE]; + char * names[BACKTRACE_STACK_SIZE]; + char namebuf[BACKTRACE_STACK_SIZE * NAMESIZE]; + + int i; + int levels; + + ZERO_ARRAY(addrs); + ZERO_ARRAY(names); + ZERO_ARRAY(namebuf); + + for (i = 0; i < BACKTRACE_STACK_SIZE; i++) { + names[i] = namebuf + (i * NAMESIZE); + } + + levels = trace_back_stack(0, addrs, names, + BACKTRACE_STACK_SIZE, NAMESIZE); + + DEBUG(0, ("BACKTRACE: %d stack frames:\n", levels)); + for (i = 0; i < levels; i++) { + DEBUGADD(0, (" #%d 0x%llx %s\n", i, addrs[i], names[i])); + } + } +#undef NAMESIZE +#else + DEBUG(0, ("call_backtrace: not implemented\n")); +#endif +} + +_PUBLIC_ const char *panic_action = NULL; +_PUBLIC_ void (*pre_panic_action_hook)(void) = NULL; +_PUBLIC_ void (*post_panic_action_hook)(void) = NULL; + +/** + Something really nasty happened - panic ! +**/ +_PUBLIC_ void smb_panic(const char *why) +{ + int result; + + if (panic_action && *panic_action) { + char pidstr[20]; + char cmdstring[200]; + strlcpy(cmdstring, panic_action, sizeof(cmdstring)); + snprintf(pidstr, sizeof(pidstr), "%u", getpid()); + all_string_sub(cmdstring, "%PID%", pidstr, sizeof(cmdstring)); + if (progname) { + all_string_sub(cmdstring, "%PROG%", progname, sizeof(cmdstring)); + } + DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmdstring)); + + if (pre_panic_action_hook) { + pre_panic_action_hook(); + } + + result = system(cmdstring); + + if (post_panic_action_hook) { + post_panic_action_hook(); + } + + if (result == -1) + DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n", + strerror(errno))); + else + DEBUG(0, ("smb_panic(): action returned status %d\n", + WEXITSTATUS(result))); + } + DEBUG(0,("PANIC: %s\n", why)); + + call_backtrace(); + +#ifdef SIGABRT + CatchSignal(SIGABRT, SIG_DFL); +#endif + abort(); +} + +/** +report a fault +**/ +_NORETURN_ static void fault_report(int sig) +{ + static int counter; + + if (counter) _exit(1); + + DEBUG(0,("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n")); + DEBUG(0,("INTERNAL ERROR: Signal %d in %s pid %d",sig, progname, (int)getpid())); + DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n")); + DEBUG(0,("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n")); + + smb_panic("internal error"); + + exit(1); +} + +/** +catch serious errors +**/ +_NORETURN_ static void sig_fault(int sig) +{ + if (fault_handlers.fault_handler) { + /* we have a fault handler, call it. It may not return. */ + fault_handlers.fault_handler(sig); + } + /* If it returns or doesn't exist, use regular reporter */ + fault_report(sig); +} + +/** +setup our fault handlers +**/ +_PUBLIC_ void fault_setup(const char *pname) +{ + if (progname == NULL) { + progname = pname; + } +#ifdef SIGSEGV + CatchSignal(SIGSEGV, sig_fault); +#endif +#ifdef SIGBUS + CatchSignal(SIGBUS, sig_fault); +#endif +#ifdef SIGABRT + CatchSignal(SIGABRT, sig_fault); +#endif +#ifdef SIGFPE + CatchSignal(SIGFPE, sig_fault); +#endif +} + +/** + register a fault handler. + Should only be called once in the execution of smbd. +*/ +_PUBLIC_ bool register_fault_handler(const char *name, + void (*fault_handler)(int sig)) +{ + if (fault_handlers.name != NULL) { + /* it's already registered! */ + DEBUG(2,("fault handler '%s' already registered - failed '%s'\n", + fault_handlers.name, name)); + return false; + } + + fault_handlers.name = name; + fault_handlers.fault_handler = fault_handler; + + DEBUG(2,("fault handler '%s' registered\n", name)); + return true; +} diff --git a/ctdb/lib/util/fault.m4 b/ctdb/lib/util/fault.m4 new file mode 100644 index 00000000000..da077af31da --- /dev/null +++ b/ctdb/lib/util/fault.m4 @@ -0,0 +1,15 @@ +AC_CHECK_HEADERS(execinfo.h) +AC_SEARCH_LIBS_EXT(backtrace, [execinfo], EXECINFO_LIBS) +AC_CHECK_FUNC_EXT(backtrace, $EXECINFO_LIBS) + + +if test x"$ac_cv_header_execinfo_h" = x"yes" -a x"$ac_cv_func_ext_backtrace" = x"yes";then + SMB_ENABLE(EXECINFO, YES) + EXECINFO_CFLAGS="$CFLAGS" + EXECINFO_CPPFLAGS="$CPPFLAGS" + EXECINFO_LDFLAGS="$LDFLAGS" +else + SMB_ENABLE(EXECINFO,NO) +fi + +SMB_EXT_LIB(EXECINFO, [${EXECINFO_LIBS}], [${EXECINFO_CFLAGS}], [${EXECINFO_CPPFLAGS}], [${EXECINFO_LDFLAGS}]) diff --git a/ctdb/lib/util/idtree.c b/ctdb/lib/util/idtree.c new file mode 100644 index 00000000000..09dc237f83c --- /dev/null +++ b/ctdb/lib/util/idtree.c @@ -0,0 +1,387 @@ +/* + Unix SMB/CIFS implementation. + + very efficient functions to manage mapping a id (such as a fnum) to + a pointer. This is used for fnum and search id allocation. + + Copyright (C) Andrew Tridgell 2004 + + This code is derived from lib/idr.c in the 2.6 Linux kernel, which was + written by Jim Houston jim.houston@ccur.com, and is + Copyright (C) 2002 by Concurrent Computer 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, see <http://www.gnu.org/licenses/>. +*/ + +/* + see the section marked "public interface" below for documentation +*/ + +/** + * @file + */ + +#include "includes.h" + +#define IDR_BITS 5 +#define IDR_FULL 0xfffffffful +#if 0 /* unused */ +#define TOP_LEVEL_FULL (IDR_FULL >> 30) +#endif +#define IDR_SIZE (1 << IDR_BITS) +#define IDR_MASK ((1 << IDR_BITS)-1) +#define MAX_ID_SHIFT (sizeof(int)*8 - 1) +#define MAX_ID_BIT (1U << MAX_ID_SHIFT) +#define MAX_ID_MASK (MAX_ID_BIT - 1) +#define MAX_LEVEL (MAX_ID_SHIFT + IDR_BITS - 1) / IDR_BITS +#define IDR_FREE_MAX MAX_LEVEL + MAX_LEVEL + +#define set_bit(bit, v) (v) |= (1<<(bit)) +#define clear_bit(bit, v) (v) &= ~(1<<(bit)) +#define test_bit(bit, v) ((v) & (1<<(bit))) + +struct idr_layer { + uint32_t bitmap; + struct idr_layer *ary[IDR_SIZE]; + int count; +}; + +struct idr_context { + struct idr_layer *top; + struct idr_layer *id_free; + int layers; + int id_free_cnt; +}; + +static struct idr_layer *alloc_layer(struct idr_context *idp) +{ + struct idr_layer *p; + + if (!(p = idp->id_free)) + return NULL; + idp->id_free = p->ary[0]; + idp->id_free_cnt--; + p->ary[0] = NULL; + return p; +} + +static int find_next_bit(uint32_t bm, int maxid, int n) +{ + while (n<maxid && !test_bit(n, bm)) n++; + return n; +} + +static void free_layer(struct idr_context *idp, struct idr_layer *p) +{ + p->ary[0] = idp->id_free; + idp->id_free = p; + idp->id_free_cnt++; +} + +static int idr_pre_get(struct idr_context *idp) +{ + while (idp->id_free_cnt < IDR_FREE_MAX) { + struct idr_layer *new = talloc_zero(idp, struct idr_layer); + if(new == NULL) + return (0); + free_layer(idp, new); + } + return 1; +} + +static int sub_alloc(struct idr_context *idp, void *ptr, int *starting_id) +{ + int n, m, sh; + struct idr_layer *p, *new; + struct idr_layer *pa[MAX_LEVEL+1]; + unsigned int l, id, oid; + uint32_t bm; + + memset(pa, 0, sizeof(pa)); + + id = *starting_id; +restart: + p = idp->top; + l = idp->layers; + pa[l--] = NULL; + while (1) { + /* + * We run around this while until we reach the leaf node... + */ + n = (id >> (IDR_BITS*l)) & IDR_MASK; + bm = ~p->bitmap; + m = find_next_bit(bm, IDR_SIZE, n); + if (m == IDR_SIZE) { + /* no space available go back to previous layer. */ + l++; + oid = id; + id = (id | ((1 << (IDR_BITS*l))-1)) + 1; + + /* if already at the top layer, we need to grow */ + if (!(p = pa[l])) { + *starting_id = id; + return -2; + } + + /* If we need to go up one layer, continue the + * loop; otherwise, restart from the top. + */ + sh = IDR_BITS * (l + 1); + if (oid >> sh == id >> sh) + continue; + else + goto restart; + } + if (m != n) { + sh = IDR_BITS*l; + id = ((id >> sh) ^ n ^ m) << sh; + } + if ((id >= MAX_ID_BIT) || (id < 0)) + return -1; + if (l == 0) + break; + /* + * Create the layer below if it is missing. + */ + if (!p->ary[m]) { + if (!(new = alloc_layer(idp))) + return -1; + p->ary[m] = new; + p->count++; + } + pa[l--] = p; + p = p->ary[m]; + } + /* + * We have reached the leaf node, plant the + * users pointer and return the raw id. + */ + p->ary[m] = (struct idr_layer *)ptr; + set_bit(m, p->bitmap); + p->count++; + /* + * If this layer is full mark the bit in the layer above + * to show that this part of the radix tree is full. + * This may complete the layer above and require walking + * up the radix tree. + */ + n = id; + while (p->bitmap == IDR_FULL) { + if (!(p = pa[++l])) + break; + n = n >> IDR_BITS; + set_bit((n & IDR_MASK), p->bitmap); + } + return(id); +} + +static int idr_get_new_above_int(struct idr_context *idp, void *ptr, int starting_id) +{ + struct idr_layer *p, *new; + int layers, v, id; + + idr_pre_get(idp); + + id = starting_id; +build_up: + p = idp->top; + layers = idp->layers; + if (!p) { + if (!(p = alloc_layer(idp))) + return -1; + layers = 1; + } + /* + * Add a new layer to the top of the tree if the requested + * id is larger than the currently allocated space. + */ + while ((layers < MAX_LEVEL) && (id >= (1 << (layers*IDR_BITS)))) { + layers++; + if (!p->count) + continue; + if (!(new = alloc_layer(idp))) { + /* + * The allocation failed. If we built part of + * the structure tear it down. + */ + for (new = p; p && p != idp->top; new = p) { + p = p->ary[0]; + new->ary[0] = NULL; + new->bitmap = new->count = 0; + free_layer(idp, new); + } + return -1; + } + new->ary[0] = p; + new->count = 1; + if (p->bitmap == IDR_FULL) + set_bit(0, new->bitmap); + p = new; + } + idp->top = p; + idp->layers = layers; + v = sub_alloc(idp, ptr, &id); + if (v == -2) + goto build_up; + return(v); +} + +static int sub_remove(struct idr_context *idp, int shift, int id) +{ + struct idr_layer *p = idp->top; + struct idr_layer **pa[1+MAX_LEVEL]; + struct idr_layer ***paa = &pa[0]; + int n; + + *paa = NULL; + *++paa = &idp->top; + + while ((shift > 0) && p) { + n = (id >> shift) & IDR_MASK; + clear_bit(n, p->bitmap); + *++paa = &p->ary[n]; + p = p->ary[n]; + shift -= IDR_BITS; + } + n = id & IDR_MASK; + if (p != NULL && test_bit(n, p->bitmap)) { + clear_bit(n, p->bitmap); + p->ary[n] = NULL; + while(*paa && ! --((**paa)->count)){ + free_layer(idp, **paa); + **paa-- = NULL; + } + if ( ! *paa ) + idp->layers = 0; + return 0; + } + return -1; +} + +static void *_idr_find(struct idr_context *idp, int id) +{ + int n; + struct idr_layer *p; + + n = idp->layers * IDR_BITS; + p = idp->top; + /* + * This tests to see if bits outside the current tree are + * present. If so, tain't one of ours! + */ + if (n + IDR_BITS < 31 && + ((id & ~(~0 << MAX_ID_SHIFT)) >> (n + IDR_BITS))) { + return NULL; + } + + /* Mask off upper bits we don't use for the search. */ + id &= MAX_ID_MASK; + + while (n >= IDR_BITS && p) { + n -= IDR_BITS; + p = p->ary[(id >> n) & IDR_MASK]; + } + return((void *)p); +} + +static int _idr_remove(struct idr_context *idp, int id) +{ + struct idr_layer *p; + + /* Mask off upper bits we don't use for the search. */ + id &= MAX_ID_MASK; + + if (sub_remove(idp, (idp->layers - 1) * IDR_BITS, id) == -1) { + return -1; + } + + if ( idp->top && idp->top->count == 1 && + (idp->layers > 1) && + idp->top->ary[0]) { + /* We can drop a layer */ + p = idp->top->ary[0]; + idp->top->bitmap = idp->top->count = 0; + free_layer(idp, idp->top); + idp->top = p; + --idp->layers; + } + while (idp->id_free_cnt >= IDR_FREE_MAX) { + p = alloc_layer(idp); + talloc_free(p); + } + return 0; +} + +/************************************************************************ + this is the public interface +**************************************************************************/ + +/** + initialise a idr tree. The context return value must be passed to + all subsequent idr calls. To destroy the idr tree use talloc_free() + on this context + */ +_PUBLIC_ struct idr_context *idr_init(TALLOC_CTX *mem_ctx) +{ + return talloc_zero(mem_ctx, struct idr_context); +} + +/** + allocate the next available id, and assign 'ptr' into its slot. + you can retrieve later this pointer using idr_find() +*/ +_PUBLIC_ int idr_get_new(struct idr_context *idp, void *ptr, int limit) +{ + int ret = idr_get_new_above_int(idp, ptr, 0); + if (ret > limit) { + idr_remove(idp, ret); + return -1; + } + return ret; +} + +/** + allocate a new id, giving the first available value greater than or + equal to the given starting id +*/ +_PUBLIC_ int idr_get_new_above(struct idr_context *idp, void *ptr, int starting_id, int limit) +{ + int ret = idr_get_new_above_int(idp, ptr, starting_id); + if (ret > limit) { + idr_remove(idp, ret); + return -1; + } + return ret; +} + +/** + find a pointer value previously set with idr_get_new given an id +*/ +_PUBLIC_ void *idr_find(struct idr_context *idp, int id) +{ + return _idr_find(idp, id); +} + +/** + remove an id from the idr tree +*/ +_PUBLIC_ int idr_remove(struct idr_context *idp, int id) +{ + int ret; + ret = _idr_remove((struct idr_context *)idp, id); + if (ret != 0) { + DEBUG(0,("WARNING: attempt to remove unset id %d in idtree\n", id)); + } + return ret; +} diff --git a/ctdb/lib/util/signal.c b/ctdb/lib/util/signal.c new file mode 100644 index 00000000000..ead947eb5e6 --- /dev/null +++ b/ctdb/lib/util/signal.c @@ -0,0 +1,144 @@ +/* + Unix SMB/CIFS implementation. + signal handling functions + + Copyright (C) Andrew Tridgell 1998 + + 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/wait.h" + +/** + * @file + * @brief Signal handling + */ + +/**************************************************************************** + Catch child exits and reap the child zombie status. +****************************************************************************/ + +static void sig_cld(int signum) +{ + while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0) + ; + + /* + * Turns out it's *really* important not to + * restore the signal handler here if we have real POSIX + * signal handling. If we do, then we get the signal re-delivered + * immediately - hey presto - instant loop ! JRA. + */ + +#if !defined(HAVE_SIGACTION) + CatchSignal(SIGCLD, sig_cld); +#endif +} + +/**************************************************************************** +catch child exits - leave status; +****************************************************************************/ + +static void sig_cld_leave_status(int signum) +{ + /* + * Turns out it's *really* important not to + * restore the signal handler here if we have real POSIX + * signal handling. If we do, then we get the signal re-delivered + * immediately - hey presto - instant loop ! JRA. + */ + +#if !defined(HAVE_SIGACTION) + CatchSignal(SIGCLD, sig_cld_leave_status); +#endif +} + +/** + Block sigs. +**/ + +void BlockSignals(bool block, int signum) +{ +#ifdef HAVE_SIGPROCMASK + sigset_t set; + sigemptyset(&set); + sigaddset(&set,signum); + sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK,&set,NULL); +#elif defined(HAVE_SIGBLOCK) + if (block) { + sigblock(sigmask(signum)); + } else { + sigsetmask(siggetmask() & ~sigmask(signum)); + } +#else + /* yikes! This platform can't block signals? */ + static int done; + if (!done) { + DEBUG(0,("WARNING: No signal blocking available\n")); + done=1; + } +#endif +} + +/** + Catch a signal. This should implement the following semantics: + + 1) The handler remains installed after being called. + 2) The signal should be blocked during handler execution. +**/ + +void (*CatchSignal(int signum,void (*handler)(int )))(int) +{ +#ifdef HAVE_SIGACTION + struct sigaction act; + struct sigaction oldact; + + ZERO_STRUCT(act); + + act.sa_handler = handler; +#ifdef SA_RESTART + /* + * We *want* SIGALRM to interrupt a system call. + */ + if(signum != SIGALRM) + act.sa_flags = SA_RESTART; +#endif + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,signum); + sigaction(signum,&act,&oldact); + return oldact.sa_handler; +#else /* !HAVE_SIGACTION */ + /* FIXME: need to handle sigvec and systems with broken signal() */ + return signal(signum, handler); +#endif +} + +/** + Ignore SIGCLD via whatever means is necessary for this OS. +**/ + +void CatchChild(void) +{ + CatchSignal(SIGCLD, sig_cld); +} + +/** + Catch SIGCLD but leave the child around so it's status can be reaped. +**/ + +void CatchChildLeaveStatus(void) +{ + CatchSignal(SIGCLD, sig_cld_leave_status); +} diff --git a/ctdb/lib/util/signal.m4 b/ctdb/lib/util/signal.m4 new file mode 100644 index 00000000000..c6d7f72f683 --- /dev/null +++ b/ctdb/lib/util/signal.m4 @@ -0,0 +1 @@ +AC_CHECK_FUNCS(sigprocmask sigblock sigaction) diff --git a/ctdb/lib/util/strlist.c b/ctdb/lib/util/strlist.c new file mode 100644 index 00000000000..48a6e45acea --- /dev/null +++ b/ctdb/lib/util/strlist.c @@ -0,0 +1,52 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 2005 + Copyright (C) Jelmer Vernooij 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/locale.h" + +/** + return the number of elements in a string list +*/ +_PUBLIC_ size_t str_list_length(const char **list) +{ + size_t ret; + for (ret=0;list && list[ret];ret++) /* noop */ ; + return ret; +} + + +/** + add an entry to a string list +*/ +_PUBLIC_ const char **str_list_add(const char **list, const char *s) +{ + size_t len = str_list_length(list); + const char **ret; + + ret = talloc_realloc(NULL, list, const char *, len+2); + if (ret == NULL) return NULL; + + ret[len] = talloc_strdup(ret, s); + if (ret[len] == NULL) return NULL; + + ret[len+1] = NULL; + + return ret; +} diff --git a/ctdb/lib/util/substitute.c b/ctdb/lib/util/substitute.c new file mode 100644 index 00000000000..32945a72134 --- /dev/null +++ b/ctdb/lib/util/substitute.c @@ -0,0 +1,167 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 2001-2002 + Copyright (C) Martin Pool 2003 + Copyright (C) James Peach 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/** + * @file + * @brief Substitute utilities. + **/ + +/** + Substitute a string for a pattern in another string. Make sure there is + enough room! + + This routine looks for pattern in s and replaces it with + insert. It may do multiple replacements. + + Any of " ; ' $ or ` in the insert string are replaced with _ + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ + +_PUBLIC_ void string_sub(char *s, const char *pattern, const char *insert, size_t len) +{ + char *p; + ssize_t ls, lp, li, i; + + if (!insert || !pattern || !*pattern || !s) + return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (len == 0) + len = ls + 1; /* len is number of *bytes* */ + + while (lp <= ls && (p = strstr(s, pattern))) { + if (ls + (li-lp) >= len) { + DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n", + (int)(ls + (li-lp) - len), + pattern, (int)len)); + break; + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + for (i=0;i<li;i++) { + switch (insert[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + p[i] = '_'; + break; + default: + p[i] = insert[i]; + } + } + s = p + li; + ls += (li-lp); + } +} + +/** + * Talloc'ed version of string_sub + */ +_PUBLIC_ char *string_sub_talloc(TALLOC_CTX *mem_ctx, const char *s, + const char *pattern, const char *insert) +{ + const char *p; + char *ret; + size_t len, alloc_len; + + if (insert == NULL || pattern == NULL || !*pattern || s == NULL) + return NULL; + + /* determine length needed */ + len = strlen(s); + + for (p = strstr(s, pattern); p != NULL; + p = strstr(p+strlen(pattern), pattern)) { + len += strlen(insert) - strlen(pattern); + } + + alloc_len = MAX(len, strlen(s))+1; + ret = talloc_array(mem_ctx, char, alloc_len); + if (ret == NULL) + return NULL; + strncpy(ret, s, alloc_len); + string_sub(ret, pattern, insert, alloc_len); + + ret = talloc_realloc(mem_ctx, ret, char, len+1); + if (ret == NULL) + return NULL; + + SMB_ASSERT(ret[len] == '\0'); + + talloc_set_name_const(ret, ret); + + return ret; +} + +/** + Similar to string_sub() but allows for any character to be substituted. + Use with caution! + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ + +_PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, size_t len) +{ + char *p; + ssize_t ls,lp,li; + + if (!insert || !pattern || !s) + return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (!*pattern) + return; + + if (len == 0) + len = ls + 1; /* len is number of *bytes* */ + + while (lp <= ls && (p = strstr(s,pattern))) { + if (ls + (li-lp) >= len) { + DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n", + (int)(ls + (li-lp) - len), + pattern, (int)len)); + break; + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + memcpy(p, insert, li); + s = p + li; + ls += (li-lp); + } +} diff --git a/ctdb/lib/util/util.c b/ctdb/lib/util/util.c new file mode 100644 index 00000000000..af528059954 --- /dev/null +++ b/ctdb/lib/util/util.c @@ -0,0 +1,52 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" + + +/** + Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, + else + if SYSV use O_NDELAY + if BSD use FNDELAY +**/ + +_PUBLIC_ int set_blocking(int fd, bool set) +{ + int val; +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return -1; + if(set) /* Turn blocking on - ie. clear nonblock flag */ + val &= ~FLAG_TO_SET; + else + val |= FLAG_TO_SET; + return fcntl( fd, F_SETFL, val); +#undef FLAG_TO_SET +} diff --git a/ctdb/lib/util/util.h b/ctdb/lib/util/util.h new file mode 100644 index 00000000000..467fba88ad6 --- /dev/null +++ b/ctdb/lib/util/util.h @@ -0,0 +1,651 @@ +/* + Unix SMB/CIFS implementation. + Utility functions for Samba + Copyright (C) Andrew Tridgell 1992-1999 + Copyright (C) Jelmer Vernooij 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _SAMBA_UTIL_H_ +#define _SAMBA_UTIL_H_ + +/** + * @file + * @brief Helpful macros + */ + +struct smbsrv_tcon; + +#ifdef _SAMBA_BUILD_ +extern const char *logfile; +#endif +extern const char *panic_action; +extern void (*pre_panic_action_hook)(void); +extern void (*post_panic_action_hook)(void); + +/** + * assert macros + */ +#ifdef DEVELOPER +#define SMB_ASSERT(b) do { if (!(b)) { \ + DEBUG(0,("PANIC: assert failed at %s(%d): %s\n", \ + __FILE__, __LINE__, #b)); smb_panic("assert failed: " #b); }} while(0) +#else +/* redefine the assert macro for non-developer builds */ +#define SMB_ASSERT(b) do { if (!(b)) { \ + DEBUG(0,("PANIC: assert failed at %s(%d): %s\n", \ + __FILE__, __LINE__, #b)); }} while (0) +#endif + +#if _SAMBA_BUILD_ == 4 +#ifdef VALGRIND +#define strlen(x) valgrind_strlen(x) +size_t valgrind_strlen(const char *s); +#endif +#endif + +#ifndef ABS +#define ABS(a) ((a)>0?(a):(-(a))) +#endif + +/** + * Write backtrace to debug log + */ +_PUBLIC_ void call_backtrace(void); + +/** + Something really nasty happened - panic ! +**/ +_PUBLIC_ _NORETURN_ void smb_panic(const char *why); + +/** +setup our fault handlers +**/ +_PUBLIC_ void fault_setup(const char *pname); + +/** + register a fault handler. + Should only be called once in the execution of smbd. +*/ +_PUBLIC_ bool register_fault_handler(const char *name, void (*fault_handler)(int sig)); + +/* The following definitions come from lib/util/signal.c */ + + +/** + Block sigs. +**/ +void BlockSignals(bool block, int signum); + +/** + Catch a signal. This should implement the following semantics: + + 1) The handler remains installed after being called. + 2) The signal should be blocked during handler execution. +**/ +void (*CatchSignal(int signum,void (*handler)(int )))(int); + +/** + Ignore SIGCLD via whatever means is necessary for this OS. +**/ +void CatchChild(void); + +/** + Catch SIGCLD but leave the child around so it's status can be reaped. +**/ +void CatchChildLeaveStatus(void); + + +/* The following definitions come from lib/util/util_str.c */ + + +/** + Trim the specified elements off the front and back of a string. +**/ +_PUBLIC_ bool trim_string(char *s, const char *front, const char *back); + +/** + Find the number of 'c' chars in a string +**/ +_PUBLIC_ _PURE_ size_t count_chars(const char *s, char c); + +/** + Safe string copy into a known length string. maxlength does not + include the terminating zero. +**/ +_PUBLIC_ char *safe_strcpy(char *dest,const char *src, size_t maxlength); + +/** + Safe string cat into a string. maxlength does not + include the terminating zero. +**/ +_PUBLIC_ char *safe_strcat(char *dest, const char *src, size_t maxlength); + +/** + Routine to get hex characters and turn them into a 16 byte array. + the array can be variable length, and any non-hex-numeric + characters are skipped. "0xnn" or "0Xnn" is specially catered + for. + + valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n" + + +**/ +_PUBLIC_ size_t strhex_to_str(char *p, size_t p_len, const char *strhex, size_t strhex_len); + +#ifdef _SAMBA_BUILD_ +/** + * Parse a hex string and return a data blob. + */ +_PUBLIC_ _PURE_ DATA_BLOB strhex_to_data_blob(TALLOC_CTX *mem_ctx, const char *strhex) ; +#endif + +/** + * Routine to print a buffer as HEX digits, into an allocated string. + */ +_PUBLIC_ void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer); + +/** + * talloc version of hex_encode() + */ +_PUBLIC_ char *hex_encode_talloc(TALLOC_CTX *mem_ctx, const unsigned char *buff_in, size_t len); + +/** + Substitute a string for a pattern in another string. Make sure there is + enough room! + + This routine looks for pattern in s and replaces it with + insert. It may do multiple replacements. + + Any of " ; ' $ or ` in the insert string are replaced with _ + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ +_PUBLIC_ void string_sub(char *s,const char *pattern, const char *insert, size_t len); + + +_PUBLIC_ char *string_sub_talloc(TALLOC_CTX *mem_ctx, const char *s, + const char *pattern, const char *insert); + +/** + Similar to string_sub() but allows for any character to be substituted. + Use with caution! + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ +_PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, size_t len); + +/** + Unescape a URL encoded string, in place. +**/ +_PUBLIC_ void rfc1738_unescape(char *buf); + +/** + format a string into length-prefixed dotted domain format, as used in NBT + and in some ADS structures +**/ +_PUBLIC_ const char *str_format_nbt_domain(TALLOC_CTX *mem_ctx, const char *s); + +/** + * Add a string to an array of strings. + * + * num should be a pointer to an integer that holds the current + * number of elements in strings. It will be updated by this function. + */ +_PUBLIC_ bool add_string_to_array(TALLOC_CTX *mem_ctx, + const char *str, const char ***strings, int *num); + +/** + varient of strcmp() that handles NULL ptrs +**/ +_PUBLIC_ int strcmp_safe(const char *s1, const char *s2); + +/** +return the number of bytes occupied by a buffer in ASCII format +the result includes the null termination +limited by 'n' bytes +**/ +_PUBLIC_ size_t ascii_len_n(const char *src, size_t n); + +/** + Set a boolean variable from the text value stored in the passed string. + Returns true in success, false if the passed string does not correctly + represent a boolean. +**/ +_PUBLIC_ bool set_boolean(const char *boolean_string, bool *boolean); + +/** + * Parse a string containing a boolean value. + * + * val will be set to the read value. + * + * @retval true if a boolean value was parsed, false otherwise. + */ +_PUBLIC_ bool conv_str_bool(const char * str, bool * val); + +#if _SAMBA_BUILD_ == 4 +/** + * Convert a size specification like 16K into an integral number of bytes. + **/ +_PUBLIC_ bool conv_str_size(const char * str, uint64_t * val); +#endif + +/** + * Parse a uint64_t value from a string + * + * val will be set to the value read. + * + * @retval true if parsing was successful, false otherwise + */ +_PUBLIC_ bool conv_str_u64(const char * str, uint64_t * val); + +/** +return the number of bytes occupied by a buffer in CH_UTF16 format +the result includes the null termination +**/ +_PUBLIC_ size_t utf16_len(const void *buf); + +/** +return the number of bytes occupied by a buffer in CH_UTF16 format +the result includes the null termination +limited by 'n' bytes +**/ +_PUBLIC_ size_t utf16_len_n(const void *src, size_t n); +_PUBLIC_ size_t ucs2_align(const void *base_ptr, const void *p, int flags); + +/** +Do a case-insensitive, whitespace-ignoring string compare. +**/ +_PUBLIC_ int strwicmp(const char *psz1, const char *psz2); + +/** + String replace. +**/ +_PUBLIC_ void string_replace(char *s, char oldc, char newc); + +/** + * Compare 2 strings. + * + * @note The comparison is case-insensitive. + **/ +_PUBLIC_ bool strequal(const char *s1, const char *s2); + +/* The following definitions come from lib/util/util_strlist.c */ +#ifdef _SAMBA_BUILD_ + +/* separators for lists */ +#ifndef LIST_SEP +#define LIST_SEP " \t,\n\r" +#endif + +/** + build a null terminated list of strings from a input string and a + separator list. The separator list must contain characters less than + or equal to 0x2f for this to work correctly on multi-byte strings +*/ +_PUBLIC_ char **str_list_make(TALLOC_CTX *mem_ctx, const char *string, const char *sep); + +/** + * build a null terminated list of strings from an argv-like input string + * Entries are seperated by spaces and can be enclosed by quotes. + * Does NOT support escaping + */ +_PUBLIC_ const char **str_list_make_shell(TALLOC_CTX *mem_ctx, const char *string, const char *sep); + +/** + * join a list back to one string + */ +_PUBLIC_ char *str_list_join(TALLOC_CTX *mem_ctx, const char **list, char seperator); + +/** join a list back to one (shell-like) string; entries + * seperated by spaces, using quotes where necessary */ +_PUBLIC_ char *str_list_join_shell(TALLOC_CTX *mem_ctx, const char **list, char sep); + +/** + return the number of elements in a string list +*/ +_PUBLIC_ size_t str_list_length(const char * const *list); + +/** + copy a string list +*/ +_PUBLIC_ char **str_list_copy(TALLOC_CTX *mem_ctx, const char **list); + +/** + Return true if all the elements of the list match exactly. + */ +_PUBLIC_ bool str_list_equal(const char **list1, const char **list2); + +/** + add an entry to a string list +*/ +_PUBLIC_ const char **str_list_add(const char **list, const char *s); + +/** + remove an entry from a string list +*/ +_PUBLIC_ void str_list_remove(const char **list, const char *s); + +/** + return true if a string is in a list +*/ +_PUBLIC_ bool str_list_check(const char **list, const char *s); + +/** + return true if a string is in a list, case insensitively +*/ +_PUBLIC_ bool str_list_check_ci(const char **list, const char *s); +#endif + +/* The following definitions come from lib/util/util_file.c */ + + +#ifdef _SAMBA_BUILD_ +/** +read a line from a file with possible \ continuation chars. +Blanks at the start or end of a line are stripped. +The string will be allocated if s2 is NULL +**/ +_PUBLIC_ char *fgets_slash(char *s2,int maxlen,XFILE *f); +#endif + +/** + * Read one line (data until next newline or eof) and allocate it + */ +_PUBLIC_ char *afdgets(int fd, TALLOC_CTX *mem_ctx, size_t hint); + +#ifdef _SAMBA_BUILD_ +/** +load a file into memory from a fd. +**/ +_PUBLIC_ char *fd_load(int fd, size_t *size, size_t maxsize, TALLOC_CTX *mem_ctx); + + +char **file_lines_parse(char *p, size_t size, int *numlines, TALLOC_CTX *mem_ctx); + +/** +load a file into memory +**/ +_PUBLIC_ char *file_load(const char *fname, size_t *size, size_t maxsize, TALLOC_CTX *mem_ctx); +#endif + +/** +mmap (if possible) or read a file +**/ +_PUBLIC_ void *map_file(const char *fname, size_t size); + +#ifdef _SAMBA_BUILD_ +/** +load a file into memory and return an array of pointers to lines in the file +must be freed with talloc_free(). +**/ +_PUBLIC_ char **file_lines_load(const char *fname, int *numlines, size_t maxsize, TALLOC_CTX *mem_ctx); +#endif + +/** +load a fd into memory and return an array of pointers to lines in the file +must be freed with talloc_free(). If convert is true calls unix_to_dos on +the list. +**/ +_PUBLIC_ char **fd_lines_load(int fd, int *numlines, size_t maxsize, TALLOC_CTX *mem_ctx); + +/** +take a list of lines and modify them to produce a list where \ continues +a line +**/ +_PUBLIC_ void file_lines_slashcont(char **lines); + +/** + save a lump of data into a file. Mostly used for debugging +*/ +_PUBLIC_ bool file_save(const char *fname, const void *packet, size_t length); +_PUBLIC_ int vfdprintf(int fd, const char *format, va_list ap) PRINTF_ATTRIBUTE(2,0); +_PUBLIC_ int fdprintf(int fd, const char *format, ...) PRINTF_ATTRIBUTE(2,3); +_PUBLIC_ bool large_file_support(const char *path); + +/* The following definitions come from lib/util/util.c */ + + +/** + Find a suitable temporary directory. The result should be copied immediately + as it may be overwritten by a subsequent call. +**/ +_PUBLIC_ const char *tmpdir(void); + +/** + Check if a file exists - call vfs_file_exist for samba files. +**/ +_PUBLIC_ bool file_exist(const char *fname); + +/** + Check a files mod time. +**/ +_PUBLIC_ time_t file_modtime(const char *fname); + +/** + Check if a directory exists. +**/ +_PUBLIC_ bool directory_exist(const char *dname); + +/** + * Try to create the specified directory if it didn't exist. + * + * @retval true if the directory already existed and has the right permissions + * or was successfully created. + */ +_PUBLIC_ bool directory_create_or_exist(const char *dname, uid_t uid, + mode_t dir_perms); + +/** + Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, + else + if SYSV use O_NDELAY + if BSD use FNDELAY +**/ +_PUBLIC_ int set_blocking(int fd, bool set); + +/** + Sleep for a specified number of milliseconds. +**/ +_PUBLIC_ void msleep(unsigned int t); + +/** + Get my own name, return in malloc'ed storage. +**/ +_PUBLIC_ char* get_myname(void); + +/** + Return true if a string could be a pure IP address. +**/ +_PUBLIC_ bool is_ipaddress(const char *str); + +/** + Interpret an internet address or name into an IP address in 4 byte form. +**/ +_PUBLIC_ uint32_t interpret_addr(const char *str); + +/** + A convenient addition to interpret_addr(). +**/ +_PUBLIC_ struct in_addr interpret_addr2(const char *str); + +/** + Check if an IP is the 0.0.0.0. +**/ +_PUBLIC_ bool is_zero_ip_v4(struct in_addr ip); + +/** + Are two IPs on the same subnet? +**/ +_PUBLIC_ bool same_net_v4(struct in_addr ip1,struct in_addr ip2,struct in_addr mask); + +_PUBLIC_ bool is_ipaddress_v4(const char *str); + +/** + Check if a process exists. Does this work on all unixes? +**/ +_PUBLIC_ bool process_exists_by_pid(pid_t pid); + +/** + Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping + is dealt with in posix.c +**/ +_PUBLIC_ bool fcntl_lock(int fd, int op, off_t offset, off_t count, int type); + +/** + malloc that aborts with smb_panic on fail or zero size. +**/ +_PUBLIC_ void *smb_xmalloc(size_t size); + +/** + Memdup with smb_panic on fail. +**/ +_PUBLIC_ void *smb_xmemdup(const void *p, size_t size); + +/** + strdup that aborts on malloc fail. +**/ +_PUBLIC_ char *smb_xstrdup(const char *s); + +char *smb_xstrndup(const char *s, size_t n); + +/** + Like strdup but for memory. +**/ +_PUBLIC_ void *memdup(const void *p, size_t size); + +/** + * see if a range of memory is all zero. A NULL pointer is considered + * to be all zero + */ +_PUBLIC_ bool all_zero(const uint8_t *ptr, size_t size); + +/** + realloc an array, checking for integer overflow in the array size +*/ +_PUBLIC_ void *realloc_array(void *ptr, size_t el_size, unsigned count, bool free_on_fail); + +void *malloc_array(size_t el_size, unsigned int count); + +/* The following definitions come from lib/util/fsusage.c */ + + +/** + * Retrieve amount of free disk space. + * this does all of the system specific guff to get the free disk space. + * It is derived from code in the GNU fileutils package, but has been + * considerably mangled for use here + * + * results are returned in *dfree and *dsize, in 512 byte units +*/ +_PUBLIC_ int sys_fsusage(const char *path, uint64_t *dfree, uint64_t *dsize); + +/* The following definitions come from lib/util/ms_fnmatch.c */ + + +/** + * @file + * @brief MS-style Filename matching + */ + +#if _SAMBA_BUILD_ == 4 +/* protocol types. It assumes that higher protocols include lower protocols + as subsets. FIXME: Move to one of the smb-specific headers */ +enum protocol_types { + PROTOCOL_NONE, + PROTOCOL_CORE, + PROTOCOL_COREPLUS, + PROTOCOL_LANMAN1, + PROTOCOL_LANMAN2, + PROTOCOL_NT1, + PROTOCOL_SMB2 +}; + +int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol); + +/** a generic fnmatch function - uses for non-CIFS pattern matching */ +int gen_fnmatch(const char *pattern, const char *string); +#endif + +/* The following definitions come from lib/util/mutex.c */ + + +#ifdef _SAMBA_BUILD_ +/** + register a set of mutex/rwlock handlers. + Should only be called once in the execution of smbd. +*/ +_PUBLIC_ bool register_mutex_handlers(const char *name, struct mutex_ops *ops); +#endif + +/* The following definitions come from lib/util/idtree.c */ + + +/** + initialise a idr tree. The context return value must be passed to + all subsequent idr calls. To destroy the idr tree use talloc_free() + on this context + */ +_PUBLIC_ struct idr_context *idr_init(TALLOC_CTX *mem_ctx); + +/** + allocate the next available id, and assign 'ptr' into its slot. + you can retrieve later this pointer using idr_find() +*/ +_PUBLIC_ int idr_get_new(struct idr_context *idp, void *ptr, int limit); + +/** + allocate a new id, giving the first available value greater than or + equal to the given starting id +*/ +_PUBLIC_ int idr_get_new_above(struct idr_context *idp, void *ptr, int starting_id, int limit); + +/** + allocate a new id randomly in the given range +*/ +_PUBLIC_ int idr_get_new_random(struct idr_context *idp, void *ptr, int limit); + +/** + find a pointer value previously set with idr_get_new given an id +*/ +_PUBLIC_ void *idr_find(struct idr_context *idp, int id); + +/** + remove an id from the idr tree +*/ +_PUBLIC_ int idr_remove(struct idr_context *idp, int id); + +/* The following definitions come from lib/util/become_daemon.c */ + +#if _SAMBA_BUILD_ == 4 +/** + Become a daemon, discarding the controlling terminal. +**/ +_PUBLIC_ void become_daemon(bool fork); +#endif + +/** + * Load a ini-style file. + */ +bool pm_process( const char *fileName, + bool (*sfunc)(const char *, void *), + bool (*pfunc)(const char *, const char *, void *), + void *userdata); + +bool unmap_file(void *start, size_t size); + +#define CONST_DISCARD(type, ptr) ((type) ((void *) (ptr))) + +#endif /* _SAMBA_UTIL_H_ */ diff --git a/ctdb/lib/util/util_file.c b/ctdb/lib/util/util_file.c new file mode 100644 index 00000000000..3a90201cce7 --- /dev/null +++ b/ctdb/lib/util/util_file.c @@ -0,0 +1,132 @@ +/* + functions taken from samba4 for quick prototyping of ctdb. These are + not intended to remain part of ctdb +*/ + +#include "includes.h" +#include "system/filesys.h" + + +static char *fd_load(int fd, size_t *size, TALLOC_CTX *mem_ctx) +{ + struct stat sbuf; + char *p; + + if (fstat(fd, &sbuf) != 0) return NULL; + + p = (char *)talloc_size(mem_ctx, sbuf.st_size+1); + if (!p) return NULL; + + if (read(fd, p, sbuf.st_size) != sbuf.st_size) { + talloc_free(p); + return NULL; + } + p[sbuf.st_size] = 0; + + if (size) *size = sbuf.st_size; + + return p; +} + + +static char *file_load(const char *fname, size_t *size, TALLOC_CTX *mem_ctx) +{ + int fd; + char *p; + + if (!fname || !*fname) return NULL; + + fd = open(fname,O_RDONLY); + if (fd == -1) return NULL; + + p = fd_load(fd, size, mem_ctx); + + close(fd); + + return p; +} + + +/** +parse a buffer into lines +'p' will be freed on error, and otherwise will be made a child of the returned array +**/ +static char **file_lines_parse(char *p, size_t size, int *numlines, TALLOC_CTX *mem_ctx) +{ + int i; + char *s, **ret; + + if (!p) return NULL; + + for (s = p, i=0; s < p+size; s++) { + if (s[0] == '\n') i++; + } + + ret = talloc_array(mem_ctx, char *, i+2); + if (!ret) { + talloc_free(p); + return NULL; + } + + talloc_steal(ret, p); + + memset(ret, 0, sizeof(ret[0])*(i+2)); + if (numlines) *numlines = i; + + ret[0] = p; + for (s = p, i=0; s < p+size; s++) { + if (s[0] == '\n') { + s[0] = 0; + i++; + ret[i] = s+1; + } + if (s[0] == '\r') s[0] = 0; + } + + return ret; +} + + +/** +load a file into memory and return an array of pointers to lines in the file +must be freed with talloc_free(). +**/ +_PUBLIC_ char **file_lines_load(const char *fname, int *numlines, TALLOC_CTX *mem_ctx) +{ + char *p; + size_t size; + + p = file_load(fname, &size, mem_ctx); + if (!p) return NULL; + + return file_lines_parse(p, size, numlines, mem_ctx); +} + +char *hex_encode_talloc(TALLOC_CTX *mem_ctx, const unsigned char *buff_in, size_t len) +{ + int i; + char *hex_buffer; + + hex_buffer = talloc_array(mem_ctx, char, (len*2)+1); + + for (i = 0; i < len; i++) + slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]); + + return hex_buffer; +} + +uint8_t *hex_decode_talloc(TALLOC_CTX *mem_ctx, const char *hex_in, size_t *len) +{ + int i, num; + uint8_t *buffer; + + *len = strlen(hex_in) / 2; + buffer = talloc_array(mem_ctx, unsigned char, *len); + + for (i=0; i<*len; i++) { + sscanf(&hex_in[i*2], "%02X", &num); + buffer[i] = (uint8_t)num; + } + + return buffer; +} diff --git a/ctdb/lib/util/util_time.c b/ctdb/lib/util/util_time.c new file mode 100644 index 00000000000..be14f26458a --- /dev/null +++ b/ctdb/lib/util/util_time.c @@ -0,0 +1,108 @@ +/* + functions taken from samba4 for quick prototyping of ctdb. These are + not intended to remain part of ctdb +*/ + +#include "includes.h" +#include "system/time.h" +#include "system/filesys.h" + + +/** + return a zero timeval +*/ +struct timeval timeval_zero(void) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + return tv; +} + +/** + return True if a timeval is zero +*/ +bool timeval_is_zero(const struct timeval *tv) +{ + return tv->tv_sec == 0 && tv->tv_usec == 0; +} + +/** + return a timeval for the current time +*/ +struct timeval timeval_current(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return tv; +} + +double timeval_elapsed(struct timeval *tv) +{ + struct timeval tv2 = timeval_current(); + return (tv2.tv_sec - tv->tv_sec) + + (tv2.tv_usec - tv->tv_usec)*1.0e-6; +} + +double timeval_delta(struct timeval *tv2, struct timeval *tv) +{ + return (tv2->tv_sec - tv->tv_sec) + + (tv2->tv_usec - tv->tv_usec)*1.0e-6; +} + +/** + return a timeval struct with the given elements +*/ +_PUBLIC_ struct timeval timeval_set(uint32_t secs, uint32_t usecs) +{ + struct timeval tv; + tv.tv_sec = secs; + tv.tv_usec = usecs; + return tv; +} + +_PUBLIC_ int timeval_compare(const struct timeval *tv1, const struct timeval *tv2) +{ + if (tv1->tv_sec > tv2->tv_sec) return 1; + if (tv1->tv_sec < tv2->tv_sec) return -1; + if (tv1->tv_usec > tv2->tv_usec) return 1; + if (tv1->tv_usec < tv2->tv_usec) return -1; + return 0; +} + +_PUBLIC_ struct timeval timeval_until(const struct timeval *tv1, + const struct timeval *tv2) +{ + struct timeval t; + if (timeval_compare(tv1, tv2) >= 0) { + return timeval_zero(); + } + t.tv_sec = tv2->tv_sec - tv1->tv_sec; + if (tv1->tv_usec > tv2->tv_usec) { + t.tv_sec--; + t.tv_usec = 1000000 - (tv1->tv_usec - tv2->tv_usec); + } else { + t.tv_usec = tv2->tv_usec - tv1->tv_usec; + } + return t; +} + +static struct timeval timeval_add(const struct timeval *tv, + uint32_t secs, uint32_t usecs) +{ + struct timeval tv2 = *tv; + const unsigned int million = 1000000; + tv2.tv_sec += secs; + tv2.tv_usec += usecs; + tv2.tv_sec += tv2.tv_usec / million; + tv2.tv_usec = tv2.tv_usec % million; + return tv2; +} + + +_PUBLIC_ struct timeval timeval_current_ofs(uint32_t secs, uint32_t usecs) +{ + struct timeval tv = timeval_current(); + return timeval_add(&tv, secs, usecs); +} + |