lib/util: import fault/backtrace handling from samba.
authorStefan Metzmacher <metze@samba.org>
Fri, 18 Dec 2009 11:32:38 +0000 (12:32 +0100)
committerStefan Metzmacher <metze@samba.org>
Wed, 20 Jan 2010 08:44:36 +0000 (09:44 +0100)
metze

Makefile.in
config/gdb_backtrace [new file with mode: 0755]
configure.ac
include/ctdb_private.h
include/includes.h
lib/util/fault.c [new file with mode: 0644]
lib/util/fault.m4 [new file with mode: 0644]
lib/util/signal.c [new file with mode: 0644]
lib/util/signal.m4 [new file with mode: 0644]
lib/util/substitute.c [new file with mode: 0644]
lib/util/util.h [new file with mode: 0644]

index 0684df3eca65b1dc371e19c3d3e18f50cf2898d2..41b255d71cc173d4d51d81c2ef5ea92996e7d035 100755 (executable)
@@ -35,7 +35,8 @@ CFLAGS=-g -I$(srcdir)/include -Iinclude -Ilib -Ilib/util -I$(srcdir) \
 LIB_FLAGS=@LDFLAGS@ -Llib @LIBS@ $(POPT_LIBS) @INFINIBAND_LIBS@ @CTDB_PCAP_LDFLAGS@
 
 UTIL_OBJ = lib/util/idtree.o lib/util/db_wrap.o lib/util/strlist.o lib/util/util.o \
-       lib/util/util_time.o lib/util/util_file.o
+       lib/util/util_time.o lib/util/util_file.o lib/util/fault.o lib/util/substitute.o \
+       lib/util/signal.o
 
 CTDB_COMMON_OBJ =  common/ctdb_io.o common/ctdb_util.o \
        common/ctdb_ltdb.o common/ctdb_message.o common/cmdline.o  \
diff --git a/config/gdb_backtrace b/config/gdb_backtrace
new file mode 100755 (executable)
index 0000000..826381e
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+BASENAME=`basename $0`
+
+if [ -n "$VALGRIND" -o -n "$SMBD_VALGRIND" ]; then
+       echo "${BASENAME}: Not running debugger under valgrind"
+       exit 1
+fi
+
+# we want everything on stderr, so the program is not disturbed
+exec 1>&2
+
+BASENAME=`basename $0`
+UNAME=`uname`
+
+PID=$1
+BINARY=$2
+
+test x"${PID}" = x"" && {
+       echo "Usage: ${BASENAME} <pid> [<binary>]"
+       exit 1
+}
+
+DB_LIST="gdb"
+case "${UNAME}" in
+       #
+       # on Tru64 we need to try ladebug first
+       # because gdb crashes itself...
+       #
+       OSF1)
+               DB_LIST="ladebug ${DB_LIST}"
+       ;;
+esac
+
+for DB in ${DB_LIST}; do
+       DB_BIN=`which ${DB} 2>/dev/null | grep '^/'`
+       test x"${DB_BIN}" != x"" && {
+               break
+       }
+done
+
+test x"${DB_BIN}" = x"" && {
+       echo "${BASENAME}: ERROR: No debugger found."
+       exit 1
+}
+
+#
+# we first try to use /proc/${PID}/exe
+# then fallback to the binary from the commandline
+# then we search for the commandline argument with
+# 'which'
+#
+test -f "/proc/${PID}/exe" && BINARY="/proc/${PID}/exe"
+test x"${BINARY}" = x"" && BINARY="/proc/${PID}/exe"
+test -f "${BINARY}" || BINARY=`which ${BINARY}`
+
+test -f "${BINARY}" || {
+       echo "${BASENAME}: ERROR: Cannot find binary '${BINARY}'."
+       exit 1
+}
+
+echo "${BASENAME}: Trying to use ${DB_BIN} on ${BINARY} on PID ${PID}"
+
+BATCHFILE_PRE=/tmp/gdb_backtrace_pre.$$
+BATCHFILE_MAIN=/tmp/gdb_backtrace_main.$$
+case "${DB}" in
+       ladebug)
+cat << EOF  > ${BATCHFILE_PRE}
+set \$stoponattach
+EOF
+
+cat << EOF  > ${BATCHFILE_MAIN}
+where
+quit
+EOF
+       ${DB_BIN} -c "${BATCHFILE_MAIN}" -i "${BATCHFILE_PRE}" -pid "${PID}" "${BINARY}"
+       ;;
+       gdb)
+cat << EOF  > ${BATCHFILE_MAIN}
+set height 1000
+bt full
+quit
+EOF
+       ${DB_BIN} -x "${BATCHFILE_MAIN}" "${BINARY}" "${PID}"
+       ;;
+esac
+/bin/rm -f ${BATCHFILE_PRE} ${BATCHFILE_MAIN}
index 0b060fec7e61a4d6a641d9abf581a7be2e874bcd..f159c293e0d8d058862a2b56e049c31edb0b4a1f 100644 (file)
@@ -60,6 +60,8 @@ m4_include(libtalloc.m4)
 m4_include(libtdb.m4)
 m4_include(libevents.m4)
 m4_include(ib/config.m4)
