diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/Makefile | 5 | ||||
-rw-r--r-- | utils/rpctrace.c | 218 |
2 files changed, 220 insertions, 3 deletions
diff --git a/utils/Makefile b/utils/Makefile index 955789b7..352494ae 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -34,7 +34,7 @@ SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \ nullauth.c match-options.c msgids.c rpcscan.c OBJS = $(filter-out %.sh,$(SRCS:.c=.o)) -HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc +HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc introspection LDLIBS += -lpthread login-LDLIBS = -lutil -lcrypt addauth-LDLIBS = -lcrypt @@ -67,7 +67,8 @@ ps w ids settrans syncfs showtrans fsysopts storeinfo login vmstat portinfo \ $(filter-out $(special-targets), $(targets)): %: %.o -rpctrace: ../libports/libports.a +rpctrace: ../libports/libports.a \ + ../libintrospection/libintrospection.a hurd_portUser.o rpctrace rpcscan: msgids.o \ ../libihash/libihash.a msgids-CPPFLAGS = -DDATADIR=\"${datadir}\" diff --git a/utils/rpctrace.c b/utils/rpctrace.c index d53b510f..c3c311d5 100644 --- a/utils/rpctrace.c +++ b/utils/rpctrace.c @@ -23,6 +23,7 @@ #include <hurd.h> #include <hurd/ports.h> #include <hurd/ihash.h> +#include <hurd/introspection.h> #include <mach/message.h> #include <assert.h> #include <fcntl.h> @@ -40,6 +41,7 @@ #include <envz.h> #include "msgids.h" +#include "hurd_port_U.h" const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace); @@ -48,6 +50,11 @@ static unsigned strsize = 80; static const struct argp_option options[] = { {"output", 'o', "FILE", 0, "Send trace output to FILE instead of stderr."}, + {"pid", 'p', "PID", 0, "Attach to PID and trace all requests to objects " + "of the same class as the given reference port. This will only work " + "for Hurd servers implementing the introspection protocol."}, + {"reference-port", 'P', "PORT", 0, "Trace all requests to ports of the " + "same class as PORT. PORT must denote a receive right in PID."}, {0, 's', "SIZE", 0, "Specify the maximum string size to print (the default is 80)."}, {0, 'E', "var[=value]", 0, "Set/change (var=value) or remove (var) an environment variable among the " @@ -852,7 +859,7 @@ print_contents (mach_msg_header_t *inp, what task that port name is meaningful in. If it's meaningful in a traced task, then it refers to our intercepting port rather than the original port anyway. */ - if (MACH_MSG_TYPE_PORT_ANY_RIGHT (name)) + if (MACH_MSG_TYPE_PORT_ANY_RIGHT (name) && req != NULL) { /* These are port rights. Translate them into wrappers. */ mach_port_t *const portnames = data; @@ -1669,10 +1676,196 @@ traced_spawn (char **argv, char **envp) return pid; } + +/* Return the task corresponding to the user argument ARG, exiting with an + appriate error message if we can't. */ +static task_t +parse_task (char *arg) +{ + error_t err; + task_t task; + char *arg_end; + pid_t pid = strtoul (arg, &arg_end, 10); + static process_t proc = MACH_PORT_NULL; + + if (*arg == '\0' || *arg_end != '\0') + error (10, 0, "%s: Invalid process id", arg); + + if (proc == MACH_PORT_NULL) + proc = getproc (); + + err = proc_pid2task (proc, pid, &task); + if (err) + error (11, err, "%s", arg); + else if (task == MACH_PORT_NULL) + error (11, 0, "%s: Process %d is dead and has no task", arg, (int) pid); + + return task; +} + +static mach_port_t trace_notification_port; + +boolean_t +trace_server (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + error_t err; + struct msgid_info *info; + int is_reply; + + if (inp->msgh_local_port == trace_notification_port + && inp->msgh_id == MACH_NOTIFY_NO_SENDERS) + { + error (0, 0, "The tracee vanished."); + exit (EXIT_SUCCESS); + } + + err = introspection_extract_message (inp); + if (err) + { + error (0, err, "introspection_extract_message"); + goto out; + } + info = msgid_info (inp->msgh_id); + + /* XXX This hardcodes an assumption about reply message ids. */ + is_reply = (inp->msgh_id / 100) % 2 == 1; + if (is_reply) + { + /* This looks like a traced reply or a pseudo-reply. A + pseudo-reply is a message containing the result of a simple + procedure that is only sent to us. */ + mig_reply_header_t *reply = (mig_reply_header_t *) inp; + + if (last_reply_port != inp->msgh_remote_port) + { + print_ellipsis (); + fprintf (ostream, "%u...", (unsigned int) inp->msgh_remote_port); + } + last_reply_port = MACH_PORT_NULL; + + fprintf (ostream, " = "); + + if (reply->RetCode == 0) + fprintf (ostream, "0"); + else + { + const char *str = strerror (reply->RetCode); + if (str == 0) + fprintf (ostream, "%#x", reply->RetCode); + else + fprintf (ostream, "%#x (%s)", reply->RetCode, str); + } + + if (inp->msgh_size > sizeof *reply) + { + fprintf (ostream, " "); + print_contents (inp, (void *) inp + sizeof *reply, NULL); + } + fprintf (ostream, "\n"); + } + else + { + /* This looks like a traced request. */ + print_ellipsis (); + last_reply_port = inp->msgh_remote_port; + + if (info) + fprintf (ostream, "% 4d->%s (", inp->msgh_local_port, info->name); + else + fprintf (ostream, "% 4d->%d (", inp->msgh_local_port, inp->msgh_id); + + print_contents (inp, (void *) inp + sizeof *inp, NULL); + fprintf (ostream, ")"); + } + + out: + /* vm_deallocate any out-of-band memory. */ + mach_msg_destroy (inp); + + /* Prevent mach_msg_server from sending messages. */ + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + return TRUE; +} int +trace_class_rpcs (mach_port_t task, + mach_port_t name) +{ + error_t err; + mach_port_t trace_port; + mach_port_t introspection_port; + mach_port_t previous; + mach_port_t port_set; + + err = introspection_get_port (task, &introspection_port); + if (err) + error (13, err, "Failed to get introspection port"); + + if (! MACH_PORT_VALID (introspection_port)) + error (13, 0, "The server does not implement the introspection protocol"); + + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &trace_port); + if (err) + error (13, err, "mach_port_allocate"); + + err = hurd_port_trace_class_rpcs (introspection_port, name, + trace_port, MACH_MSG_TYPE_MAKE_SEND); + if (err) + { + if (err == EINVAL) + error (13, 0, + "%d does not denote a receive right managed by libports", name); + else + error (13, err, "hurd_port_trace_class_rpcs"); + } + + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &trace_notification_port); + if (err) + error (13, err, "mach_port_allocate"); + + err = mach_port_request_notification (mach_task_self (), + trace_port, + MACH_NOTIFY_NO_SENDERS, + 0, + trace_notification_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &previous); + if (err) + error (13, err, "mach_port_request_notification"); + assert (! MACH_PORT_VALID (previous)); + + + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET, + &port_set); + if (err) + error (13, err, "mach_port_allocate"); + + err = mach_port_move_member (mach_task_self (), trace_port, port_set); + if (err) + error (13, err, "mach_port_move_member"); + + err = mach_port_move_member (mach_task_self (), trace_notification_port, + port_set); + if (err) + error (13, err, "mach_port_move_member"); + + error (0, 0, "entering service loop"); + while (1) + mach_msg_server (trace_server, 0, port_set); + + /* Not reached. */ + return 0; +} + +int main (int argc, char **argv, char **envp) { + mach_port_t target_task = MACH_PORT_NULL; + mach_port_t reference_port = MACH_PORT_NULL; + bool nostdinc = FALSE; const char *outfile = 0; char **cmd_argv = 0; pthread_t thread; @@ -1684,12 +1877,23 @@ main (int argc, char **argv, char **envp) /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) { + char *arg_end; switch (key) { case 'o': outfile = arg; break; + case 'p': + target_task = parse_task (arg); + break; + + case 'P': + reference_port = strtoul (arg, &arg_end, 10); + if (*arg == '\0' || *arg_end != '\0') + argp_error (state, "Invalid port name: %s", arg); + break; + case 's': strsize = atoi (arg); break; @@ -1723,10 +1927,16 @@ main (int argc, char **argv, char **envp) break; case ARGP_KEY_NO_ARGS: + if (MACH_PORT_VALID (target_task)) + break; + argp_usage (state); return EINVAL; case ARGP_KEY_ARG: + if (MACH_PORT_VALID (target_task)) + argp_error (state, "Superfluous argument: %s", arg); + cmd_argv = &state->argv[state->next - 1]; state->next = state->argc; break; @@ -1746,6 +1956,9 @@ main (int argc, char **argv, char **envp) /* Parse our arguments. */ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); + if (MACH_PORT_VALID (target_task) != MACH_PORT_VALID (reference_port)) + error (10, 0, "Please specify either both -p and -P, or neither."); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_DEAD_NAME, &unknown_task); assert_perror (err); @@ -1760,6 +1973,9 @@ main (int argc, char **argv, char **envp) ostream = stderr; setlinebuf (ostream); + if (MACH_PORT_VALID (target_task)) + return trace_class_rpcs (target_task, reference_port); + traced_bucket = ports_create_bucket (); traced_class = ports_create_class (&traced_clean, NULL); other_class = ports_create_class (0, 0); |