From fec19c66965a3d5e4af1694d51ac8ce2d2b6580e Mon Sep 17 00:00:00 2001 From: Ali Polatel Date: Sat, 15 Oct 2011 00:40:30 +0300 Subject: [PATCH] ruby: split source files and add comments for rdoc --- configure.ac | 2 +- doc/api/ruby/Makefile.am | 4 +- ruby/Makefile.am | 10 +- ruby/bitness.c | 83 ++ ruby/event.c | 65 ++ ruby/globals.h | 173 +++ ruby/init.c | 458 ++++++++ ruby/pink-ruby.c | 2259 -------------------------------------- ruby/socket.c | 514 +++++++++ ruby/strarray.c | 100 ++ ruby/string.c | 171 +++ ruby/syscall.c | 273 +++++ ruby/trace.c | 522 +++++++++ 13 files changed, 2371 insertions(+), 2263 deletions(-) create mode 100644 ruby/bitness.c create mode 100644 ruby/event.c create mode 100644 ruby/globals.h create mode 100644 ruby/init.c delete mode 100644 ruby/pink-ruby.c create mode 100644 ruby/socket.c create mode 100644 ruby/strarray.c create mode 100644 ruby/string.c create mode 100644 ruby/syscall.c create mode 100644 ruby/trace.c diff --git a/configure.ac b/configure.ac index 5ee614d6..2c86ec57 100644 --- a/configure.ac +++ b/configure.ac @@ -488,7 +488,7 @@ if test x"$enable_ruby" = x"yes" ; then fi # Extra cflags for Ruby to quiet useless warnings - WANTED_RUBY_CFLAGS="-Wno-strict-prototypes -Wno-missing-prototypes -Wno-redundant-decls" + WANTED_RUBY_CFLAGS="-Wno-strict-prototypes -Wno-missing-prototypes -Wno-redundant-decls -Wno-missing-noreturn -Wno-unused" PINKTRACE_RUBY_CFLAGS= for flag in $WANTED_RUBY_CFLAGS ; do AX_CHECK_COMPILER_FLAGS([$flag], [PINKTRACE_RUBY_CFLAGS="$PINKTRACE_RUBY_CFLAGS $flag"],) diff --git a/doc/api/ruby/Makefile.am b/doc/api/ruby/Makefile.am index 34b99435..4b749fb3 100644 --- a/doc/api/ruby/Makefile.am +++ b/doc/api/ruby/Makefile.am @@ -6,10 +6,10 @@ all-local: rdoc rdoc: ruby -ruby: $(top_srcdir)/ruby/pink-ruby.c +ruby: $(AM_V_GEN) $(AM_V_at)rm -fr ruby - $(AM_V_at)$(RDOC) $(RDOC_FLAGS) --op $@ $< + $(AM_V_at)$(RDOC) $(RDOC_FLAGS) --op $@ $(wildcard $(top_srcdir)/ruby/*.c) install-data-local: mkdir -p $(DESTDIR)$(htmldir)/api diff --git a/ruby/Makefile.am b/ruby/Makefile.am index c3fa27e2..1bdac107 100644 --- a/ruby/Makefile.am +++ b/ruby/Makefile.am @@ -1,4 +1,11 @@ -IF_RUBY_FILES= pink-ruby.c +IF_RUBY_FILES= \ + trace.c \ + event.c \ + bitness.c \ + syscall.c \ + string.c \ + socket.c \ + init.c IF_RUBY_TESTS= \ TEST_trace.rb \ TEST_bitness.rb \ @@ -15,6 +22,7 @@ IF_RUBY_TESTS= \ EXTRA_DIST= $(IF_RUBY_FILES) $(IF_RUBY_TESTS) CLEANFILES= TEST_UNIX_SOCKET +dist_noinst_HEADERS= globals.h dist_noinst_SCRIPTS= ruby-test.sh if ENABLE_RUBY diff --git a/ruby/bitness.c b/ruby/bitness.c new file mode 100644 index 00000000..a8f22362 --- /dev/null +++ b/ruby/bitness.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::Bitness.get + * call-seq: + * PinkTrace::Bitness.get(pid) -> fixnum + * + * Returns the bitness of the traced child. + */ +VALUE +pinkrb_bitness_get(VALUE mod, VALUE vpid) +{ + pid_t pid; + int bit; + + pid = NUM2PIDT(vpid); + bit = pink_bitness_get(pid); + if (bit == PINK_BITNESS_UNKNOWN) + rb_sys_fail("pink_bitness_get()"); + + return INT2FIX(bit); +} + +/* + * Document-method: Pinktrace::Bitness.name + * call-seq: + * PinkTrace::Bitness.name(bitness) -> String + * + * Returns the name of the given bitness. + */ +VALUE +pinkrb_bitness_name(VALUE mod, VALUE vbit) +{ + pink_bitness_t bit; + + bit = FIX2UINT(vbit); + return rb_str_new2(pink_bitness_name(bit)); +} + +/* + * Document-method: PinkTrace::Bitness.wordsize + * call-seq: + * PinkTrace::Bitness.wordsize(bitness) -> Fixnum + * + * Returns the word size of the given bitness. + */ +VALUE +pinkrb_bitness_wordsize(VALUE mod, VALUE vbit) +{ + unsigned short wordsize; + unsigned bit; + + bit = FIX2UINT(vbit); + wordsize = pink_bitness_wordsize(bit); + return INT2FIX(wordsize); +} diff --git a/ruby/event.c b/ruby/event.c new file mode 100644 index 00000000..72dab8d2 --- /dev/null +++ b/ruby/event.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::Event.decide + * call-seq: + * PinkTrace::Event.decide([status=$?.status]) -> fixnum + * + * Returns the last event made by child. + */ +VALUE +pinkrb_event_decide(int argc, VALUE *argv, VALUE mod) +{ + unsigned int event; + int status; + VALUE vstatus, ls; + + switch (rb_scan_args(argc, argv, "01", &vstatus)) { + case 0: + /* Use $?.status */ +#ifdef HAVE_RB_LAST_STATUS_GET /* ruby-1.9 */ + ls = rb_last_status_get(); +#else /* ruby-1.8 */ + ls = rb_gv_get("$?"); +#endif /* HAVE_RB_LAST_STATUS_GET */ + if (NIL_P(ls)) + rb_raise(rb_eTypeError, "$? is nil"); + status = NUM2INT(rb_iv_get(ls, "status")); + break; + case 1: + status = NUM2INT(vstatus); + break; + default: + abort(); + } + + event = pink_event_decide(status); + return UINT2NUM(event); +} diff --git a/ruby/globals.h b/ruby/globals.h new file mode 100644 index 00000000..074bfba4 --- /dev/null +++ b/ruby/globals.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PINKTRACE_GUARD_RUBY_GLOBALS_H +#define PINKTRACE_GUARD_RUBY_GLOBALS_H 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include + +#include + +/* undef bunch of defines that ruby-1.8 defines in its standard headers */ +#ifdef PACKAGE_NAME +#undef PACKAGE_NAME +#endif +#ifdef PACKAGE_TARNAME +#undef PACKAGE_TARNAME +#endif +#ifdef PACKAGE_VERSION +#undef PACKAGE_VERSION +#endif +#ifdef PACKAGE_STRING +#undef PACKAGE_STRING +#endif +#ifdef PACKAGE_BUGREPORT +#undef PACKAGE_BUGREPORT +#endif +#include + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif + +#if PINKTRACE_HAVE_IPV6 +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif +#endif + +/* + * `pid_t' requires special attention as its size varies a lot between + * different architectures. + */ +#if !defined(SIZEOF_PID_T) || SIZEOF_PID_T == SIZEOF_INT +#ifndef PIDT2NUM +#define PIDT2NUM(p) INT2FIX((p)) +#endif +#ifndef NUM2PIDT +#define NUM2PIDT(p) NUM2INT((p)) +#endif +#elif SIZEOF_PID_T == SIZEOF_LONG +#ifndef PIDT2NUM +#define PIDT2NUM(p) LONG2NUM((p)) +#endif +#ifndef NUM2PIDT +#define NUM2PIDT(p) NUM2LONG((p)) +#endif +#elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG +#ifndef PIDT2NUM +#define PIDT2NUM(p) LL2NUM((p)) +#endif +#ifndef NUM2PIDT +#define NUM2PIDT(p) NUM2LL((p)) +#endif +#else +#error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long long)" +#endif + +#ifdef PINKTRACE_LINUX +#define IS_ABSTRACT(addr) ((addr)->u.sa_un.sun_path[0] == '\0' && (addr)->u.sa_un.sun_path[1] != '\0') +#else +#define IS_ABSTRACT(addr) 0 +#endif + +#ifndef RSTRING_LEN +#define RSTRING_LEN(v) (RSTRING((v))->len) +#define RSTRING_PTR(v) (RSTRING((v))->ptr) +#endif + +VALUE pinkrb_cAddress; + +VALUE pinkrb_trace_me(VALUE mod); +VALUE pinkrb_trace_cont(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_resume(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_kill(VALUE mod, VALUE vpid); +VALUE pinkrb_trace_singlestep(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_syscall(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_syscall_entry(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_syscall_exit(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_sysemu(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_sysemu_singlestep(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_geteventmsg(VALUE mod, VALUE vpid); +VALUE pinkrb_trace_setup(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_trace_attach(VALUE mod, VALUE vpid); +VALUE pinkrb_trace_detach(int argc, VALUE *argv, VALUE mod); + +VALUE pinkrb_event_decide(int argc, VALUE *argv, VALUE mod); + +VALUE pinkrb_bitness_get(VALUE mod, VALUE vpid); +VALUE pinkrb_bitness_name(VALUE mod, VALUE vbit); +VALUE pinkrb_bitness_wordsize(VALUE mod, VALUE vbit); + +VALUE pinkrb_name_syscall(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_name_lookup(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_util_get_syscall(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_util_set_syscall(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_util_get_return(VALUE mod, VALUE vpid); +VALUE pinkrb_util_set_return(VALUE mod, VALUE vpid, VALUE vret); +VALUE pinkrb_util_get_arg(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_util_set_arg(int argc, VALUE *argv, VALUE mod); + +VALUE pinkrb_decode_string(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_encode_string_safe(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_encode_string(int argc, VALUE *argv, VALUE mod); + +VALUE pinkrb_decode_strarray(int argc, VALUE *argv, VALUE mod); + +VALUE pinkrb_has_socketcall(VALUE mod, int argc, VALUE *argv); +VALUE pinkrb_name_socket_subcall(VALUE mod, VALUE vsubcall); +VALUE pinkrb_decode_socket_call(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_decode_socket_fd(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_decode_socket_address(int argc, VALUE *argv, VALUE mod); +VALUE pinkrb_decode_socket_address_fd(int argc, VALUE *argv, VALUE mod); + +VALUE pinkrb_Address_family(VALUE self); +VALUE pinkrb_Address_length(VALUE self); +VALUE pinkrb_Address_is_unix(VALUE self); +VALUE pinkrb_Address_is_inet(VALUE self); +VALUE pinkrb_Address_is_inet6(VALUE self); +VALUE pinkrb_Address_is_netlink(VALUE self); +VALUE pinkrb_Address_is_abstract(VALUE self); +VALUE pinkrb_Address_path(VALUE self); +VALUE pinkrb_Address_port(VALUE self); +VALUE pinkrb_Address_ip(VALUE self); +VALUE pinkrb_Address_ipv6(VALUE self); +VALUE pinkrb_Address_pid(VALUE self); +VALUE pinkrb_Address_groups(VALUE self); + +void Init_PinkTrace(void); + +#endif /* !PINKTRACE_GUARD_RUBY_GLOBALS_H */ diff --git a/ruby/init.c b/ruby/init.c new file mode 100644 index 00000000..334c5af1 --- /dev/null +++ b/ruby/init.c @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-module: PinkTrace + * + * Ruby extension to the pinktrace library. + * + * pinktrace is a lightweight library which provides a robust API for + * tracing processes. + * + * == Classes + * + * Following are the classes that are most likely to be of interest to the user: + * + * - PinkTrace::Trace + * - PinkTrace::Event + * - PinkTrace::Bitness + * - PinkTrace::Syscall + * - PinkTrace::String + * - PinkTrace::StringArray + * - PinkTrace::Socket + * + * == Exceptions + * + * Errno::* errors are raised in case of ptrace errors. + * Check ptrace(2) manual page for more information. + */ + +void +Init_PinkTrace(void) +{ + VALUE mod; + VALUE bitness_mod; + VALUE event_mod; + VALUE string_mod; + VALUE strarray_mod; + VALUE socket_mod; + VALUE syscall_mod; + VALUE trace_mod; + + mod = rb_define_module("PinkTrace"); + /* + * Document-const: PinkTrace::HAVE_IPV6 + * + * This constant is +true+ if IPV6 support is available + */ + rb_define_const(mod, "HAVE_IPV6", PINKTRACE_HAVE_IPV6 ? Qtrue : Qfalse); + /* + * Document-const: PinkTrace::HAVE_NETLINK + * + * This constant is +true+ if Netlink support is available + */ + rb_define_const(mod, "HAVE_NETLINK", PINKTRACE_HAVE_NETLINK ? Qtrue : Qfalse); + /* + * Document-const: PinkTrace::PACKAGE + * + * The name of the package (eg pinktrace) + */ + rb_define_const(mod, "PACKAGE", rb_str_new2(PINKTRACE_PACKAGE)); + /* + * Document-const: PinkTrace::VERSION + * + * The version, two digits per part (eg 1.3.5 -> 10305) + */ + rb_define_const(mod, "VERSION", INT2FIX(PINKTRACE_VERSION)); + /* + * Document-const: PinkTrace::VERSION_MAJOR + * + * The major version (eg 0.4.1 -> 0) + */ + rb_define_const(mod, "VERSION_MAJOR", INT2FIX(PINKTRACE_VERSION_MAJOR)); + /* + * Document-const: PinkTrace::VERSION_MINOR + * + * The minor version (eg 0.4.1 -> 4) + */ + rb_define_const(mod, "VERSION_MINOR", INT2FIX(PINKTRACE_VERSION_MINOR)); + /* + * Document-const: PinkTrace::VERSION_MICRO + * + * The micro version (eg 0.4.1 -> 1) + */ + rb_define_const(mod, "VERSION_MICRO", INT2FIX(PINKTRACE_VERSION_MICRO)); + /* + * Document-const: PinkTrace::VERSION_SUFFIX + * + * The version suffix (eg "_alpha1"), often an empty string + */ + rb_define_const(mod, "VERSION_SUFFIX", rb_str_new2(PINKTRACE_VERSION_SUFFIX)); + /* + * Document-const: PinkTrace::GIT_HEAD + * + * The Git head used to build this binary, if applicable (eg "deadbeef" or "1.0.0-40-f00-dirty" or "") + */ + rb_define_const(mod, "GIT_HEAD", rb_str_new2(PINKTRACE_GIT_HEAD)); + /* + * Document-const: PinkTrace::PC_SLOT + * + * The suffix used for so names (eg "0.30" or "0.31_15ece615") + */ + rb_define_const(mod, "PC_SLOT", rb_str_new2(PINKTRACE_PC_SLOT)); + + /* + * Document-module: PinkTrace::Trace + * + * This class includes thin wrappers around the ptrace() system call. + */ + trace_mod = rb_define_module_under(mod, "Trace"); +#ifdef PINKTRACE_LINUX + /* + * Document-const: PinkTrace::Trace::OPTION_SYSGOOD + * (Availability: Linux) + * + * This constant represents the trace option +SYSGOOD+. If this flag is + * set in the options argument of PinkTrace::Trace.setup, when + * delivering syscall traps, bit 7 is set in signal number (i.e., + * deliver (SIGTRAP | 0x80) This makes it easy for the tracer to tell + * the difference between normal traps and those caused by a sycall. + * This option may not work on all architectures. + */ + rb_define_const(trace_mod, "OPTION_SYSGOOD", INT2FIX(PINK_TRACE_OPTION_SYSGOOD)); + /* + * Document-const: PinkTrace::Trace::OPTION_FORK + * (Availability: Linux) + * + * This constant represents the trace option +FORK+. If this flag is set + * in the options argument of PinkTrace::Trace.setup, stop the child at + * the next fork(2) call with (SIGTRAP | PTRACE_EVENT_FORK << 8) and + * automatically start tracing the newly forked process, which will + * start with a SIGSTOP. The PID for the new process can be retrieved + * with PinkTrace::Trace.geteventmsg. + */ + rb_define_const(trace_mod, "OPTION_FORK", INT2FIX(PINK_TRACE_OPTION_FORK)); + /* + * Document-const: PinkTrace::Trace::OPTION_VFORK + * (Availability: Linux) + * + * This constant represents the trace option +VFORK+. If this flag is + * set in the options argument of PinkTrace::Trace.setup, stop the + * child at the next vfork(2) call with + * (SIGTRAP | PTRACE_EVENT_VFORK << 8) and automatically start tracing + * the newly vforked process, which will start with a SIGSTOP. The PID + * for the new process can be retrieved with + * PinkTrace::Trace.geteventmsg. + */ + rb_define_const(trace_mod, "OPTION_VFORK", INT2FIX(PINK_TRACE_OPTION_VFORK)); + /* + * Document-const: PinkTrace::Trace::OPTION_CLONE + * (Availability: Linux) + * + * This constant represnets the trace option +CLONE+. If this flag is + * set in the options argument of PinkTrace::Trace.setup, stop the + * child at the next clone(2) call with + * (SIGTRAP | PTRACE_EVENT_CLONE << 8) and automatically start tracing + * the newly cloned process, which will start with a SIGSTOP. The PID + * for the new process can be retrieved with + * PinkTrace::Trace.geteventmsg. + */ + rb_define_const(trace_mod, "OPTION_CLONE", INT2FIX(PINK_TRACE_OPTION_CLONE)); + /* + * Document-const: PinkTrace::Trace::OPTION_EXEC + * (Availability: Linux) + * + * This constant represents the trace option +EXEC+. If this flag is set + * in the options argument of PinkTrace::Trace.setup, stop the child at + * the next execve(2) call with + * (SIGTRAP | PTRACE_EVENT_EXEC << 8). + */ + rb_define_const(trace_mod, "OPTION_EXEC", INT2FIX(PINK_TRACE_OPTION_EXEC)); + /* + * Document-const: PinkTrace::Trace::OPTION_VFORK_DONE + * (Availability: Linux) + * + * This constant represents the trace option +VFORK_DONE+. If this flag + * is set in the options argument of PinkTrace::Trace.setup, stop the + * child at the completion of the next vfork(2) call with + * (SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8). + */ + rb_define_const(trace_mod, "OPTION_VFORK_DONE", INT2FIX(PINK_TRACE_OPTION_VFORK_DONE)); + /* + * Document-const: PinkTrace::Trace::OPTION_EXIT + * (Availability: Linux) + * + * This constant represents the trace option +EXIT+. If this flag is set + * in the options argument of PinkTrace::Trace.setup, stop the child at + * exit with (SIGTRAP | PTRACE_EVENT_EXIT << 8). This child's exit + * status can be retrieved with PinkTrace::Trace.geteventmsg. This + * stop will be done early during process exit when registers are still + * available, allowing the tracer to see where the exit occured, + * whereas the normal exit notification is done after the process is + * finished exiting. Even though context is available, the tracer + * cannot prevent the exit from happening at this point. + */ + rb_define_const(trace_mod, "OPTION_EXIT", INT2FIX(PINK_TRACE_OPTION_EXIT)); + /* + * Document-const: PinkTrace::Trace::OPTION_EXIT + * (Availability: Linux) + * + * This constant represents all option flags bitwise OR'ed together. + */ + rb_define_const(trace_mod, "OPTION_ALL", INT2FIX(PINK_TRACE_OPTION_ALL)); +#endif + rb_define_module_function(trace_mod, "me", pinkrb_trace_me, 0); /* in trace.c */ + rb_define_module_function(trace_mod, "cont", pinkrb_trace_cont, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "resume", pinkrb_trace_resume, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "kill", pinkrb_trace_kill, 1); /* in trace.c */ + rb_define_module_function(trace_mod, "singlestep", pinkrb_trace_singlestep, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "syscall", pinkrb_trace_syscall, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "syscall_entry", pinkrb_trace_syscall_entry, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "syscall_exit", pinkrb_trace_syscall_exit, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "sysemu", pinkrb_trace_sysemu, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "sysemu_singlestep", pinkrb_trace_sysemu_singlestep, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "geteventmsg", pinkrb_trace_geteventmsg, 1); /* in trace.c */ + rb_define_module_function(trace_mod, "setup", pinkrb_trace_setup, -1); /* in trace.c */ + rb_define_module_function(trace_mod, "attach", pinkrb_trace_attach, 1); /* in trace.c */ + rb_define_module_function(trace_mod, "detach", pinkrb_trace_detach, -1); /* in trace.c */ + + /* + * Document-module: PinkTrace::Event + * + * This class defines constants and functions for event decisions. + */ + event_mod = rb_define_module_under(mod, "Event"); + /* + * Document-const: PinkTrace::Event::EVENT_STOP + * + * The traced child has received a SIGSTOP. + */ + rb_define_const(event_mod, "EVENT_STOP", INT2FIX(PINK_EVENT_STOP)); + /* + * Document-const: PinkTrace::Event::EVENT_TRAP + * + * The traced child has received a SIGTRAP. + */ + rb_define_const(event_mod, "EVENT_TRAP", INT2FIX(PINK_EVENT_TRAP)); + /* + * Document-const: PinkTrace::Event::EVENT_SYSCALL + * + * The traced child is entering or exiting a system call. + */ + rb_define_const(event_mod, "EVENT_SYSCALL", INT2FIX(PINK_EVENT_SYSCALL)); + /* + * Document-const: PinkTrace::Event::EVENT_FORK + * + * The traced child called fork(2). + */ + rb_define_const(event_mod, "EVENT_FORK", INT2FIX(PINK_EVENT_FORK)); + /* + * Document-const: PinkTrace::Event::EVENT_VFORK + * + * The traced child called vfork(2). + */ + rb_define_const(event_mod, "EVENT_VFORK", INT2FIX(PINK_EVENT_VFORK)); + /* + * Document-const: PinkTrace::Event::EVENT_CLONE + * + * The traced child called clone(2). + */ + rb_define_const(event_mod, "EVENT_CLONE", INT2FIX(PINK_EVENT_CLONE)); + /* + * Document-const: PinkTrace::Event::EVENT_VFORK_DONE + * + * The traced child is exiting a vfork(2) call. + */ + rb_define_const(event_mod, "EVENT_VFORK_DONE", INT2FIX(PINK_EVENT_VFORK_DONE)); + /* + * Document-const: PinkTrace::Event::EVENT_EXEC + * + * The traced child has called execve(2). + */ + rb_define_const(event_mod, "EVENT_EXEC", INT2FIX(PINK_EVENT_EXEC)); + /* + * Document-const: PinkTrace::Event::EVENT_EXIT + * + * The traced child is exiting. (ptrace way, stopped before exit) + */ + rb_define_const(event_mod, "EVENT_EXIT", INT2FIX(PINK_EVENT_EXIT)); + /* + * Document-const: PinkTrace::Event::EVENT_GENUINE + * + * The traced child has received a genuine signal. + */ + rb_define_const(event_mod, "EVENT_GENUINE", INT2FIX(PINK_EVENT_GENUINE)); + /* + * Document-const: PinkTrace::Event::EVENT_EXIT_GENUINE + * + * The traced child has exited normally. + */ + rb_define_const(event_mod, "EVENT_EXIT_GENUINE", INT2FIX(PINK_EVENT_EXIT_GENUINE)); + /* + * Document-const: PinkTrace::Event::EVENT_EXIT_SIGNAL + * + * The traced child has been terminated with a signal. + */ + rb_define_const(event_mod, "EVENT_EXIT_SIGNAL", INT2FIX(PINK_EVENT_EXIT_SIGNAL)); + /* + * Document-const: PinkTrace::Event::EVENT_UNKNOWN + * + * Unknown event + */ + rb_define_const(event_mod, "EVENT_UNKNOWN", INT2FIX(PINK_EVENT_UNKNOWN)); + rb_define_module_function(event_mod, "decide", pinkrb_event_decide, -1); /* in event.c */ + + /* + * Document-module: PinkTrace::Bitness + * + * This class defines constants and functions about bitness. + */ + bitness_mod = rb_define_module_under(mod, "Bitness"); + /* + * Document-const: PinkTrace::Bitness::COUNT_SUPPORTED + * + * Count of supported bitnesses (eg 2 on x86_64, 1 on i386) + */ + rb_define_const(bitness_mod, "COUNT_SUPPORTED", INT2FIX(PINKTRACE_BITNESS_COUNT_SUPPORTED)); + /* + * Document-const: PinkTrace::Bitness::DEFAULT + * + * The default bitness + */ + rb_define_const(bitness_mod, "DEFAULT", INT2FIX(PINKTRACE_BITNESS_DEFAULT)); + /* + * Document-const: PinkTrace::Bitness::BITNESS_32 + * + * 32 bit mode + */ + rb_define_const(bitness_mod, "BITNESS_32", INT2FIX(PINK_BITNESS_32)); + /* + * Document-const: PinkTrace::Bitness::BITNESS_64 + * + * 64 bit mode + */ + rb_define_const(bitness_mod, "BITNESS_64", INT2FIX(PINK_BITNESS_64)); + /* + * Document-const: PinkTrace::Bitness::BITNESS_32_SUPPORTED + * + * +true+ if 32 bit is supported, +false+ otherwise. + */ + rb_define_const(bitness_mod, "BITNESS_32_SUPPORTED", PINKTRACE_BITNESS_32_SUPPORTED ? Qtrue : Qfalse); + /* + * Document-const: PinkTrace::Bitness::BITNESS_64_SUPPORTED + * + * +true+ if 64 bit is supported, +false+ otherwise. + */ + rb_define_const(bitness_mod, "BITNESS_64_SUPPORTED", PINKTRACE_BITNESS_64_SUPPORTED ? Qtrue : Qfalse); + rb_define_module_function(bitness_mod, "get", pinkrb_bitness_get, 1); /* in bitness.c */ + rb_define_module_function(bitness_mod, "name", pinkrb_bitness_name, 1); /* in bitness.c */ + rb_define_module_function(bitness_mod, "wordsize", pinkrb_bitness_wordsize, 1); /* in bitness.c */ + + /* + * Document-module: PinkTrace::Syscall + * + * This class defines utilities useful when tracing processes. + */ + syscall_mod = rb_define_module_under(mod, "Syscall"); + /* + * Document-const: PinkTrace::Syscall::INVALID + * + * This constant is an invalid system call number. You may use this + * constant as an argument to PinkTrace::Syscall.set_no to deny a + * system call from execution. + */ + rb_define_const(syscall_mod, "INVALID", INT2FIX(PINKTRACE_INVALID_SYSCALL)); + /* + * Document-const: PinkTrace::Syscall::MAX_INDEX + * + * The index arguments of system call functions must be smaller than + * this constant. + */ + rb_define_const(syscall_mod, "MAX_INDEX", INT2FIX(PINK_MAX_INDEX)); + rb_define_module_function(syscall_mod, "name", pinkrb_name_syscall, -1); /* in syscall.c */ + rb_define_module_function(syscall_mod, "lookup", pinkrb_name_lookup, -1); /* in syscall.c */ + rb_define_module_function(syscall_mod, "get_no", pinkrb_util_get_syscall, -1); /* in syscall.c */ + rb_define_module_function(syscall_mod, "set_no", pinkrb_util_set_syscall, -1); /* in syscall.c */ + rb_define_module_function(syscall_mod, "get_ret", pinkrb_util_get_return, 1); /* in syscall.c */ + rb_define_module_function(syscall_mod, "set_ret", pinkrb_util_set_return, 2); /* in syscall.c */ + rb_define_module_function(syscall_mod, "get_arg", pinkrb_util_get_arg, -1); /* in syscall.c */ + rb_define_module_function(syscall_mod, "set_arg", pinkrb_util_set_arg, -1); /* in syscall.c */ + /* + * Document-module: PinkTrace::String + * + * This class contains functions to decode/encode string arguments. + */ + string_mod = rb_define_module_under(mod, "String"); + rb_define_module_function(string_mod, "decode", pinkrb_decode_string, -1); /* in string.c */ + rb_define_module_function(string_mod, "encode", pinkrb_encode_string_safe, -1); /* in string.c */ + rb_define_module_function(string_mod, "encode!", pinkrb_encode_string, -1); /* in string.c */ + + /* + * Document-module: PinkTrace::StringArray + * + * This class contains functions to decode NULL-terminated string array members. + */ + strarray_mod = rb_define_module_under(mod, "StringArray"); + rb_define_module_function(strarray_mod, "decode", pinkrb_decode_strarray, -1); /* in strarray.c */ + + /* + * Document-module: PinkTrace::Socket + * + * This module includes functions for decoding socket calls. + */ + socket_mod = rb_define_module_under(mod, "Socket"); + rb_define_module_function(socket_mod, "has_socketcall?", pinkrb_has_socketcall, -1); /* in socket.c */ + rb_define_module_function(socket_mod, "name", pinkrb_name_socket_subcall, 1); /* in socket.c */ + rb_define_module_function(socket_mod, "decode_call", pinkrb_decode_socket_call, -1); /* in socket.c */ + rb_define_module_function(socket_mod, "decode_fd", pinkrb_decode_socket_fd, -1); /* in socket.c */ + + /* + * Document-class: PinkTrace::Socket::Address + * + * This class represents a decoded socket address. + */ + pinkrb_cAddress = rb_define_class_under(socket_mod, "Address", rb_cObject); + rb_undef_method(pinkrb_cAddress, "initialize"); + rb_define_method(pinkrb_cAddress, "family", pinkrb_Address_family, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "length", pinkrb_Address_length, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "unix?", pinkrb_Address_is_unix, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "inet?", pinkrb_Address_is_inet, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "inet6?", pinkrb_Address_is_inet6, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "netlink?", pinkrb_Address_is_netlink, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "abstract?", pinkrb_Address_is_abstract, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "path", pinkrb_Address_path, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "port", pinkrb_Address_port, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "ip", pinkrb_Address_ip, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "ipv6", pinkrb_Address_ipv6, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "pid", pinkrb_Address_pid, 0); /* in socket.c */ + rb_define_method(pinkrb_cAddress, "groups", pinkrb_Address_groups, 0); /* in socket.c */ + + rb_define_module_function(socket_mod, "decode_address", pinkrb_decode_socket_address, -1); /* in socket.c */ + rb_define_module_function(socket_mod, "decode_address_fd", pinkrb_decode_socket_address_fd, -1); /* in socket.c */ +} diff --git a/ruby/pink-ruby.c b/ruby/pink-ruby.c deleted file mode 100644 index c4afbcd7..00000000 --- a/ruby/pink-ruby.c +++ /dev/null @@ -1,2259 +0,0 @@ -/* - * Copyright (c) 2010, 2011 Ali Polatel - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include -#include /* free() */ -#include /* memcpy() */ - -#include /* INET{,6}_ADDRSTRLEN */ -#include /* inet_ntop() */ - -#include -#include - -#ifndef INET_ADDRSTRLEN -#define INET_ADDRSTRLEN 16 -#endif /* !INET_ADDRSTRLEN */ - -#if PINKTRACE_HAVE_IPV6 -#ifndef INET6_ADDRSTRLEN -#define INET6_ADDRSTRLEN 46 -#endif /* !INET6_ADDRSTRLEN */ -#endif - -/* - * `pid_t' requires special attention as its size varies a lot between - * different architectures. - */ -#if !defined(SIZEOF_PID_T) || SIZEOF_PID_T == SIZEOF_INT -#ifndef PIDT2NUM -#define PIDT2NUM(p) INT2FIX((p)) -#endif /* !PIDT2NUM */ -#ifndef NUM2PIDT -#define NUM2PIDT(p) NUM2INT((p)) -#endif /* !NUM2PIDT */ -#elif SIZEOF_PID_T == SIZEOF_LONG -#ifndef PIDT2NUM -#define PIDT2NUM(p) LONG2NUM((p)) -#endif /* !PIDT2NUM */ -#ifndef NUM2PIDT -#define NUM2PIDT(p) NUM2LONG((p)) -#endif /* !NUM2PIDT */ -#elif defined(SIZEOF_LONG_LONG) && SIZEOF_PID_T == SIZEOF_LONG_LONG -#ifndef PIDT2NUM -#define PIDT2NUM(p) LL2NUM((p)) -#endif /* !PIDT2NUM */ -#ifndef NUM2PIDT -#define NUM2PIDT(p) NUM2LL((p)) -#endif /* !NUM2PIDT */ -#else -#error "sizeof(pid_t) is neither sizeof(int), sizeof(long) or sizeof(long long)" -#endif /* SIZEOF_PID_T */ - -#ifdef PINKTRACE_LINUX -#define IS_ABSTRACT(addr) \ - ((addr)->u.sa_un.sun_path[0] == '\0' \ - && (addr)->u.sa_un.sun_path[1] != '\0') -#else -#define IS_ABSTRACT(addr) 0 -#endif /* PINKTRACE_LINUX */ - -void -Init_PinkTrace(void); - -static VALUE pinkrb_eBitnessError; -static VALUE pinkrb_eIndexError; - -static VALUE pinkrb_cAddress; - -static void -check_bitness(unsigned bit) -{ - switch (bit) { - case PINK_BITNESS_64: -#if !PINKTRACE_BITNESS_64_SUPPORTED - rb_raise(pinkrb_eBitnessError, "Unsupported bitness"); -#endif /* !PINKTRACE_BITNESS_64_SUPPORTED */ - break; - case PINK_BITNESS_32: -#if !PINKTRACE_BITNESS_32_SUPPORTED - rb_raise(pinkrb_eBitnessError, "Unsupported bitness"); -#endif - break; - default: - rb_raise(pinkrb_eBitnessError, "Undefined bitness"); - break; - } -} - -static void -check_index(unsigned ind) -{ - if (ind >= PINK_MAX_INDEX) - rb_raise(pinkrb_eIndexError, "index not smaller than MAX_INDEX"); -} - -/* - * Document-class: PinkTrace - * - * == Summary - * - * Ruby extension to the pinktrace library. - * - * pinktrace is a lightweight library which provides a robust API for - * tracing processes. - * - * == Classes - * - * Following are the classes that are most likely to be of interest to the user: - * - * - PinkTrace::Trace - * - PinkTrace::Event - * - PinkTrace::Bitness - * - PinkTrace::Syscall - * - PinkTrace::String - * - PinkTrace::StringArray - * - PinkTrace::Socket - * - * == Constants - * - * - PinkTrace::PACKAGE - * - * The name of the package (eg pinktrace) - * - * - PinkTrace::VERSION_MAJOR - * - * The major version (eg 0.4.1 -> 0) - * - * - PinkTrace::VERSION_MINOR - * - * The minor version (eg 0.4.1 -> 4) - * - * - PinkTrace::VERSION_MICRO - * - * The micro version (eg 0.4.1 -> 1) - * - * - PinkTrace::VERSION - * - * The version, two digits per part (eg 1.3.5 -> 10305) - * - * - PinkTrace::VERSION_SUFFIX - * - * The version suffix (eg "_alpha1"), often an empty string - * - * - PinkTrace::GIT_HEAD - * - * The Git head used to build this binary, if applicable (eg "deadbeef" or "1.0.0-40-f00-dirty" or "") - * - * - PinkTrace::PC_SLOT - * - * The suffix used for so names (eg "0.30" or "0.31_15ece615") - * - * - PinkTrace::HAVE_IPV6 - * - * This constant can be used to figure out if IPV6 support was compiled in. - * +false+ if IPV6 support isn't available, +true+ otherwise. - * - * == Exceptions - * - * - PinkTrace::BitnessError - * - PinkTrace::IndexError - */ - -/* - * Document-class: PinkTrace::BitnessError - * - * Raised when the given bitness argument is either unsupported or undefined. - */ - -/* - * Document-class: PinkTrace::IndexError - * - * Raised when an index argument is not smaller than PinkTrace::Syscall::MAX_INDEX. - */ - -/* - * Document-class: PinkTrace::Trace - * - * This class includes thin wrappers around the ptrace() system call. - * - * == Constants - * - * Note: The constants below are only available on Linux. - * - * - PinkTrace::Trace::OPTION_SYSGOOD - * - * This constant represents the trace option SYSGOOD. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * when delivering syscall traps, bit 7 is set in signal number (i.e., - * deliver (SIGTRAP | 0x80) This makes it easy for the tracer to tell the - * difference between normal traps and those caused by a sycall. This - * option may not work on all architectures. - * - * - PinkTrace::Trace::OPTION_FORK - * - * This constant represents the trace option FORK. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * stop the child at the next fork(2) call with (SIGTRAP | PTRACE_EVENT_FORK << 8) - * and automatically start tracing the newly forked process, which will start with - * a SIGSTOP. The PID for the new process can be retrieved with PinkTrace::Trace.geteventmsg. - * - * - PinkTrace::Trace::OPTION_VFORK - * - * This constant represents the trace option VFORK. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * stop the child at the next vfork(2) call with (SIGTRAP | PTRACE_EVENT_VFORK << 8) - * and automatically start tracing the newly vforked process, which will start with - * a SIGSTOP. The PID for the new process can be retrieved with PinkTrace::Trace.geteventmsg. - * - * - PinkTrace::Trace::OPTION_CLONE - * - * This constant represnets the trace option CLONE. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * stop the child at the next clone(2) call with (SIGTRAP | PTRACE_EVENT_CLONE << 8) - * and automatically start tracing the newly cloned process, which will start with - * a SIGSTOP. The PID for the new process can be retrieved with PinkTrace::Trace.geteventmsg. - * - * - PinkTrace::Trace::OPTION_EXEC - * - * This constant represents the trace option EXEC. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * stop the child at the next execve(2) call with (SIGTRAP | PTRACE_EVENT_EXEC << 8). - * - * - PinkTrace::Trace::OPTION_VFORK_DONE - * - * This constant represents the trace option VFORK_DONE. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * stop the child at the completion of the next vfork(2) call with - * (SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8). - * - * - PinkTrace::Trace::OPTION_EXIT - * - * This constant represents the trace option EXIT. - * If this flag is set in the options argument of PinkTrace::Trace.setup, - * stop the child at exit with (SIGTRAP | PTRACE_EVENT_EXIT << 8). - * This child's exit status can be retrieved with PinkTrace::Trace.geteventmsg. - * This stop will be done early during process exit when registers are still available, - * allowing the tracer to see where the exit occured, whereas the normal exit - * notification is done after the process is finished exiting. Even though - * context is available, the tracer cannot prevent the exit from happening at - * this point. - * - * - PinkTrace::Trace::OPTION_ALL - * - * This constant represents all option flags bitwise OR'ed together. - * - * == Exceptions - * - * Errno::* errors are raised in case of ptrace errors. - * Check ptrace(2) manual page for more information. - */ - -/* - * Document-method: PinkTrace::Trace.me - * call-seq: PinkTrace::Trace.me() => nil - * - * Indicates that this process is to be traced by its parent. Any signal - * (except SIGKILL) delivered to this process will cause it to stop and its - * parent to be notified via Process.wait. Also, all subsequent calls to - * execve(2) by this process will cause a SIGTRAP to be sent to it, giving the - * parent a chance to gain control before the new program begins execution. - * - * Note: This function is used only by the child process; the rest are used - * only by the parent. - */ -static VALUE -pinkrb_trace_me(PINK_GCC_ATTR((unused)) VALUE mod) -{ - if (!pink_trace_me()) - rb_sys_fail("pink_trace_me()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.cont - * call-seq: PinkTrace::Trace.cont(pid, [[sig=0], [addr=1]]) => nil - * - * Restarts the stopped child process. - * - * If +sig+ argument is non-zero and not SIGSTOP, it is interpreted as the - * signal to be delivered to the child; otherwise, no signal is delivered. - * Thus, for example, the parent can control whether a signal sent to the child - * is delivered or not. - * - * On FreeBSD +addr+ is an address specifying the place where execution - * is to be resumed (a new value for the program counter), or 1 to indicate - * that execution is to pick up where it left off. - * On Linux, this argument is not used. - */ -static VALUE -pinkrb_trace_cont(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - long sig, addr; - VALUE vpid, vsig, vaddr; - - switch (rb_scan_args(argc, argv, "12", &vpid, &vsig, &vaddr)) { - case 1: - addr = 1; - sig = 0; - break; - case 2: - addr = 1; - sig = NUM2LONG(vsig); - break; - case 3: - addr = NUM2LONG(vaddr); - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_cont(pid, sig, (char *)addr)) - rb_sys_fail("pink_trace_cont()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.resume - * call-seq: PinkTrace::Trace.resume(pid, [sig=0]) => nil - * - * Resumes the stopped child process. This is equivalent to - * PinkTrace::Trace.cont(pid, sig, 1) - * - * If +sig+ argument is non-zero and not SIGSTOP, it is interpreted as the - * signal to be delivered to the child; otherwise, no signal is delivered. - * Thus, for example, the parent can control whether a signal sent to the child - * is delivered or not. - */ -static VALUE -pinkrb_trace_resume(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_resume(pid, sig)) - rb_sys_fail("pink_trace_resume()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.kill - * call-seq: PinkTrace::Trace.kill(pid) => nil - * - * Kills the traced child process with SIGKILL. - */ -static VALUE -pinkrb_trace_kill(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vpid) -{ - pid_t pid; - - pid = NUM2PIDT(vpid); - if (!pink_trace_kill(pid)) - rb_sys_fail("pink_trace_kill()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.singlestep - * call-seq: PinkTrace::Trace.singlestep(pid, [sig=0]) => nil - * - * Restarts the stopped child process and arranges it to be stopped after - * execution of a single instruction. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - */ -static VALUE -pinkrb_trace_singlestep(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_singlestep(pid, sig)) - rb_sys_fail("pink_trace_singlestep()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.syscall - * call-seq: PinkTrace::Trace.syscall(pid, [sig=0]) => nil - * - * Restarts the stopped child process and arranges it to be stopped after - * the entry or exit of the next system call. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - */ -static VALUE -pinkrb_trace_syscall(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_syscall(pid, sig)) - rb_sys_fail("pink_trace_syscall()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.syscall_entry - * call-seq: PinkTrace::Trace.syscall_entry(pid, [sig=0]) => nil - * - * Restarts the stopped child process and arranges it to be stopped after - * the entry of next system call. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - * - * Availability: FreeBSD - */ -#if !defined(PINKTRACE_FREEBSD) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_trace_syscall_entry( -#if !defined(PINKTRACE_FREEBSD) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_FREEBSD) - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_syscall_entry(pid, sig)) - rb_sys_fail("pink_trace_syscall_entry()"); - - return Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_FREEBSD) */ -} - -/* - * Document-method: PinkTrace::Trace.syscall_exit - * call-seq: PinkTrace::Trace.syscall_exit(pid, [sig=0]) => nil - * - * Restarts the stopped child process and arranges it to be stopped after - * the exit of next system call. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - * - * Availability: FreeBSD - */ -#if !defined(PINKTRACE_FREEBSD) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_trace_syscall_exit( -#if !defined(PINKTRACE_FREEBSD) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_FREEBSD) - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_syscall_exit(pid, sig)) - rb_sys_fail("pink_trace_syscall_exit()"); - - return Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_FREEBSD) */ -} - -/* - * Document-method: PinkTrace::Trace.sysemu - * call-seq: PinkTrace::Trace.sysemu(pid, [sig=0]) => nil - * - * Restarts the stopped child process and arranges it to be stopped after the - * entry of the next system call which will *not* be executed. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_trace_sysemu( -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_sysemu(pid, sig)) - rb_sys_fail("pink_trace_sysemu()"); - - return Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Trace.sysemu_singlestep - * call-seq: PinkTrace::Trace.sysemu_singlestep(pid, [sig=0]) => nil - * - * Restarts the stopped child process PinkTrace::Trace.sysemu but also - * singlesteps if not a system call. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_trace_sysemu_singlestep( -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_sysemu_singlestep(pid, sig)) - rb_sys_fail("pink_trace_sysemu_singlestep()"); - - return Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Trace.geteventmsg - * call-seq: PinkTrace::Trace.geteventmsg(pid) => fixnum - * - * Returns a message (as a fixnum) about the trace event that just - * happened, For *EXIT* event this is the child's exit status. For *FORK*, - * *VFORK*, *CLONE* and *VFORK_DONE* events this is the process ID of the new - * process. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_trace_geteventmsg(PINK_GCC_ATTR((unused)) VALUE mod, -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) -#endif - VALUE vpid) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - unsigned long data; - - pid = NUM2PIDT(vpid); - if (!pink_trace_geteventmsg(pid, &data)) - rb_sys_fail("pink_trace_geteventmsg()"); - - return ULONG2NUM(data); -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Trace.setup - * call-seq: PinkTrace::Trace.setup(pid, [options=0]) => nil - * - * Sets the tracing options. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_trace_setup( -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - int opts; - VALUE vpid, vopts; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vopts)) { - case 1: - opts = 0; - break; - case 2: - opts = NUM2INT(vopts); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_setup(pid, opts)) - rb_sys_fail("pink_trace_setup()"); - - return Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Trace.attach - * call-seq: PinkTrace::Trace.attach(pid) => nil - * - * Attaches to the process specified in pid, making it a traced "child" of the - * calling process; the behaviour of the child is as if it had done a - * PinkTrace::Trace.me. The child is sent a SIGSTOP, but will not necessarily have - * stopped by the completion of this call; use Process.waitpid to wait for the - * child to stop. - */ -static VALUE -pinkrb_trace_attach(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vpid) -{ - pid_t pid; - - pid = NUM2PIDT(vpid); - if (!pink_trace_attach(pid)) - rb_sys_fail("pink_trace_attach()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Trace.detach - * call-seq: PinkTrace::Trace.detach(pid, [sig=0]) => nil - * - * Restarts the stopped child as for PinkTrace::Trace.cont, but first detaches - * from the process, undoing the reparenting effect of PinkTrace::Trace.attach. - * - * The +sig+ argument is treated as the same way as the +sig+ argument of - * PinkTrace::Trace.cont. - */ -static VALUE -pinkrb_trace_detach(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - long sig; - VALUE vpid, vsig; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { - case 1: - sig = 0; - break; - case 2: - sig = NUM2LONG(vsig); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_trace_detach(pid, sig)) - rb_sys_fail("pink_trace_detach()"); - - return Qnil; -} - -/* - * Document-class: PinkTrace::Event - * - * This class defines constants and functions for event decisions. - * - * == Constants - * - * Note: The constants below are only available on Linux. - * - * - PinkTrace::Event::EVENT_STOP - * - * The traced child has received a SIGSTOP. - * - * - PinkTrace::Event::EVENT_TRAP - * - * The traced child has received a SIGTRAP. - * - * - PinkTrace::Event::EVENT_SYSCALL - * - * The traced child is entering or exiting a system call. - * - * - PinkTrace::Event::EVENT_FORK - * - * The traced child called fork(2). - * - * - PinkTrace::Event::EVENT_VFORK - * - * The traced child called vfork(2). - * - * - PinkTrace::Event::EVENT_CLONE - * - * The traced child called clone(2). - * - * - PinkTrace::Event::EVENT_VFORK_DONE - * - * The traced child is exiting a vfork(2) call. - * - * - PinkTrace::Event::EVENT_EXEC - * - * The traced child is exiting. (ptrace way, stopped before exit) - * - * - PinkTrace::Event::EVENT_GENUINE - * - * The traced child has received a genuine signal. - * - * - PinkTrace::Event::EVENT_EXIT_GENUINE - * - * The traced child has exited normally. - * - * - PinkTrace::Event::EVENT_EXIT_SIGNAL - * - * The traced child has been terminated with a signal. - */ - -/* - * Document-method: PinkTrace::Event.decide - * call-seq: PinkTrace::Event.decide([status=$?.status]) => fixnum - * - * Returns the last event made by child. - */ -static VALUE -pinkrb_event_decide(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - unsigned int event; - int status; - VALUE vstatus, ls; - - switch (rb_scan_args(argc, argv, "01", &vstatus)) { - case 0: - /* Use $?.status */ -#ifdef HAVE_RB_LAST_STATUS_GET /* ruby-1.9 */ - ls = rb_last_status_get(); -#else /* ruby-1.8 */ - ls = rb_gv_get("$?"); -#endif /* HAVE_RB_LAST_STATUS_GET */ - if (NIL_P(ls)) - rb_raise(rb_eTypeError, "$? is nil"); - status = NUM2INT(rb_iv_get(ls, "status")); - break; - case 1: - status = NUM2INT(vstatus); - break; - default: - abort(); - } - - event = pink_event_decide(status); - return UINT2NUM(event); -} - -/* - * Document-class: PinkTrace::Bitness - * - * This class defines constants and functions about bitness. - * - * == Constants - * - * - PinkTrace::Bitness::COUNT_SUPPORTED - * - * Number of supported bitnesses (eg 2 on x86_64, 1 on i386) - * - * - PinkTrace::Bitness::DEFAULT - * - * The default bitness - * - * - PinkTrace::Bitness::BITNESS_32 - * - * 32 bit mode - * - * - PinkTrace::Bitness::BITNESS_64 - * - * 64 bit mode - * - * - PinkTrace::Bitness::BITNESS_32_SUPPORTED - * - * True if 32 bit is supported, False otherwise - * - * - PinkTrace::Bitness::BITNESS_64_SUPPORTED - * - * True if 64 bit is supported, False otherwise - */ - -/* - * Document-method: PinkTrace::Bitness.get - * call-seq: PinkTrace::Bitness.get(pid) => fixnum - * - * Returns the bitness of the traced child. - */ -static VALUE -pinkrb_bitness_get(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vpid) -{ - pid_t pid; - int bit; - - pid = NUM2PIDT(vpid); - bit = pink_bitness_get(pid); - if (bit == PINK_BITNESS_UNKNOWN) - rb_sys_fail("pink_bitness_get()"); - - return INT2FIX(bit); -} - -/* - * Document-method: Pinktrace::Bitness.name - * call-seq: PinkTrace::Bitness.name(bitness) => String - * - * Returns the name of the given bitness. - */ -static VALUE -pinkrb_bitness_name(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vbit) -{ - pink_bitness_t bit; - - bit = FIX2UINT(vbit); - return rb_str_new2(pink_bitness_name(bit)); -} - -/* - * Document-method: PinkTrace::Bitness.wordsize - * call-seq: PinkTrace::Bitness.wordsize(bitness) => Fixnum - * - * Returns the word size of the given bitness. - */ -static VALUE -pinkrb_bitness_wordsize(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vbit) -{ - unsigned short wordsize; - unsigned bit; - - bit = FIX2UINT(vbit); - check_bitness(bit); - wordsize = pink_bitness_wordsize(bit); - return INT2FIX(wordsize); -} - -/* - * Document-class: PinkTrace::Syscall - * - * This class defines utilities useful when tracing processes. - * - * == Constants - * - * - PinkTrace::Syscall::INVALID - * - * This constant is an invalid system call number. You may use this constant - * as an argument to PinkTrace::Syscall.set_no to deny a system call from - * execution. - * - * - PinkTrace::Syscall::MAX_INDEX - * - * The index arguments of system call functions must be smaller than this - * constant. - */ - -/* - * Document-method: PinkTrace::Syscall.name - * call-seq: PinkTrace::Syscall.name(scno, [bitness=PinkTrace::Bitness::DEFAULT]) => String or nil - * - * Return the name of the given system call. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_name_syscall(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - unsigned bit; - long scno; - const char *scname; - VALUE vscno, vbit; - - switch (rb_scan_args(argc, argv, "11", &vscno, &vbit)) { - case 1: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 2: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - scno = NUM2LONG(vscno); - - scname = pink_name_syscall(scno, bit); - return scname ? rb_str_new2(scname) : Qnil; -} - -/* - * Document-method: PinkTrace::Syscall.lookup - * call-seq: PinkTrace::Syscall.lookup(name, [bitness=PinkTrace::Bitness::DEFAULT]) => fixnum - * - * Look up the given system call name. - * Returns -1 if the lookup fails. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_name_lookup(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - unsigned bit; - const char *name; - VALUE vname, vbit; - -#if !defined(RSTRING_PTR) -#define RSTRING_PTR(v) (RSTRING((v))->ptr) -#endif /* !defined(RSTRING_PTR) */ - - switch (rb_scan_args(argc, argv, "11", &vname, &vbit)) { - case 1: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 2: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - SafeStringValue(vname); - name = RSTRING_PTR(vname); - - return LONG2NUM(pink_name_lookup(name, bit)); -} - -/* - * Document-method: PinkTrace::Syscall.get_no - * call-seq: PinkTrace::Syscall.get_no(pid, [bitness=PinkTrace::Bitness::DEFAULT]) => fixnum - * - * Returns the last system call number called by the traced child. - */ -static VALUE -pinkrb_util_get_syscall(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit; - long scno; - VALUE vpid, vbit; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vbit)) { - case 1: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 2: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_util_get_syscall(pid, bit, &scno)) - rb_sys_fail("pink_util_get_syscall()"); - - return LONG2NUM(scno); -} - -/* - * Document-method: PinkTrace::Syscall.set_no - * call-seq: PinkTrace::Syscall.set_no(pid, scno, [bitness=PinkTrace::Bitness::DEFAULT]) => nil - * - * Sets the system call number for the traced child. - */ -static VALUE -pinkrb_util_set_syscall(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit; - long scno; - VALUE vpid, vscno, vbit; - - switch (rb_scan_args(argc, argv, "21", &vpid, &vscno, &vbit)) { - case 2: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 3: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - scno = NUM2LONG(vscno); - - if (!pink_util_set_syscall(pid, bit, scno)) - rb_sys_fail("pink_util_set_syscall()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Syscall.get_ret - * call-seq: PinkTrace::Syscall.get_ret(pid) => fixnum - * - * Returns the return value of the last system call called by the traced child. - */ -static VALUE -pinkrb_util_get_return(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vpid) -{ - pid_t pid; - long ret; - - pid = NUM2PIDT(vpid); - if (!pink_util_get_return(pid, &ret)) - rb_sys_fail("pink_util_get_return()"); - - return LONG2FIX(ret); -} - -/* - * Document-method: PinkTrace::Syscall.set_ret - * call-seq: PinkTrace::Syscall.set_ret(pid, ret) => nil - * - * Set the return value of the system call for the traced child. - */ -static VALUE -pinkrb_util_set_return(PINK_GCC_ATTR((unused)) VALUE mod, VALUE vpid, VALUE vret) -{ - pid_t pid; - long ret; - - pid = NUM2PIDT(vpid); - ret = NUM2LONG(vret); - - if (!pink_util_set_return(pid, ret)) - rb_sys_fail("pink_util_set_return()"); - - return Qnil; -} - -/* - * Document-method: PinkTrace::Syscall.get_arg - * call-seq: PinkTrace::Syscall.get_arg(pid, index, [bitness=PinkTrace::Bitness::Default]) => fixnum - * - * Returns the system call argument at the given index for the traced child. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_util_get_arg(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit, ind; - long arg; - VALUE vpid, vind, vbit; - - switch (rb_scan_args(argc, argv, "21", &vpid, &vind, &vbit)) { - case 2: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 3: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - - if (!pink_util_get_arg(pid, bit, ind, &arg)) - rb_sys_fail("pink_util_get_arg()"); - - return LONG2NUM(arg); -} - -/* - * Document-method: PinkTrace::Syscall.set_arg - * call-seq: PinkTrace::Syscall.set_arg(pid, index, arg, [bitness=PinkTrace::Bitness::Default]) => nil - * - * Sets the system call argument at the specified index to the given value. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_util_set_arg(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit, ind; - long arg; - VALUE vpid, vind, varg, vbit; - - switch (rb_scan_args(argc, argv, "31", &vpid, &vind, &varg, &vbit)) { - case 3: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 4: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - arg = NUM2LONG(varg); - - if (!pink_util_set_arg(pid, bit, ind, arg)) - rb_sys_fail("pink_util_set_arg()"); - - return Qnil; -} - -/* - * Document-class: PinkTrace::String - * - * This class contains functions to decode/encode string arguments. - */ - -/* - * Document-method: PinkTrace::String.decode - * call-seq: PinkTrace::String.decode(pid, index, [[maxlen=-1], [bitness=PinkTrace::Bitness::DEFAULT]]) => String - * - * This function decodes the string at the argument of the given index. If - * +maxlen+ is smaller than zero, which is the default, pinktrace tries to - * determine the length of the string itself. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_decode_string(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit, ind; - int maxlen; - char *str; - VALUE vpid, vind, vmax, vbit, vret; - - switch (rb_scan_args(argc, argv, "22", &vpid, &vind, &vmax, &vbit)) { - case 2: - maxlen = -1; - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 3: - maxlen = NUM2INT(vmax); - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 4: - maxlen = NUM2INT(vmax); - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - - if (maxlen < 0) { - /* Use pink_decode_string_persistent() */ - str = pink_decode_string_persistent(pid, bit, ind); - if (!str) - rb_sys_fail("pink_decode_string_persistent()"); - - vret = rb_str_new2(str); - free(str); - return vret; - } - - /* Use pink_decode_string() */ - str = ALLOC_N(char, maxlen); - if (!pink_decode_string(pid, bit, ind, str, maxlen)) - rb_sys_fail("pink_decode_string()"); - - vret = rb_str_new2(str); - if (str) - free(str); - return vret; -} - -/* - * Document-method: PinkTrace::String.encode - * call-seq: PinkTrace::String.encode(pid, index, str, [bitness=PinkTrace::Bitness::DEFAULT]) => nil - * - * Encode a string into the argument of the given index safely. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_encode_string_safe( -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - unsigned bit, ind; - size_t len; - char *src; - VALUE vpid, vind, vsrc, vbit; - -#if !defined(RSTRING_LEN) -#define RSTRING_LEN(v) (RSTRING((v))->len) -#define RSTRING_PTR(v) (RSTRING((v))->ptr) -#endif /* !defined(RSTRING_LEN) */ - - switch (rb_scan_args(argc, argv, "31", &vpid, &vind, &vsrc, &vbit)) { - case 3: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 4: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - - SafeStringValue(vsrc); - src = RSTRING_PTR(vsrc); - len = RSTRING_LEN(vsrc); - - if (!pink_encode_simple_safe(pid, bit, ind, src, ++len)) - rb_sys_fail("pink_encode_simple_safe()"); - - return Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::String.encode! - * call-seq: PinkTrace::String.encode!(pid, index, str, [bitness=PinkTrace::Bitness::DEFAULT]) => nil - * - * Encode a string into the argument of the given index. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_encode_string(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit, ind; - size_t len; - char *src; - VALUE vpid, vind, vsrc, vbit; - -#if !defined(RSTRING_LEN) -#define RSTRING_LEN(v) (RSTRING((v))->len) -#define RSTRING_PTR(v) (RSTRING((v))->ptr) -#endif /* !defined(RSTRING_LEN) */ - - switch (rb_scan_args(argc, argv, "31", &vpid, &vind, &vsrc, &vbit)) { - case 3: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 4: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - - SafeStringValue(vsrc); - src = RSTRING_PTR(vsrc); - len = RSTRING_LEN(vsrc); - - if (!pink_encode_simple(pid, bit, ind, src, ++len)) - rb_sys_fail("pink_encode_simple()"); - - return Qnil; -} - - /* - * Document-class: PinkTrace::StringArray - * - * This class contains functions to decode NULL-terminated string array members. - */ - -/* - * Document-method: PinkTrace::StringArray.decode - * call-seq: PinkTrace::StringArray.decode(pid, arg, index, [[maxlen=-1], [bitness=PinkTrace::Bitness::DEFAULT]]) => String or nil - * - * This function decodes the member of the string array pointed by the address - * +arg+. The +index+ argument specifies the index of the member in the string - * array. - * - * Note: If the string array member was NULL, this function returns nil. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_decode_strarray(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - bool nil; - pid_t pid; - unsigned bit, ind; - long arg; - int maxlen; - char *str; - VALUE vpid, varg, vind, vmax, vbit, vret; - - switch (rb_scan_args(argc, argv, "32", &vpid, &varg, &vind, &vmax, &vbit)) { - case 3: - maxlen = -1; - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 4: - maxlen = NUM2INT(vmax); - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 5: - maxlen = NUM2INT(vmax); - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - arg = NUM2LONG(varg); - ind = FIX2UINT(vind); - check_index(vind); - - if (maxlen < 0) { - /* Use pink_decode_string_array_member_persistent() */ - errno = 0; - str = pink_decode_string_array_member_persistent(pid, bit, arg, ind); - if (!str) { - if (errno) - rb_sys_fail("pink_decode_string_array_member_persistent()"); - return Qnil; - } - - vret = rb_str_new2(str); - free(str); - return vret; - } - - /* Use pink_decode_string_array_member() */ - str = ALLOC_N(char, maxlen); - if (!pink_decode_string_array_member(pid, bit, arg, ind, str, maxlen, &nil)) - rb_sys_fail("pink_decode_string_array_member()"); - if (nil) { - free(str); - return Qnil; - } - - vret = rb_str_new2(str); - if (str) - free(str); - return vret; -} - -/* - * Document-class: PinkTrace::Socket - * - * This class includes functions for decoding socket calls. - */ - -/* - * Document-method: PinkTrace::Socket.has_socketcall? - * call-seq: PinkTrace::Socket.has_socketcall?([bitness=PinkTrace::Bitness::DEFAULT]) => true or false - * - * Returns true if the socket calls - like connect, bind, sendto etc. - are - * implemented as subcalls of the socketcall(2) system call, false otherwise. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_has_socketcall(PINK_GCC_ATTR((unused)) VALUE mod, -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) -#endif - int argc, -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) -#endif - VALUE *argv) -{ -#if defined(PINKTRACE_LINUX) - unsigned bit; - VALUE vbit; - - switch (rb_scan_args(argc, argv, "01", &vbit)) { - case 0: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 1: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - - return pink_has_socketcall(bit) ? Qtrue : Qfalse; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Socket.name - * call-seq: PinkTrace::Socket.name(subcall) => String or nil - * - * Returns a string representation of the socket subcall. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_name_socket_subcall(PINK_GCC_ATTR((unused)) VALUE mod, -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) -#endif - VALUE vsubcall) -{ -#if defined(PINKTRACE_LINUX) - unsigned subcall; - const char *subname; - - subcall = FIX2UINT(vsubcall); - subname = pink_name_socket_subcall(subcall); - return subname ? rb_str_new2(subname) : Qnil; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Socket.decode_call - * call-seq: PinkTrace::Socket.decode_call(pid, [bitness=PinkTrace::Bitness::DEFAULT]) => fixnum - * - * Returns the decoded socket call. - * - * Note: This function decodes the socketcall(2) system call on some - * architectures. On others it's equivalent to PinkTrace::Syscall.get_no - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_decode_socket_call( -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - unsigned bit; - long subcall; - VALUE vpid, vbit; - - switch (rb_scan_args(argc, argv, "11", &vpid, &vbit)) { - case 1: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 2: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_decode_socket_call(pid, bit, &subcall)) - rb_sys_fail("pink_decode_socket_call()"); - - return LONG2NUM(subcall); -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-method: PinkTrace::Socket.decode_fd - * call-seq: PinkTrace::Socket.decode_fd(pid, [[index=0], [bitness=PinkTrace::Bitness::DEFAULT]]) => fixnum - * - * Returns the socket file descriptor. - * - * Note: This function decodes the socketcall(2) system call on some - * architectures. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - * - * Availability: Linux - */ -#if !defined(PINKTRACE_LINUX) -PINK_GCC_ATTR((noreturn)) -#endif -static VALUE -pinkrb_decode_socket_fd( -#if !defined(PINKTRACE_LINUX) - PINK_GCC_ATTR((unused)) int argc, PINK_GCC_ATTR((unused)) VALUE *argv, -#else - int argc, VALUE *argv, -#endif - PINK_GCC_ATTR((unused)) VALUE mod) -{ -#if defined(PINKTRACE_LINUX) - pid_t pid; - unsigned bit, ind; - long fd; - VALUE vpid, vind, vbit; - - switch (rb_scan_args(argc, argv, "12", &vpid, &vind, &vbit)) { - case 1: - bit = PINKTRACE_BITNESS_DEFAULT; - ind = 0; - break; - case 2: - bit = PINKTRACE_BITNESS_DEFAULT; - ind = FIX2UINT(vind); - check_index(ind); - break; - case 3: - bit = FIX2UINT(vbit); - check_bitness(bit); - ind = FIX2UINT(vind); - check_index(ind); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - - if (!pink_decode_socket_fd(pid, bit, ind, &fd)) - rb_sys_fail("pink_decode_socket_fd()"); - - return LONG2NUM(fd); -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* defined(PINKTRACE_LINUX) */ -} - -/* - * Document-class: PinkTrace::Socket::Address - * - * This class represents a decoded socket address. - */ - -/* - * Document-method: PinkTrace::Socket.decode_address - * call-seq: PinkTrace::Socket.decode_address(pid, index, [bitness=PinkTrace::Bitness::DEFAULT]) => addr - * - * Decodes the socket address at the given index. - * If the system call's address argument was NULL, this function sets - * +addr.family+ to -1. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_decode_socket_address(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit, ind; - pink_socket_address_t *addr; - VALUE vpid, vind, vbit; - VALUE addrObj; - - switch (rb_scan_args(argc, argv, "21", &vpid, &vind, &vbit)) { - case 2: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 3: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - - addrObj = Data_Make_Struct(pinkrb_cAddress, pink_socket_address_t, NULL, free, addr); - - if (!pink_decode_socket_address(pid, bit, ind, NULL, addr)) - rb_sys_fail("pink_decode_socket_address()"); - - return addrObj; -} - -/* - * Document-method: PinkTrace::Socket.decode_address_fd - * call-seq: PinkTrace::Socket.decode_address_fd(pid, index, [bitness=PinkTrace::Bitness::DEFAULT]) => addr, fd - * - * Decodes the socket address at the given index and the file descriptor at index 0. - * If the system call's address argument was NULL this function sets - * +addr.family+ to -1. - * - * Note: PinkTrace::IndexError is raised if +index+ argument is not smaller - * than PinkTrace::Syscall::MAX_INDEX. - * - * Note: PinkTrace::BitnessError is raised if +bitness+ is either unsupported - * or undefined. - */ -static VALUE -pinkrb_decode_socket_address_fd(int argc, VALUE *argv, PINK_GCC_ATTR((unused)) VALUE mod) -{ - pid_t pid; - unsigned bit, ind; - long fd; - pink_socket_address_t *addr; - VALUE vpid, vind, vbit; - VALUE addrObj; - - switch (rb_scan_args(argc, argv, "21", &vpid, &vind, &vbit)) { - case 2: - bit = PINKTRACE_BITNESS_DEFAULT; - break; - case 3: - bit = FIX2UINT(vbit); - check_bitness(bit); - break; - default: - abort(); - } - pid = NUM2PIDT(vpid); - ind = FIX2UINT(vind); - check_index(ind); - - addrObj = Data_Make_Struct(pinkrb_cAddress, pink_socket_address_t, NULL, free, addr); - - if (!pink_decode_socket_address(pid, bit, ind, &fd, addr)) - rb_sys_fail("pink_decode_socket_address()"); - - return rb_assoc_new(addrObj, LONG2NUM(fd)); -} - -/* - * Document-method: family - * call-seq: addr.family => fixnum - * - * Returns the family of the address. - */ -static VALUE -pinkrb_Address_family(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - - return INT2FIX(addr->family); -} - -/* - * Document-method: length - * call-seq: addr.length => fixnum - * - * Returns the length of the address. - */ -static VALUE -pinkrb_Address_length(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - - return INT2FIX(addr->length); -} - -/* - * Document-method: unix? - * call-seq: addr.unix? => true or false - * - * Returns true if the address is of family +AF_UNIX+. - */ -static VALUE -pinkrb_Address_is_unix(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - return (addr->family == AF_UNIX) ? Qtrue : Qfalse; -} - -/* - * Document-method: inet? - * call-seq: addr.inet? => true or false - * - * Returns true if the address is of family +AF_INET+. - */ -static VALUE -pinkrb_Address_is_inet(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - return (addr->family == AF_INET) ? Qtrue : Qfalse; -} - -/* - * Document-method: inet6? - * call-seq: addr.inet6? => true or false - * - * Returns true if the address is of family +AF_INET6+. - */ -#if !PINKTRACE_HAVE_IPV6 -PINK_GCC_ATTR((noreturn)) -#endif /* !PINKTRACE_HAVE_IPV6 */ -static VALUE -pinkrb_Address_is_inet6( -#if !PINKTRACE_HAVE_IPV6 - PINK_GCC_ATTR((unused)) -#endif /* !PINKTRACE_HAVE_IPV6 */ - VALUE self) -{ -#if PINKTRACE_HAVE_IPV6 - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - return (addr->family == AF_INET6) ? Qtrue : Qfalse; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* PINKTRACE_HAVE_IPV6 */ -} - -/* - * Document-method: netlink? - * call-seq: addr.netlink? => true or false - * - * Returns true if the address is of family +AF_NETLINK+. - */ -#if !PINKTRACE_HAVE_NETLINK -PINK_GCC_ATTR((noreturn)) -#endif /* !PINKTRACE_HAVE_NETLINK */ -static VALUE -pinkrb_Address_is_netlink( -#if !PINKTRACE_HAVE_NETLINK - PINK_GCC_ATTR((unused)) -#endif /* !PINKTRACE_HAVE_NETLINK */ - VALUE self) -{ -#if PINKTRACE_HAVE_NETLINK - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - return (addr->family == AF_NETLINK) ? Qtrue : Qfalse; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* PINKTRACE_HAVE_NETLINK */ -} - -/* - * Document-method: abstract? - * call-seq: addr.abstract? => true or false - * - * Returns true if the address is an abstract UNIX socket address. - */ -static VALUE -pinkrb_Address_is_abstract(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - if (addr->family == AF_UNIX && IS_ABSTRACT(addr)) - return Qtrue; - return Qfalse; -} - -/* - * Document-method: path - * call-seq: addr.path => String - * - * Returns the path of the UNIX socket address. - */ -static VALUE -pinkrb_Address_path(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - if (addr->family != AF_UNIX) - rb_raise(rb_eTypeError, "Invalid family"); - return rb_str_new2(addr->u.sa_un.sun_path + (IS_ABSTRACT(addr) ? 1 : 0)); -} - -/* - * Document-method: port - * call-seq: addr.port => Fixnum - * - * Returns the port of an Inet or Inet6 socket address - */ -static VALUE -pinkrb_Address_port(VALUE self) -{ - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - switch (addr->family) { - case AF_INET: - return INT2FIX(ntohs(addr->u.sa_in.sin_port)); -#if PINKTRACE_HAVE_IPV6 - case AF_INET6: - return INT2FIX(ntohs(addr->u.sa6.sin6_port)); -#endif /* PINKTRACE_HAVE_IPV6 */ - default: - rb_raise(rb_eTypeError, "Invalid family"); - } -} - -/* - * Document-method: ip - * call-seq: addr.ip => String - * - * Returns the address of an Inet socket address as a string - */ -static VALUE -pinkrb_Address_ip(VALUE self) -{ - char *ip; - pink_socket_address_t *addr; - VALUE ret; - - Data_Get_Struct(self, pink_socket_address_t, addr); - if (addr->family != AF_INET) - rb_raise(rb_eTypeError, "Invalid family"); - ip = ALLOC_N(char, INET_ADDRSTRLEN); - inet_ntop(AF_INET, &addr->u.sa_in.sin_addr, ip, INET_ADDRSTRLEN); - ret = rb_str_new2(ip); - free(ip); - return ret; -} - -/* - * Document-method: ipv6 - * call-seq: addr.ipv6 => String - * - * Returns the IPV6 address of an Inet6 socket address as string. - */ -#if !PINKTRACE_HAVE_IPV6 -PINK_GCC_ATTR((noreturn)) -#endif /* !PINKTRACE_HAVE_IPV6 */ -static VALUE -pinkrb_Address_ipv6( -#if !PINKTRACE_HAVE_IPV6 - PINK_GCC_ATTR((unused)) -#endif /* !PINKTRACE_HAVE_IPV6 */ - VALUE self) -{ -#if PINKTRACE_HAVE_IPV6 - char *ip; - pink_socket_address_t *addr; - VALUE ret; - - Data_Get_Struct(self, pink_socket_address_t, addr); - if (addr->family != AF_INET6) - rb_raise(rb_eTypeError, "Invalid family"); - ip = ALLOC_N(char, INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, &addr->u.sa6.sin6_addr, ip, INET6_ADDRSTRLEN); - ret = rb_str_new2(ip); - free(ip); - return ret; -#else - rb_raise(rb_eNotImpError, "Not implemented"); -#endif /* PINKTRACE_HAVE_IPV6 */ -} - -/* - * Document-method: pid - * call-seq: addr.pid => Fixnum - * - * Returns the process ID of the netlink socket address - */ -#if !PINKTRACE_HAVE_NETLINK -PINK_GCC_ATTR((noreturn)) -#endif /* !PINKTRACE_HAVE_NETLINK */ -static VALUE -pinkrb_Address_pid( -#if !PINKTRACE_HAVE_NETLINK - PINK_GCC_ATTR((unused)) -#endif /* !PINKTRACE_HAVE_NETLINK */ - VALUE self) -{ -#if PINKTRACE_HAVE_NETLINK - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - if (addr->family == AF_NETLINK) - return PIDT2NUM(addr->u.nl.nl_pid); -#endif /* PINKTRACE_HAVE_NETLINK */ - rb_raise(rb_eNotImpError, "Not implemented"); -} - -/* - * Document-method: groups - * call-seq: addr.groups => Fixnum - * - * Returns the mcast groups mask of the netlink socket address - */ -#if !PINKTRACE_HAVE_NETLINK -PINK_GCC_ATTR((noreturn)) -#endif /* !PINKTRACE_HAVE_NETLINK */ -static VALUE -pinkrb_Address_groups( -#if !PINKTRACE_HAVE_NETLINK - PINK_GCC_ATTR((unused)) -#endif /* !PINKTRACE_HAVE_NETLINK */ - VALUE self) -{ -#if PINKTRACE_HAVE_NETLINK - pink_socket_address_t *addr; - - Data_Get_Struct(self, pink_socket_address_t, addr); - if (addr->family == AF_NETLINK) - return LONG2NUM(addr->u.nl.nl_groups); -#endif /* PINKTRACE_HAVE_NETLINK */ - rb_raise(rb_eNotImpError, "Not implemented"); -} - -void -Init_PinkTrace(void) -{ - VALUE mod; - VALUE bitness_mod; - VALUE event_mod; - VALUE string_mod; - VALUE strarray_mod; - VALUE socket_mod; - VALUE syscall_mod; - VALUE trace_mod; - - /* PinkTrace module */ - mod = rb_define_module("PinkTrace"); - pinkrb_eBitnessError = rb_define_class_under(mod, "BitnessError", rb_eStandardError); - pinkrb_eIndexError = rb_define_class_under(mod, "IndexError", rb_eIndexError); - - /* Global Constants */ - rb_define_const(mod, "HAVE_IPV6", PINKTRACE_HAVE_IPV6 ? Qtrue : Qfalse); - rb_define_const(mod, "HAVE_NETLINK", PINKTRACE_HAVE_NETLINK ? Qtrue : Qfalse); - /* about.h */ - rb_define_const(mod, "PACKAGE", rb_str_new2(PINKTRACE_PACKAGE)); - rb_define_const(mod, "VERSION", INT2FIX(PINKTRACE_VERSION)); - rb_define_const(mod, "VERSION_MAJOR", INT2FIX(PINKTRACE_VERSION_MAJOR)); - rb_define_const(mod, "VERSION_MINOR", INT2FIX(PINKTRACE_VERSION_MINOR)); - rb_define_const(mod, "VERSION_MICRO", INT2FIX(PINKTRACE_VERSION_MICRO)); - rb_define_const(mod, "VERSION_SUFFIX", rb_str_new2(PINKTRACE_VERSION_SUFFIX)); - rb_define_const(mod, "GIT_HEAD", rb_str_new2(PINKTRACE_GIT_HEAD)); - rb_define_const(mod, "PC_SLOT", rb_str_new2(PINKTRACE_PC_SLOT)); - - /* trace.h */ - trace_mod = rb_define_module_under(mod, "Trace"); -#if defined(PINKTRACE_LINUX) - rb_define_const(trace_mod, "OPTION_SYSGOOD", INT2FIX(PINK_TRACE_OPTION_SYSGOOD)); - rb_define_const(trace_mod, "OPTION_FORK", INT2FIX(PINK_TRACE_OPTION_FORK)); - rb_define_const(trace_mod, "OPTION_VFORK", INT2FIX(PINK_TRACE_OPTION_VFORK)); - rb_define_const(trace_mod, "OPTION_CLONE", INT2FIX(PINK_TRACE_OPTION_CLONE)); - rb_define_const(trace_mod, "OPTION_EXEC", INT2FIX(PINK_TRACE_OPTION_EXEC)); - rb_define_const(trace_mod, "OPTION_VFORK_DONE", INT2FIX(PINK_TRACE_OPTION_VFORK_DONE)); - rb_define_const(trace_mod, "OPTION_EXIT", INT2FIX(PINK_TRACE_OPTION_EXIT)); - rb_define_const(trace_mod, "OPTION_ALL", INT2FIX(PINK_TRACE_OPTION_ALL)); -#endif /* defined(PINKTRACE_LINUX) */ - rb_define_module_function(trace_mod, "me", pinkrb_trace_me, 0); - rb_define_module_function(trace_mod, "cont", pinkrb_trace_cont, -1); - rb_define_module_function(trace_mod, "resume", pinkrb_trace_resume, -1); - rb_define_module_function(trace_mod, "kill", pinkrb_trace_kill, 1); - rb_define_module_function(trace_mod, "singlestep", pinkrb_trace_singlestep, -1); - rb_define_module_function(trace_mod, "syscall", pinkrb_trace_syscall, -1); - rb_define_module_function(trace_mod, "syscall_entry", pinkrb_trace_syscall_entry, -1); - rb_define_module_function(trace_mod, "syscall_exit", pinkrb_trace_syscall_exit, -1); - rb_define_module_function(trace_mod, "sysemu", pinkrb_trace_sysemu, -1); - rb_define_module_function(trace_mod, "sysemu_singlestep", pinkrb_trace_sysemu_singlestep, -1); - rb_define_module_function(trace_mod, "geteventmsg", pinkrb_trace_geteventmsg, 1); - rb_define_module_function(trace_mod, "setup", pinkrb_trace_setup, -1); - rb_define_module_function(trace_mod, "attach", pinkrb_trace_attach, 1); - rb_define_module_function(trace_mod, "detach", pinkrb_trace_detach, -1); - - /* event.h */ - event_mod = rb_define_module_under(mod, "Event"); - rb_define_const(event_mod, "EVENT_STOP", INT2FIX(PINK_EVENT_STOP)); - rb_define_const(event_mod, "EVENT_TRAP", INT2FIX(PINK_EVENT_TRAP)); - rb_define_const(event_mod, "EVENT_SYSCALL", INT2FIX(PINK_EVENT_SYSCALL)); - rb_define_const(event_mod, "EVENT_FORK", INT2FIX(PINK_EVENT_FORK)); - rb_define_const(event_mod, "EVENT_VFORK", INT2FIX(PINK_EVENT_VFORK)); - rb_define_const(event_mod, "EVENT_CLONE", INT2FIX(PINK_EVENT_CLONE)); - rb_define_const(event_mod, "EVENT_VFORK_DONE", INT2FIX(PINK_EVENT_VFORK_DONE)); - rb_define_const(event_mod, "EVENT_EXEC", INT2FIX(PINK_EVENT_EXEC)); - rb_define_const(event_mod, "EVENT_EXIT", INT2FIX(PINK_EVENT_EXIT)); - rb_define_const(event_mod, "EVENT_GENUINE", INT2FIX(PINK_EVENT_GENUINE)); - rb_define_const(event_mod, "EVENT_EXIT_GENUINE", INT2FIX(PINK_EVENT_EXIT_GENUINE)); - rb_define_const(event_mod, "EVENT_EXIT_SIGNAL", INT2FIX(PINK_EVENT_EXIT_SIGNAL)); - rb_define_const(event_mod, "EVENT_UNKNOWN", INT2FIX(PINK_EVENT_UNKNOWN)); - rb_define_module_function(event_mod, "decide", pinkrb_event_decide, -1); - - /* bitness.h */ - bitness_mod = rb_define_module_under(mod, "Bitness"); - rb_define_const(bitness_mod, "COUNT_SUPPORTED", INT2FIX(PINKTRACE_BITNESS_COUNT_SUPPORTED)); - rb_define_const(bitness_mod, "DEFAULT", INT2FIX(PINKTRACE_BITNESS_DEFAULT)); - rb_define_const(bitness_mod, "BITNESS_32", INT2FIX(PINK_BITNESS_32)); - rb_define_const(bitness_mod, "BITNESS_64", INT2FIX(PINK_BITNESS_64)); - rb_define_const(bitness_mod, "BITNESS_32_SUPPORTED", PINKTRACE_BITNESS_32_SUPPORTED ? Qtrue : Qfalse); - rb_define_const(bitness_mod, "BITNESS_64_SUPPORTED", PINKTRACE_BITNESS_64_SUPPORTED ? Qtrue : Qfalse); - rb_define_module_function(bitness_mod, "get", pinkrb_bitness_get, 1); - rb_define_module_function(bitness_mod, "name", pinkrb_bitness_name, 1); - rb_define_module_function(bitness_mod, "wordsize", pinkrb_bitness_wordsize, 1); - - /* util.h && name.h */ - syscall_mod = rb_define_module_under(mod, "Syscall"); - rb_define_const(syscall_mod, "INVALID", INT2FIX(PINKTRACE_INVALID_SYSCALL)); - rb_define_const(syscall_mod, "MAX_INDEX", INT2FIX(PINK_MAX_INDEX)); - rb_define_module_function(syscall_mod, "name", pinkrb_name_syscall, -1); - rb_define_module_function(syscall_mod, "lookup", pinkrb_name_lookup, -1); - rb_define_module_function(syscall_mod, "get_no", pinkrb_util_get_syscall, -1); - rb_define_module_function(syscall_mod, "set_no", pinkrb_util_set_syscall, -1); - rb_define_module_function(syscall_mod, "get_ret", pinkrb_util_get_return, 1); - rb_define_module_function(syscall_mod, "set_ret", pinkrb_util_set_return, 2); - rb_define_module_function(syscall_mod, "get_arg", pinkrb_util_get_arg, -1); - rb_define_module_function(syscall_mod, "set_arg", pinkrb_util_set_arg, -1); - - /* decode.h && encode.h (only string {en,de}coding) */ - string_mod = rb_define_module_under(mod, "String"); - rb_define_module_function(string_mod, "decode", pinkrb_decode_string, -1); - rb_define_module_function(string_mod, "encode", pinkrb_encode_string_safe, -1); - rb_define_module_function(string_mod, "encode!", pinkrb_encode_string, -1); - - /* decode.h (only string array decoding */ - strarray_mod = rb_define_module_under(mod, "StringArray"); - rb_define_module_function(strarray_mod, "decode", pinkrb_decode_strarray, -1); - - /* decode.h && socket.h */ - socket_mod = rb_define_module_under(mod, "Socket"); - rb_define_module_function(socket_mod, "has_socketcall?", pinkrb_has_socketcall, -1); - rb_define_module_function(socket_mod, "name", pinkrb_name_socket_subcall, 1); - rb_define_module_function(socket_mod, "decode_call", pinkrb_decode_socket_call, -1); - rb_define_module_function(socket_mod, "decode_fd", pinkrb_decode_socket_fd, -1); - - /* Address Objects */ - pinkrb_cAddress = rb_define_class_under(socket_mod, "Address", rb_cObject); - - /* The address objects are only returned by PinkTrace::Socket.decode_address; - * thus we don't need an initialize method. */ - rb_undef_method(pinkrb_cAddress, "initialize"); - - /* Address methods */ - rb_define_method(pinkrb_cAddress, "family", pinkrb_Address_family, 0); - rb_define_method(pinkrb_cAddress, "length", pinkrb_Address_length, 0); - rb_define_method(pinkrb_cAddress, "unix?", pinkrb_Address_is_unix, 0); - rb_define_method(pinkrb_cAddress, "inet?", pinkrb_Address_is_inet, 0); - rb_define_method(pinkrb_cAddress, "inet6?", pinkrb_Address_is_inet6, 0); - rb_define_method(pinkrb_cAddress, "netlink?", pinkrb_Address_is_netlink, 0); - rb_define_method(pinkrb_cAddress, "abstract?", pinkrb_Address_is_abstract, 0); - rb_define_method(pinkrb_cAddress, "path", pinkrb_Address_path, 0); - rb_define_method(pinkrb_cAddress, "port", pinkrb_Address_port, 0); - rb_define_method(pinkrb_cAddress, "ip", pinkrb_Address_ip, 0); - rb_define_method(pinkrb_cAddress, "ipv6", pinkrb_Address_ipv6, 0); - rb_define_method(pinkrb_cAddress, "pid", pinkrb_Address_pid, 0); - rb_define_method(pinkrb_cAddress, "groups", pinkrb_Address_groups, 0); - - rb_define_module_function(socket_mod, "decode_address", pinkrb_decode_socket_address, -1); - rb_define_module_function(socket_mod, "decode_address_fd", pinkrb_decode_socket_address_fd, -1); -} diff --git a/ruby/socket.c b/ruby/socket.c new file mode 100644 index 00000000..261d7428 --- /dev/null +++ b/ruby/socket.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::Socket.has_socketcall? + * call-seq: + * PinkTrace::Socket.has_socketcall?([bitness=PinkTrace::Bitness::DEFAULT]) -> true or false + * + * Returns true if the socket calls - like connect, bind, sendto etc. - are + * implemented as subcalls of the socketcall(2) system call, false otherwise. + * + * Availability: Linux + */ +VALUE +pinkrb_has_socketcall(VALUE mod, int argc, VALUE *argv) +{ +#ifdef PINKTRACE_LINUX + unsigned bit; + VALUE vbit; + + switch (rb_scan_args(argc, argv, "01", &vbit)) { + case 0: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 1: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + + return pink_has_socketcall(bit) ? Qtrue : Qfalse; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Socket.name + * call-seq: + * PinkTrace::Socket.name(subcall) -> String or nil + * + * Returns a string representation of the socket subcall. + * + * Availability: Linux + */ +VALUE +pinkrb_name_socket_subcall(VALUE mod, VALUE vsubcall) +{ +#ifdef PINKTRACE_LINUX + unsigned subcall; + const char *subname; + + subcall = FIX2UINT(vsubcall); + subname = pink_name_socket_subcall(subcall); + return subname ? rb_str_new2(subname) : Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Socket.decode_call + * call-seq: + * PinkTrace::Socket.decode_call(pid, [bitness=PinkTrace::Bitness::DEFAULT]) -> fixnum + * + * Returns the decoded socket call. + * + * Note: This function decodes the socketcall(2) system call on some + * architectures. On others it's equivalent to PinkTrace::Syscall.get_no + * + * Availability: Linux + */ +VALUE +pinkrb_decode_socket_call(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + unsigned bit; + long subcall; + VALUE vpid, vbit; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vbit)) { + case 1: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 2: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_decode_socket_call(pid, bit, &subcall)) + rb_sys_fail("pink_decode_socket_call()"); + + return LONG2NUM(subcall); +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Socket.decode_fd + * call-seq: + * PinkTrace::Socket.decode_fd(pid, [[index=0], [bitness=PinkTrace::Bitness::DEFAULT]]) -> fixnum + * + * Returns the socket file descriptor. + * + * Note: This function decodes the socketcall(2) system call on some + * architectures. + * + * Availability: Linux + */ +VALUE +pinkrb_decode_socket_fd(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + unsigned bit, ind; + long fd; + VALUE vpid, vind, vbit; + + switch (rb_scan_args(argc, argv, "12", &vpid, &vind, &vbit)) { + case 1: + bit = PINKTRACE_BITNESS_DEFAULT; + ind = 0; + break; + case 2: + bit = PINKTRACE_BITNESS_DEFAULT; + ind = FIX2UINT(vind); + break; + case 3: + bit = FIX2UINT(vbit); + ind = FIX2UINT(vind); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_decode_socket_fd(pid, bit, ind, &fd)) + rb_sys_fail("pink_decode_socket_fd()"); + + return LONG2NUM(fd); +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Socket.decode_address + * call-seq: + * PinkTrace::Socket.decode_address(pid, index, [bitness=PinkTrace::Bitness::DEFAULT]) -> addr + * + * Decodes the socket address at the given index. + * If the system call's address argument was NULL, this function sets + * +addr.family+ to -1. + */ +VALUE +pinkrb_decode_socket_address(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit, ind; + pink_socket_address_t *addr; + VALUE vpid, vind, vbit; + VALUE addrObj; + + switch (rb_scan_args(argc, argv, "21", &vpid, &vind, &vbit)) { + case 2: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 3: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + + addrObj = Data_Make_Struct(pinkrb_cAddress, pink_socket_address_t, NULL, free, addr); + + if (!pink_decode_socket_address(pid, bit, ind, NULL, addr)) + rb_sys_fail("pink_decode_socket_address()"); + + return addrObj; +} + +/* + * Document-method: PinkTrace::Socket.decode_address_fd + * call-seq: + * PinkTrace::Socket.decode_address_fd(pid, index, [bitness=PinkTrace::Bitness::DEFAULT]) -> addr, fd + * + * Decodes the socket address at the given index and the file descriptor at index 0. + * If the system call's address argument was NULL this function sets + * +addr.family+ to -1. + */ +VALUE +pinkrb_decode_socket_address_fd(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit, ind; + long fd; + pink_socket_address_t *addr; + VALUE vpid, vind, vbit; + VALUE addrObj; + + switch (rb_scan_args(argc, argv, "21", &vpid, &vind, &vbit)) { + case 2: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 3: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + + addrObj = Data_Make_Struct(pinkrb_cAddress, pink_socket_address_t, NULL, free, addr); + + if (!pink_decode_socket_address(pid, bit, ind, &fd, addr)) + rb_sys_fail("pink_decode_socket_address()"); + + return rb_assoc_new(addrObj, LONG2NUM(fd)); +} + +/* + * Document-method: family + * call-seq: + * addr.family -> fixnum + * + * Returns the family of the address. + */ +VALUE +pinkrb_Address_family(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + + return INT2FIX(addr->family); +} + +/* + * Document-method: length + * call-seq: + * addr.length -> fixnum + * + * Returns the length of the address. + */ +VALUE +pinkrb_Address_length(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + + return INT2FIX(addr->length); +} + +/* + * Document-method: unix? + * call-seq: + * addr.unix? -> true or false + * + * Returns true if the address is of family +AF_UNIX+. + */ +VALUE +pinkrb_Address_is_unix(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + return (addr->family == AF_UNIX) ? Qtrue : Qfalse; +} + +/* + * Document-method: inet? + * call-seq: + * addr.inet? -> true or false + * + * Returns true if the address is of family +AF_INET+. + */ +VALUE +pinkrb_Address_is_inet(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + return (addr->family == AF_INET) ? Qtrue : Qfalse; +} + +/* + * Document-method: inet6? + * call-seq: + * addr.inet6? -> true or false + * + * Returns true if the address is of family +AF_INET6+. + */ +VALUE +pinkrb_Address_is_inet6(VALUE self) +{ +#if PINKTRACE_HAVE_IPV6 + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + return (addr->family == AF_INET6) ? Qtrue : Qfalse; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: netlink? + * call-seq: + * addr.netlink? -> true or false + * + * Returns true if the address is of family +AF_NETLINK+. + */ +VALUE +pinkrb_Address_is_netlink(VALUE self) +{ +#if PINKTRACE_HAVE_NETLINK + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + return (addr->family == AF_NETLINK) ? Qtrue : Qfalse; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: abstract? + * call-seq: + * addr.abstract? -> true or false + * + * Returns true if the address is an abstract UNIX socket address. + */ +VALUE +pinkrb_Address_is_abstract(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + if (addr->family == AF_UNIX && IS_ABSTRACT(addr)) + return Qtrue; + return Qfalse; +} + +/* + * Document-method: path + * call-seq: + * addr.path -> String + * + * Returns the path of the UNIX socket address. + */ +VALUE +pinkrb_Address_path(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + if (addr->family != AF_UNIX) + rb_raise(rb_eTypeError, "Invalid family"); + return rb_str_new2(addr->u.sa_un.sun_path + (IS_ABSTRACT(addr) ? 1 : 0)); +} + +/* + * Document-method: port + * call-seq: + * addr.port -> Fixnum + * + * Returns the port of an Inet or Inet6 socket address + */ +VALUE +pinkrb_Address_port(VALUE self) +{ + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + switch (addr->family) { + case AF_INET: + return INT2FIX(ntohs(addr->u.sa_in.sin_port)); +#if PINKTRACE_HAVE_IPV6 + case AF_INET6: + return INT2FIX(ntohs(addr->u.sa6.sin6_port)); +#endif + default: + rb_raise(rb_eTypeError, "Invalid family"); + } +} + +/* + * Document-method: ip + * call-seq: + * addr.ip -> String + * + * Returns the address of an Inet socket address as a string + */ +VALUE +pinkrb_Address_ip(VALUE self) +{ + char *ip; + pink_socket_address_t *addr; + VALUE ret; + + Data_Get_Struct(self, pink_socket_address_t, addr); + if (addr->family != AF_INET) + rb_raise(rb_eTypeError, "Invalid family"); + ip = ALLOC_N(char, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &addr->u.sa_in.sin_addr, ip, INET_ADDRSTRLEN); + ret = rb_str_new2(ip); + free(ip); + return ret; +} + +/* + * Document-method: ipv6 + * call-seq: + * addr.ipv6 -> String + * + * Returns the IPV6 address of an Inet6 socket address as string. + */ +VALUE +pinkrb_Address_ipv6(VALUE self) +{ +#if PINKTRACE_HAVE_IPV6 + char *ip; + pink_socket_address_t *addr; + VALUE ret; + + Data_Get_Struct(self, pink_socket_address_t, addr); + if (addr->family != AF_INET6) + rb_raise(rb_eTypeError, "Invalid family"); + ip = ALLOC_N(char, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &addr->u.sa6.sin6_addr, ip, INET6_ADDRSTRLEN); + ret = rb_str_new2(ip); + free(ip); + return ret; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: pid + * call-seq: + * addr.pid -> Fixnum + * + * Returns the process ID of the netlink socket address + */ +VALUE +pinkrb_Address_pid(VALUE self) +{ +#if PINKTRACE_HAVE_NETLINK + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + if (addr->family == AF_NETLINK) + return PIDT2NUM(addr->u.nl.nl_pid); +#endif + rb_raise(rb_eNotImpError, "Not implemented"); +} + +/* + * Document-method: groups + * call-seq: + * addr.groups -> Fixnum + * + * Returns the mcast groups mask of the netlink socket address + */ +VALUE +pinkrb_Address_groups(VALUE self) +{ +#if PINKTRACE_HAVE_NETLINK + pink_socket_address_t *addr; + + Data_Get_Struct(self, pink_socket_address_t, addr); + if (addr->family == AF_NETLINK) + return LONG2NUM(addr->u.nl.nl_groups); +#endif + rb_raise(rb_eNotImpError, "Not implemented"); +} diff --git a/ruby/strarray.c b/ruby/strarray.c new file mode 100644 index 00000000..bfc05461 --- /dev/null +++ b/ruby/strarray.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::StringArray.decode + * call-seq: + * PinkTrace::StringArray.decode(pid, arg, index, [[maxlen=-1], [bitness=PinkTrace::Bitness::DEFAULT]]) -> String or nil + * + * This function decodes the member of the string array pointed by the address + * +arg+. The +index+ argument specifies the index of the member in the string + * array. + * + * Note: If the string array member was NULL, this function returns nil. + */ +VALUE +pinkrb_decode_strarray(int argc, VALUE *argv, VALUE mod) +{ + bool nil; + pid_t pid; + unsigned bit, ind; + long arg; + int maxlen; + char *str; + VALUE vpid, varg, vind, vmax, vbit, vret; + + switch (rb_scan_args(argc, argv, "32", &vpid, &varg, &vind, &vmax, &vbit)) { + case 3: + maxlen = -1; + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 4: + maxlen = NUM2INT(vmax); + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 5: + maxlen = NUM2INT(vmax); + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + arg = NUM2LONG(varg); + ind = FIX2UINT(vind); + + if (maxlen < 0) { + /* Use pink_decode_string_array_member_persistent() */ + errno = 0; + str = pink_decode_string_array_member_persistent(pid, bit, arg, ind); + if (!str) { + if (errno) + rb_sys_fail("pink_decode_string_array_member_persistent()"); + return Qnil; + } + + vret = rb_str_new2(str); + free(str); + return vret; + } + + /* Use pink_decode_string_array_member() */ + str = ALLOC_N(char, maxlen); + if (!pink_decode_string_array_member(pid, bit, arg, ind, str, maxlen, &nil)) + rb_sys_fail("pink_decode_string_array_member()"); + if (nil) { + free(str); + return Qnil; + } + + vret = rb_str_new2(str); + if (str) + free(str); + return vret; +} diff --git a/ruby/string.c b/ruby/string.c new file mode 100644 index 00000000..7269b0fc --- /dev/null +++ b/ruby/string.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::String.decode + * call-seq: + * PinkTrace::String.decode(pid, index, [[maxlen=-1], [bitness=PinkTrace::Bitness::DEFAULT]]) -> String + * + * This function decodes the string at the argument of the given index. If + * +maxlen+ is smaller than zero, which is the default, pinktrace tries to + * determine the length of the string itself. + */ +VALUE +pinkrb_decode_string(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit, ind; + int maxlen; + char *str; + VALUE vpid, vind, vmax, vbit, vret; + + switch (rb_scan_args(argc, argv, "22", &vpid, &vind, &vmax, &vbit)) { + case 2: + maxlen = -1; + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 3: + maxlen = NUM2INT(vmax); + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 4: + maxlen = NUM2INT(vmax); + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + + if (maxlen < 0) { + /* Use pink_decode_string_persistent() */ + str = pink_decode_string_persistent(pid, bit, ind); + if (!str) + rb_sys_fail("pink_decode_string_persistent()"); + + vret = rb_str_new2(str); + free(str); + return vret; + } + + /* Use pink_decode_string() */ + str = ALLOC_N(char, maxlen); + if (!pink_decode_string(pid, bit, ind, str, maxlen)) + rb_sys_fail("pink_decode_string()"); + + vret = rb_str_new2(str); + if (str) + free(str); + return vret; +} + +/* + * Document-method: PinkTrace::String.encode + * call-seq: + * PinkTrace::String.encode(pid, index, str, [bitness=PinkTrace::Bitness::DEFAULT]) -> nil + * + * Encode a string into the argument of the given index safely. + * + * Availability: Linux + */ +VALUE +pinkrb_encode_string_safe(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + unsigned bit, ind; + size_t len; + char *src; + VALUE vpid, vind, vsrc, vbit; + + switch (rb_scan_args(argc, argv, "31", &vpid, &vind, &vsrc, &vbit)) { + case 3: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 4: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + + SafeStringValue(vsrc); + src = RSTRING_PTR(vsrc); + len = RSTRING_LEN(vsrc); + + if (!pink_encode_simple_safe(pid, bit, ind, src, ++len)) + rb_sys_fail("pink_encode_simple_safe()"); + + return Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::String.encode! + * call-seq: + * PinkTrace::String.encode!(pid, index, str, [bitness=PinkTrace::Bitness::DEFAULT]) -> nil + * + * Encode a string into the argument of the given index. + */ +VALUE +pinkrb_encode_string(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit, ind; + size_t len; + char *src; + VALUE vpid, vind, vsrc, vbit; + + switch (rb_scan_args(argc, argv, "31", &vpid, &vind, &vsrc, &vbit)) { + case 3: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 4: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + + SafeStringValue(vsrc); + src = RSTRING_PTR(vsrc); + len = RSTRING_LEN(vsrc); + + if (!pink_encode_simple(pid, bit, ind, src, ++len)) + rb_sys_fail("pink_encode_simple()"); + + return Qnil; +} diff --git a/ruby/syscall.c b/ruby/syscall.c new file mode 100644 index 00000000..5fba6bd4 --- /dev/null +++ b/ruby/syscall.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::Syscall.name + * call-seq: + * PinkTrace::Syscall.name(scno, [bitness=PinkTrace::Bitness::DEFAULT]) -> String or nil + * + * Return the name of the given system call. + */ +VALUE +pinkrb_name_syscall(int argc, VALUE *argv, VALUE mod) +{ + unsigned bit; + long scno; + const char *scname; + VALUE vscno, vbit; + + switch (rb_scan_args(argc, argv, "11", &vscno, &vbit)) { + case 1: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 2: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + scno = NUM2LONG(vscno); + + scname = pink_name_syscall(scno, bit); + return scname ? rb_str_new2(scname) : Qnil; +} + +/* + * Document-method: PinkTrace::Syscall.lookup + * call-seq: + * PinkTrace::Syscall.lookup(name, [bitness=PinkTrace::Bitness::DEFAULT]) -> fixnum + * + * Look up the given system call name. + * Returns -1 if the lookup fails. + */ +VALUE +pinkrb_name_lookup(int argc, VALUE *argv, VALUE mod) +{ + unsigned bit; + const char *name; + VALUE vname, vbit; + +#if !defined(RSTRING_PTR) +#define RSTRING_PTR(v) (RSTRING((v))->ptr) +#endif /* !defined(RSTRING_PTR) */ + + switch (rb_scan_args(argc, argv, "11", &vname, &vbit)) { + case 1: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 2: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + SafeStringValue(vname); + name = RSTRING_PTR(vname); + + return LONG2NUM(pink_name_lookup(name, bit)); +} + +/* + * Document-method: PinkTrace::Syscall.get_no + * call-seq: + * PinkTrace::Syscall.get_no(pid, [bitness=PinkTrace::Bitness::DEFAULT]) -> fixnum + * + * Returns the last system call number called by the traced child. + */ +VALUE +pinkrb_util_get_syscall(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit; + long scno; + VALUE vpid, vbit; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vbit)) { + case 1: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 2: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_util_get_syscall(pid, bit, &scno)) + rb_sys_fail("pink_util_get_syscall()"); + + return LONG2NUM(scno); +} + +/* + * Document-method: PinkTrace::Syscall.set_no + * call-seq: + * PinkTrace::Syscall.set_no(pid, scno, [bitness=PinkTrace::Bitness::DEFAULT]) -> nil + * + * Sets the system call number for the traced child. + */ +VALUE +pinkrb_util_set_syscall(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit; + long scno; + VALUE vpid, vscno, vbit; + + switch (rb_scan_args(argc, argv, "21", &vpid, &vscno, &vbit)) { + case 2: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 3: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + scno = NUM2LONG(vscno); + + if (!pink_util_set_syscall(pid, bit, scno)) + rb_sys_fail("pink_util_set_syscall()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Syscall.get_ret + * call-seq: + * PinkTrace::Syscall.get_ret(pid) -> fixnum + * + * Returns the return value of the last system call called by the traced child. + */ +VALUE +pinkrb_util_get_return(VALUE mod, VALUE vpid) +{ + pid_t pid; + long ret; + + pid = NUM2PIDT(vpid); + if (!pink_util_get_return(pid, &ret)) + rb_sys_fail("pink_util_get_return()"); + + return LONG2FIX(ret); +} + +/* + * Document-method: PinkTrace::Syscall.set_ret + * call-seq: + * PinkTrace::Syscall.set_ret(pid, ret) -> nil + * + * Set the return value of the system call for the traced child. + */ +VALUE +pinkrb_util_set_return(VALUE mod, VALUE vpid, VALUE vret) +{ + pid_t pid; + long ret; + + pid = NUM2PIDT(vpid); + ret = NUM2LONG(vret); + + if (!pink_util_set_return(pid, ret)) + rb_sys_fail("pink_util_set_return()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Syscall.get_arg + * call-seq: + * PinkTrace::Syscall.get_arg(pid, index, [bitness=PinkTrace::Bitness::Default]) -> fixnum + * + * Returns the system call argument at the given index for the traced child. + * + */ +VALUE +pinkrb_util_get_arg(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit, ind; + long arg; + VALUE vpid, vind, vbit; + + switch (rb_scan_args(argc, argv, "21", &vpid, &vind, &vbit)) { + case 2: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 3: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + + if (!pink_util_get_arg(pid, bit, ind, &arg)) + rb_sys_fail("pink_util_get_arg()"); + + return LONG2NUM(arg); +} + +/* + * Document-method: PinkTrace::Syscall.set_arg + * call-seq: + * PinkTrace::Syscall.set_arg(pid, index, arg, [bitness=PinkTrace::Bitness::Default]) -> nil + * + * Sets the system call argument at the specified index to the given value. + */ +VALUE +pinkrb_util_set_arg(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + unsigned bit, ind; + long arg; + VALUE vpid, vind, varg, vbit; + + switch (rb_scan_args(argc, argv, "31", &vpid, &vind, &varg, &vbit)) { + case 3: + bit = PINKTRACE_BITNESS_DEFAULT; + break; + case 4: + bit = FIX2UINT(vbit); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + ind = FIX2UINT(vind); + arg = NUM2LONG(varg); + + if (!pink_util_set_arg(pid, bit, ind, arg)) + rb_sys_fail("pink_util_set_arg()"); + + return Qnil; +} diff --git a/ruby/trace.c b/ruby/trace.c new file mode 100644 index 00000000..fffd8eea --- /dev/null +++ b/ruby/trace.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2010, 2011 Ali Polatel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "globals.h" + +/* + * Document-method: PinkTrace::Trace.me + * call-seq: + * PinkTrace::Trace.me() -> nil + * + * Indicates that this process is to be traced by its parent. Any signal + * (except SIGKILL) delivered to this process will cause it to stop and its + * parent to be notified via Process.wait. Also, all subsequent calls to + * execve(2) by this process will cause a SIGTRAP to be sent to it, giving the + * parent a chance to gain control before the new program begins execution. + * + * Note: This function is used only by the child process; the rest are used + * only by the parent. + */ +VALUE +pinkrb_trace_me(VALUE mod) +{ + if (!pink_trace_me()) + rb_sys_fail("pink_trace_me()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.cont + * call-seq: + * PinkTrace::Trace.cont(pid, [[sig=0], [addr=1]]) -> nil + * + * Restarts the stopped child process. + * + * If +sig+ argument is non-zero and not SIGSTOP, it is interpreted as the + * signal to be delivered to the child; otherwise, no signal is delivered. + * Thus, for example, the parent can control whether a signal sent to the child + * is delivered or not. + * + * On FreeBSD +addr+ is an address specifying the place where execution + * is to be resumed (a new value for the program counter), or 1 to indicate + * that execution is to pick up where it left off. + * On Linux, this argument is not used. + */ +VALUE +pinkrb_trace_cont(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + long sig, addr; + VALUE vpid, vsig, vaddr; + + switch (rb_scan_args(argc, argv, "12", &vpid, &vsig, &vaddr)) { + case 1: + addr = 1; + sig = 0; + break; + case 2: + addr = 1; + sig = NUM2LONG(vsig); + break; + case 3: + addr = NUM2LONG(vaddr); + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_cont(pid, sig, (char *)addr)) + rb_sys_fail("pink_trace_cont()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.resume + * call-seq: + * PinkTrace::Trace.resume(pid, [sig=0]) -> nil + * + * Resumes the stopped child process. This is equivalent to + * PinkTrace::Trace.cont(pid, sig, 1) + * + * If +sig+ argument is non-zero and not SIGSTOP, it is interpreted as the + * signal to be delivered to the child; otherwise, no signal is delivered. + * Thus, for example, the parent can control whether a signal sent to the child + * is delivered or not. + */ +VALUE +pinkrb_trace_resume(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_resume(pid, sig)) + rb_sys_fail("pink_trace_resume()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.kill + * call-seq: + * PinkTrace::Trace.kill(pid) -> nil + * + * Kills the traced child process with SIGKILL. + */ +VALUE +pinkrb_trace_kill(VALUE mod, VALUE vpid) +{ + pid_t pid; + + pid = NUM2PIDT(vpid); + if (!pink_trace_kill(pid)) + rb_sys_fail("pink_trace_kill()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.singlestep + * call-seq: + * PinkTrace::Trace.singlestep(pid, [sig=0]) -> nil + * + * Restarts the stopped child process and arranges it to be stopped after + * execution of a single instruction. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + */ +VALUE +pinkrb_trace_singlestep(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_singlestep(pid, sig)) + rb_sys_fail("pink_trace_singlestep()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.syscall + * call-seq: + * PinkTrace::Trace.syscall(pid, [sig=0]) -> nil + * + * Restarts the stopped child process and arranges it to be stopped after + * the entry or exit of the next system call. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + */ +VALUE +pinkrb_trace_syscall(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_syscall(pid, sig)) + rb_sys_fail("pink_trace_syscall()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.syscall_entry + * call-seq: + * PinkTrace::Trace.syscall_entry(pid, [sig=0]) -> nil + * + * Restarts the stopped child process and arranges it to be stopped after + * the entry of next system call. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + * + * Availability: FreeBSD + */ +VALUE +pinkrb_trace_syscall_entry(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_FREEBSD + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_syscall_entry(pid, sig)) + rb_sys_fail("pink_trace_syscall_entry()"); + + return Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Trace.syscall_exit + * call-seq: + * PinkTrace::Trace.syscall_exit(pid, [sig=0]) -> nil + * + * Restarts the stopped child process and arranges it to be stopped after + * the exit of next system call. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + * + * Availability: FreeBSD + */ +VALUE +pinkrb_trace_syscall_exit(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_FREEBSD + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_syscall_exit(pid, sig)) + rb_sys_fail("pink_trace_syscall_exit()"); + + return Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif /* defined(PINKTRACE_FREEBSD) */ +} + +/* + * Document-method: PinkTrace::Trace.sysemu + * call-seq: + * PinkTrace::Trace.sysemu(pid, [sig=0]) -> nil + * + * Restarts the stopped child process and arranges it to be stopped after the + * entry of the next system call which will *not* be executed. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + * + * Availability: Linux + */ +VALUE +pinkrb_trace_sysemu(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_sysemu(pid, sig)) + rb_sys_fail("pink_trace_sysemu()"); + + return Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Trace.sysemu_singlestep + * call-seq: + * PinkTrace::Trace.sysemu_singlestep(pid, [sig=0]) -> nil + * + * Restarts the stopped child process PinkTrace::Trace.sysemu but also + * singlesteps if not a system call. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + * + * Availability: Linux + */ +VALUE +pinkrb_trace_sysemu_singlestep(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_sysemu_singlestep(pid, sig)) + rb_sys_fail("pink_trace_sysemu_singlestep()"); + + return Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Trace.geteventmsg + * call-seq: + * PinkTrace::Trace.geteventmsg(pid) -> fixnum + * + * Returns a message (as a fixnum) about the trace event that just + * happened, For *EXIT* event this is the child's exit status. For *FORK*, + * *VFORK*, *CLONE* and *VFORK_DONE* events this is the process ID of the new + * process. + * + * Availability: Linux + */ +VALUE +pinkrb_trace_geteventmsg(VALUE mod, VALUE vpid) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + unsigned long data; + + pid = NUM2PIDT(vpid); + if (!pink_trace_geteventmsg(pid, &data)) + rb_sys_fail("pink_trace_geteventmsg()"); + + return ULONG2NUM(data); +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Trace.setup + * call-seq: + * PinkTrace::Trace.setup(pid, [options=0]) -> nil + * + * Sets the tracing options. + * + * Availability: Linux + */ +VALUE +pinkrb_trace_setup(int argc, VALUE *argv, VALUE mod) +{ +#ifdef PINKTRACE_LINUX + pid_t pid; + int opts; + VALUE vpid, vopts; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vopts)) { + case 1: + opts = 0; + break; + case 2: + opts = NUM2INT(vopts); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_setup(pid, opts)) + rb_sys_fail("pink_trace_setup()"); + + return Qnil; +#else + rb_raise(rb_eNotImpError, "Not implemented"); +#endif +} + +/* + * Document-method: PinkTrace::Trace.attach + * call-seq: + * PinkTrace::Trace.attach(pid) -> nil + * + * Attaches to the process specified in pid, making it a traced "child" of the + * calling process; the behaviour of the child is as if it had done a + * PinkTrace::Trace.me. The child is sent a SIGSTOP, but will not necessarily have + * stopped by the completion of this call; use Process.waitpid to wait for the + * child to stop. + */ +VALUE +pinkrb_trace_attach(VALUE mod, VALUE vpid) +{ + pid_t pid; + + pid = NUM2PIDT(vpid); + if (!pink_trace_attach(pid)) + rb_sys_fail("pink_trace_attach()"); + + return Qnil; +} + +/* + * Document-method: PinkTrace::Trace.detach + * call-seq: + * PinkTrace::Trace.detach(pid, [sig=0]) -> nil + * + * Restarts the stopped child as for PinkTrace::Trace.cont, but first detaches + * from the process, undoing the reparenting effect of PinkTrace::Trace.attach. + * + * The +sig+ argument is treated as the same way as the +sig+ argument of + * PinkTrace::Trace.cont. + */ +VALUE +pinkrb_trace_detach(int argc, VALUE *argv, VALUE mod) +{ + pid_t pid; + long sig; + VALUE vpid, vsig; + + switch (rb_scan_args(argc, argv, "11", &vpid, &vsig)) { + case 1: + sig = 0; + break; + case 2: + sig = NUM2LONG(vsig); + break; + default: + abort(); + } + pid = NUM2PIDT(vpid); + + if (!pink_trace_detach(pid, sig)) + rb_sys_fail("pink_trace_detach()"); + + return Qnil; +}