+m4_include(lib/util/signal.m4)
+m4_include(lib/util/fault.m4)
 
 AC_CHECK_HEADERS(sched.h)
 AC_CHECK_FUNCS(sched_setscheduler)
index 6a99736bf9de29444cba4122d54478c8d46b15e1..653fe0d9ad913ac1ee440d28209b59ef6d88bb58 100644 (file)
@@ -1564,4 +1564,5 @@ int ctdb_update_persistent_health(struct ctdb_context *ctdb,
                                  int num_healthy_nodes);
 int ctdb_recheck_persistent_health(struct ctdb_context *ctdb);
 
+void ctdb_fault_setup(void);
 #endif
index deab199ff5036fe37019db98970adbeedb6c297a..43a6a5ff68746a6c0fe8a488ec0780e78add469e 100644 (file)
@@ -7,7 +7,6 @@
 #include "tdb.h"
 #include "idtree.h"
 #include "ctdb.h"
-#include "lib/util/debug.h"
 
 typedef bool BOOL;
 
@@ -33,6 +32,8 @@ enum debug_level {
 #define DEBUGADD(lvl, x) do { if ((lvl) <= LogLevel) { this_log_level = (lvl); do_debug_add x; }} while (0)
 
 #define _PUBLIC_
+#define _NORETURN_
+#define _PURE_
 
 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
 
@@ -55,3 +56,5 @@ char *hex_encode_talloc(TALLOC_CTX *mem_ctx, const unsigned char *buff_in, size_
 _PUBLIC_ const char **str_list_add(const char **list, const char *s);
 _PUBLIC_ int set_blocking(int fd, BOOL set);
 
+#include "lib/util/debug.h"
+#include "lib/util/util.h"
diff --git a/lib/util/fault.c b/lib/util/fault.c
new file mode 100644 (file)
index 0000000..9262af2
--- /dev/null
@@ -0,0 +1,228 @@
+/* 
+   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_backstrace: not implemented\n"));
+#error bla
+#endif
+}
+
+_PUBLIC_ const char *panic_action = 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));
+               result = system(cmdstring);
+
+               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,SIGNAL_CAST 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,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGBUS
+       CatchSignal(SIGBUS,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGABRT
+       CatchSignal(SIGABRT,SIGNAL_CAST sig_fault);
+#endif
+#ifdef SIGFPE
+       CatchSignal(SIGFPE,SIGNAL_CAST 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/lib/util/fault.m4 b/lib/util/fault.m4
new file mode 100644 (file)
index 0000000..da077af
--- /dev/null
@@ -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/lib/util/signal.c b/lib/util/signal.c
new file mode 100644 (file)
index 0000000..ead947e
--- /dev/null
@@ -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/lib/util/signal.m4 b/lib/util/signal.m4
new file mode 100644 (file)
index 0000000..c6d7f72
--- /dev/null
@@ -0,0 +1 @@
+AC_CHECK_FUNCS(sigprocmask sigblock sigaction)
diff --git a/lib/util/substitute.c b/lib/util/substitute.c
new file mode 100644 (file)
index 0000000..32945a7
--- /dev/null
@@ -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/lib/util/util.h b/lib/util/util.h
new file mode 100644 (file)
index 0000000..f22c990
--- /dev/null
@@ -0,0 +1,656 @@
+/* 
+   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;
+
+/**
+ * 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);
+
+/**
+ * Add-on to talloc_get_type
+ */
+_PUBLIC_ void *talloc_check_name_abort(const void *ptr, const char *name);
+#define talloc_get_type_abort(ptr, type) \
+       (type *)talloc_check_name_abort(ptr, #type)
+
+bool unmap_file(void *start, size_t size);
+
+#define CONST_DISCARD(type, ptr)      ((type) ((void *) (ptr)))
+
+#endif /* _SAMBA_UTIL_H_ */