diff options
-rw-r--r-- | source3/Makefile.in | 6 | ||||
-rw-r--r-- | source3/configure.in | 49 | ||||
-rw-r--r-- | source3/utils/smbcontrol.c | 187 |
3 files changed, 238 insertions, 4 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 6d7919bfbf..773eb65016 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -974,9 +974,9 @@ bin/smbstatus@EXEEXT@: $(STATUS_OBJ) @BUILD_POPT@ bin/.dummy bin/smbcontrol@EXEEXT@: $(SMBCONTROL_OBJ) @BUILD_POPT@ bin/.dummy @echo Linking $@ - @$(CC) -DUSING_SMBCONTROL $(FLAGS) @PIE_LDFLAGS@ -o $@ $(SMBCONTROL_OBJ) $(DYNEXP) \ - $(LDFLAGS) $(LIBS) \ - @POPTLIBS@ + @$(CC) -DUSING_SMBCONTROL $(FLAGS) @PIE_LDFLAGS@ -o $@ \ + $(SMBCONTROL_OBJ) $(DYNEXP) $(LDFLAGS) \ + $(LIBS) @LIBUNWIND_PTRACE@ @POPTLIBS@ bin/smbtree@EXEEXT@: $(SMBTREE_OBJ) @BUILD_POPT@ bin/.dummy @echo Linking $@ diff --git a/source3/configure.in b/source3/configure.in index bb4cf82f35..fc5bad6dab 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -1280,12 +1280,61 @@ AC_TRY_LINK( [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_LIBUNWIND, 1, [Whether libunwind is available]) + + # If we have libunwind, test whether we also have libunwind-ptrace + # which would let us unwind arbitrary processes. + save_LIBS=$LIBS + AC_CHECK_HEADERS(libunwind-ptrace.h) + AC_CHECK_LIB(unwind-ptrace, _UPT_create, + [ + LIBUNWIND_PTRACE="-lunwind-ptrace"; + AC_DEFINE(HAVE_LIBUNWIND_PTRACE, 1, + [Whether libunwind-ptrace.a is available.]) + ], + [ LIBUNWIND_PTRACE="" ]) + + LIBS=$save_LIBS ], [ AC_MSG_RESULT(no) LIBS=$save_LIBS ]) +# To use libunwind-ptrace, we also need to make some ptrace system calls. +if test x"$LIBUNWIND_PTRACE" != x"" ; then + AC_CHECK_HEADERS(sys/ptrace.h) + AC_MSG_CHECKING([for the Linux ptrace(2) interface]) + AC_TRY_LINK( + [ +#if HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#if HAVE_SYS_PTRACE_H +#include <sys/ptrace.h> +#endif + ], + [ + int main(int argc, const char ** argv) + { + pid_t me = (pid_t)-1; + ptrace(PTRACE_ATTACH, me, 0, 0); + ptrace(PTRACE_DETACH, me, 0, 0); + return 0; + } + ], + [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_LINUX_PTRACE, 1, + [Whether the Linux ptrace(2) interface is available.]) + ], + [ + AC_MSG_RESULT(no) + LIBUNWIND_PTRACE="" + ]) +fi + +AC_SUBST(LIBUNWIND_PTRACE) + # syscall() is needed for smbwrapper. AC_CHECK_FUNCS(syscall) diff --git a/source3/utils/smbcontrol.c b/source3/utils/smbcontrol.c index 5c798c7901..c368ee0ad4 100644 --- a/source3/utils/smbcontrol.c +++ b/source3/utils/smbcontrol.c @@ -7,6 +7,7 @@ Copyright (C) Andrew Tridgell 1994-1998 Copyright (C) Martin Pool 2001-2002 Copyright (C) Simo Sorce 2002 + Copyright (C) James Peach 2006 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 @@ -25,6 +26,18 @@ #include "includes.h" +#if HAVE_LIBUNWIND_H +#include <libunwind.h> +#endif + +#if HAVE_LIBUNWIND_PTRACE_H +#include <libunwind-ptrace.h> +#endif + +#if HAVE_SYS_PTRACE_H +#include <sys/ptrace.h> +#endif + /* Default timeout value when waiting for replies (in seconds) */ #define DEFAULT_TIMEOUT 10 @@ -131,7 +144,177 @@ static BOOL do_debug(const struct process_id pid, pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False); } -/* Inject a fault (fata signal) into a running smbd */ +#if defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) + +/* Return the name of a process given it's PID. This will only work on Linux, + * but that's probably moot since this whole stack tracing implementatino is + * Linux-specific anyway. + */ +static const char * procname(pid_t pid, char * buf, size_t bufsz) +{ + char path[64]; + FILE * fp; + + snprintf(path, sizeof(path), "/proc/%llu/cmdline", + (unsigned long long)pid); + if ((fp = fopen(path, "r")) == NULL) { + return NULL; + } + + fgets(buf, bufsz, fp); + + fclose(fp); + return buf; +} + +static void print_stack_trace(pid_t pid, int * count) +{ + void * pinfo = NULL; + unw_addr_space_t aspace = NULL; + unw_cursor_t cursor; + unw_word_t ip, sp; + + char nbuf[256]; + unw_word_t off; + + int ret; + + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { + fprintf(stderr, + "Failed to attach to process %llu: %s\n", + (unsigned long long)pid, strerror(errno)); + return; + } + + /* Wait until the attach is complete. */ + waitpid(pid, NULL, 0); + + if (((pinfo = _UPT_create(pid)) == NULL) || + ((aspace = unw_create_addr_space(&_UPT_accessors, 0)) == NULL)) { + /* Probably out of memory. */ + fprintf(stderr, + "Unable to initialize stack unwind for process %llu\n", + (unsigned long long)pid); + goto cleanup; + } + + if ((ret = unw_init_remote(&cursor, aspace, pinfo))) { + fprintf(stderr, + "Unable to unwind stack for process %llu: %s\n", + (unsigned long long)pid, unw_strerror(ret)); + goto cleanup; + } + + if (*count > 0) { + printf("\n"); + } + + if (procname(pid, nbuf, sizeof(nbuf))) { + printf("Stack trace for process %llu (%s):\n", + (unsigned long long)pid, nbuf); + } else { + printf("Stack trace for process %llu:\n", + (unsigned long long)pid); + } + + while (unw_step(&cursor) > 0) { + ip = sp = off = 0; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + + ret = unw_get_proc_name(&cursor, nbuf, sizeof(nbuf), &off); + if (ret != 0 && ret != -UNW_ENOMEM) { + snprintf(nbuf, sizeof(nbuf), "<unknown symbol>"); + } + printf(" %s + %#llx [ip=%#llx] [sp=%#llx]\n", + nbuf, (long long)off, (long long)ip, + (long long)sp); + } + + (*count)++; + +cleanup: + if (aspace) { + unw_destroy_addr_space(aspace); + } + + if (pinfo) { + _UPT_destroy(pinfo); + } + + ptrace(PTRACE_DETACH, pid, NULL, NULL); +} + +static int stack_trace_connection(TDB_CONTEXT * tdb, TDB_DATA key, + TDB_DATA data, void * priv) +{ + struct connections_data conn; + + if (data.dsize != sizeof(conn)) + return 0; + + memcpy(&conn, data.dptr, sizeof(conn)); + print_stack_trace(procid_to_pid(&conn.pid), (int *)priv); + + return 0; +} + +static BOOL do_daemon_stack_trace(const struct process_id pid, + const int argc, const char **argv) +{ + fprintf(stderr, + "Daemon stack tracing is not supported on this platform\n"); + return False; + + pid_t dest; + int count = 0; + + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> stacktrace\n"); + return False; + } + + dest = procid_to_pid(&pid); + + if (dest != 0) { + /* It would be nice to be able to make sure that this PID is + * the PID of a smbd/winbind/nmbd process, not some random PID + * the user liked the look of. It doesn't seem like it's worth + * the effort at the moment, however. + */ + print_stack_trace(dest, &count); + } else { + TDB_CONTEXT * tdb; + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, + TDB_DEFAULT, O_RDONLY, 0); + if (!tdb) { + fprintf(stderr, + "Failed to open connections database: %s\n", + strerror(errno)); + return False; + } + + tdb_traverse(tdb, stack_trace_connection, &count); + tdb_close(tdb); + } + + return True; +} + +#else /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */ + +static BOOL do_daemon_stack_trace(const struct process_id pid, + const int argc, const char **argv) +{ + fprintf(stderr, + "Daemon stack tracing is not supported on this platform\n"); + return False; +} + +#endif /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */ + +/* Inject a fault (fatal signal) into a running smbd */ static BOOL do_inject_fault(const struct process_id pid, const int argc, const char **argv) @@ -799,6 +982,8 @@ static const struct { { "profile", do_profile, "" }, { "inject", do_inject_fault, "Inject a fatal signal into a running smbd"}, + { "stacktrace", do_daemon_stack_trace, + "Display a stack trace of a daemon" }, { "profilelevel", do_profilelevel, "" }, { "debuglevel", do_debuglevel, "Display current debuglevels" }, { "printnotify", do_printnotify, "Send a print notify message" }, |