diff options
author | Roland McGrath <roland@gnu.org> | 1999-07-18 01:10:17 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 1999-07-18 01:10:17 +0000 |
commit | 04f6d64131cd6520bde3501958e8267c04f8881a (patch) | |
tree | 5ec75a82458b8fecb5eca6515321142ee1030076 /utils | |
parent | 89fcb0de47291e9ac0e3667a6322bc886bd7f0aa (diff) |
1999-07-17 Roland McGrath <roland@baalperazim.frob.com>
* rpctrace.c: Mostly rewritten. Now mostly working with skeletal
functionality.
Diffstat (limited to 'utils')
-rw-r--r-- | utils/rpctrace.c | 708 |
1 files changed, 570 insertions, 138 deletions
diff --git a/utils/rpctrace.c b/utils/rpctrace.c index bf6e71d5..316e5cce 100644 --- a/utils/rpctrace.c +++ b/utils/rpctrace.c @@ -29,7 +29,9 @@ #include <unistd.h> #include <argp.h> #include <error.h> +#include <string.h> #include <version.h> +#include <sys/wait.h> const char *argp_program_version = STANDARD_HURD_VERSION (rpctrace); @@ -43,180 +45,542 @@ static const char *doc = "Trace Mach Remote Procedure Calls." "\v."; +pid_t traced_spawn (char **argv, char **envp); + struct traced_info { struct port_info pi; + union + { + struct traced_info *nextfree; /* Link when on free list. */ + + /* For a send right wrapper, the position in the traced_names hash table. + For a send-once right wrapper, this is null. */ + void **locp; +#define INFO_SEND_ONCE(info) ((info)->u.locp == 0) + } u; mach_port_t forward; }; +static struct traced_info *freelist; + task_t traced_task; ihash_t traced_names; struct port_class *traced_class; struct port_bucket *traced_bucket; FILE *ostream; -cthread_t traced_thread; -char *cmd; -char **cmd_argv; -char **cmd_envp; -int print_message (FILE *stream, mach_msg_header_t *msg); -#if 1 -int -print_message (FILE *stream, mach_msg_header_t *msg) +/* Create a new wrapper port and do `ports_get_right' on it. */ +static mach_port_t +new_send_wrapper (mach_port_t right) { - fprintf (stream, "[%d] %d -> %d\n", msg->msgh_id, - msg->msgh_local_port, msg->msgh_remote_port); - return 0; + error_t err; + struct traced_info *info; + mach_port_t wrapper; + + + /* Use a free send-once wrapper port if we have one. */ + if (freelist) + { + info = freelist; + freelist = info->u.nextfree; + } + else + { + /* Create a new wrapper port that forwards to *RIGHT. */ + err = ports_create_port (traced_class, traced_bucket, + sizeof *info, &info); + assert_perror (err); + } + + info->forward = right; + + /* Store it in the reverse-lookup hash table, so we can + look up this same right again to find the wrapper port. + The entry in the hash table holds a weak ref on INFO. */ + err = ihash_add (traced_names, info->forward, info, &info->u.locp); + assert_perror (err); + ports_port_ref_weak (info); + assert (info->u.locp != 0); + + wrapper = ports_get_right (info); + ports_port_deref (info); + + return wrapper; +} + +/* Create a new wrapper port and do `ports_get_right' on it. */ +static mach_port_t +new_send_once_wrapper (mach_port_t right) +{ + error_t err; + struct traced_info *info; + mach_port_t wrapper; + + /* Use a free send-once wrapper port if we have one. */ + if (freelist) + { + info = freelist; + freelist = info->u.nextfree; + } + else + { + /* Create a new wrapper port that forwards to *RIGHT. */ + err = ports_create_port (traced_class, traced_bucket, + sizeof *info, &info); + assert_perror (err); + } + + info->forward = right; + + /* Send-once never compare equal to any other right (even another + send-once right), so there is no point in putting them in the + reverse-lookup table. + + Since we never make send rights to this port, we don't want to + use the normal libports mechanisms (ports_get_right) that are + designed for send rights and no-senders notifications. + Instead, we hold on to the initial hard ref to INFO until we + receive a message on it. The kernel automatically sends a + MACH_NOTIFY_SEND_ONCE message if the send-once right dies. */ + + info->u.locp = 0; /* Used to mark this as send-once. */ + wrapper = info->pi.port_right; + + return wrapper; +} + + +/* This gets called when a wrapper port has no hard refs (send rights), + only weak refs. The only weak ref is the one held in the reverse-lookup + hash table. */ +static void +traced_dropweak (void *pi) +{ + struct traced_info *const info = pi; + + assert (info->u.locp); + + /* Remove INFO from the hash table. */ + ihash_locp_remove (traced_names, info->u.locp); + ports_port_deref_weak (info); + + /* Deallocate the forward port, so the real port also sees no-senders. */ + mach_port_deallocate (mach_task_self (), info->forward); + + /* There are no rights to this port, so we can reuse it. + Add a hard ref and put INFO on the free list. */ + ports_port_ref (info); + info->u.nextfree = freelist; + freelist = info; } -#endif -mach_port_t -trace_wrapper (mach_port_t orig) + + +/* Rewrite a port right in a message with an appropriate wrapper port. */ +static error_t +rewrite_right (mach_port_t *right, mach_msg_type_name_t *type) { + error_t err; struct traced_info *info; - mach_port_t port = MACH_PORT_NULL; - /* Never wrap the null port. */ - if (orig == MACH_PORT_NULL) - return orig; + /* We can never do anything special with a null or dead port right. */ + if (!MACH_PORT_VALID (*right)) + return 0; - info = ihash_find (traced_names, orig); - if (! info) + switch (*type) { - error_t err; - mach_port_type_t type; - mach_msg_type_name_t typename; - - ports_create_port (traced_class, traced_bucket, - sizeof (struct traced_info), &info); - port = ports_get_right (info); - info->forward = MACH_PORT_NULL; - - mach_port_type (traced_task, orig, &type); - /* FIXME: type == 0x11bdf68: how do I get the type of right? */ - - if (type & MACH_PORT_TYPE_SEND) + case MACH_MSG_TYPE_PORT_SEND: + /* See if we are already tracing this port. */ + info = ihash_find (traced_names, *right); + if (info) { - err = mach_port_extract_right (traced_task, orig, - MACH_MSG_TYPE_MOVE_SEND, - &info->forward, &typename); - assert_perror (err); - err = mach_port_insert_right (traced_task, orig, port, - MACH_MSG_TYPE_MAKE_SEND); - assert_perror (err); + /* We are already tracing this port. We will pass on a right + to our existing wrapper port. */ + *right = ports_get_right (info); + *type = MACH_MSG_TYPE_MAKE_SEND; + return 0; } - if (type & MACH_PORT_TYPE_SEND_ONCE) - { - err = mach_port_extract_right (traced_task, orig, - MACH_MSG_TYPE_MOVE_SEND_ONCE, - &info->forward, &typename); - assert_perror (err); - err = mach_port_insert_right (traced_task, orig, port, - MACH_MSG_TYPE_MAKE_SEND_ONCE); - assert_perror (err); - } - if (info->forward == MACH_PORT_NULL) + + /* See if this is already one of our own wrapper ports. */ + info = ports_lookup_port (traced_bucket, *right, 0); + if (info) { - err = mach_port_extract_right (traced_task, orig, - MACH_MSG_TYPE_MAKE_SEND, - &info->forward, &typename); - assert_perror (err); + /* This is a send right to an existing wrapper port, + so just send it on through. */ + ports_port_deref (info); + return 0; } - ihash_add (traced_names, orig, info, NULL); + /* We have never seen this port before. Create a new wrapper port + and replace the right in the message with a right to it. */ + *right = new_send_wrapper (*right); + *type = MACH_MSG_TYPE_MAKE_SEND; + return 0; + + case MACH_MSG_TYPE_PORT_SEND_ONCE: + /* There is no way to know if this send-once right is to the same + receive right as any other send-once or send right we have seen. + Fortunately, it doesn't matter, since the recipient of the + send-once right we pass along can't tell either. We always just + make a new send-once wrapper object, that will trace the one + message it receives, and then die. */ + *type = MACH_MSG_TYPE_MAKE_SEND_ONCE; + *right = new_send_once_wrapper (*right); + return 0; + case MACH_MSG_TYPE_PORT_RECEIVE: + /* We have got a receive right, call it A. We will pass along a + different receive right of our own, call it B. We ourselves will + receive messages on A, trace them, and forward them on to B. + + If A is the receive right to a send right that we have wrapped, + then B must be that wrapper receive right, moved from us to the + intended receiver of A--that way it matches previous send rights + to A that were sent through and replaced with our wrapper (B). + If not, we create a new receive right. */ + { + mach_port_t rr; /* B */ + + info = ihash_find (traced_names, *right); + if (info) + { + /* This is a receive right that we have been tracing sends to. */ + rr = ports_claim_right (info); + /* That released the refs on INFO, so it's been freed now. */ + } + else + /* This is a port we know nothing about. */ + rr = mach_reply_port (); + + /* Create a new wrapper object that receives on this port. */ + err = ports_import_port (traced_class, traced_bucket, + *right, sizeof *info, &info); + assert_perror (err); + + /* Get us a send right that we will forward on. */ + err = mach_port_insert_right (mach_task_self (), rr, rr, + MACH_MSG_TYPE_MAKE_SEND); + assert_perror (err); + info->forward = rr; + + err = ihash_add (traced_names, info->forward, info, &info->u.locp); + assert_perror (err); + ports_port_ref_weak (info); + + /* If there are no extant send rights to this port, then INFO will + die right here and release its send right to RR. + XXX what to do? + */ + ports_port_deref (info); + + *right = rr; + return 0; + } + + default: + assert (!"??? bogus port type from kernel!"); + return EGRATUITOUS; } - else - port = ports_get_right (info); - mach_port_insert_right (mach_task_self (), port, port, - MACH_MSG_TYPE_MAKE_SEND); - return port; } + +static void +print_header (mach_msg_header_t *msg) +{ + fprintf (ostream, + "msgid %d, %d bytes in msg\n" + "\treply port %d (type %d)\n", + msg->msgh_id, + msg->msgh_size, + msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(msg->msgh_bits)); +} + +static void +print_data (mach_msg_type_name_t type, + const void *data, + mach_msg_type_number_t nelt, + mach_msg_type_number_t eltsize) +{ + switch (type) + { + case MACH_MSG_TYPE_STRING: + fprintf (ostream, "\t\"%.*s\"\n", + (int) (nelt * eltsize), (const char *) data); + break; + + case MACH_MSG_TYPE_BIT: + case MACH_MSG_TYPE_INTEGER_8: + case MACH_MSG_TYPE_INTEGER_16: + case MACH_MSG_TYPE_INTEGER_32: + case MACH_MSG_TYPE_INTEGER_64: + case MACH_MSG_TYPE_CHAR: + case MACH_MSG_TYPE_REAL: + default: + /* XXX */ + fprintf (ostream, "\t%#x (type %d, %d*%d)\n", *(const int *)data, type, + nelt, eltsize); + break; + } +} + + int trace_and_forward (mach_msg_header_t *inp, mach_msg_header_t *outp) { + error_t err; struct traced_info *info; - void *msg_buf_ptr = (void *) inp + sizeof (*inp); - - /* Print the message trace. */ - print_message (ostream, inp); fflush (ostream); + void *msg_buf_ptr = inp + 1; + mach_msg_bits_t complex; - /* Wrap ports. */ - while (msg_buf_ptr < (void *) inp + inp->msgh_size) + /* Look up our record for the receiving port. There is no need to check + the class, because our port bucket only ever contains one class of + ports (traced_class). */ + info = ports_lookup_port (traced_bucket, inp->msgh_local_port, 0); + assert (info); + + if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) { - mach_msg_type_t type = *(mach_msg_type_t *) msg_buf_ptr; - msg_buf_ptr += sizeof (mach_msg_type_t); - if (type.msgt_name == MACH_MSG_TYPE_PORT_NAME) + if (inp->msgh_id == MACH_NOTIFY_DEAD_NAME) + { + /* If INFO is a send-once wrapper, this could be a forged + notification; oh well. XXX */ + + const mach_dead_name_notification_t *const n = (void *) inp; + + assert (n->not_port == info->forward); + /* Deallocate extra ref allocated by the notification. */ + mach_port_deallocate (mach_task_self (), n->not_port); + ports_destroy_right (info); + ports_port_deref (info); + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + return 1; + } + else if (inp->msgh_id == MACH_NOTIFY_NO_SENDERS + && !INFO_SEND_ONCE (info)) { - mach_port_t *port = msg_buf_ptr; - *port = trace_wrapper (*port); + /* No more senders for a send right we are tracing. Now INFO + will die, and we will release the tracee send right so it too + can see a no-senders notification. */ + mach_no_senders_notification_t *n = (void *) inp; + ports_no_senders (info, n->not_count); + ports_port_deref (info); + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; + return 1; } - msg_buf_ptr += type.msgt_size / 8; } - info = ports_lookup_port (traced_bucket, inp->msgh_local_port, - traced_class); - if (! info) - return 0; + complex = inp->msgh_bits & MACH_MSGH_BITS_COMPLEX; + + /* Print something about the message header. */ + fprintf (ostream, "port %d(=>%d) receives (type %d) ", + inp->msgh_local_port, info->forward, + MACH_MSGH_BITS_LOCAL (inp->msgh_bits)); + print_header (inp); /* Swap the header data like a crossover cable. */ - inp->msgh_bits - = MACH_MSGH_BITS (MACH_MSGH_BITS_LOCAL (inp->msgh_bits), - MACH_MSGH_BITS_REMOTE (inp->msgh_bits)); - inp->msgh_local_port = trace_wrapper (inp->msgh_remote_port); - inp->msgh_remote_port = info->forward; + { + mach_msg_type_name_t this_type = MACH_MSGH_BITS_LOCAL (inp->msgh_bits); + mach_msg_type_name_t reply_type = MACH_MSGH_BITS_REMOTE (inp->msgh_bits); + + inp->msgh_local_port = inp->msgh_remote_port; + if (reply_type) + { + err = rewrite_right (&inp->msgh_local_port, &reply_type); + assert_perror (err); + } + + inp->msgh_remote_port = info->forward; + if (this_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) + { + /* We have a message to forward for a send-once wrapper object. + Since each wrapper object only lives for a single message, this + one can be reclaimed now. We continue to hold a hard ref to the + ports object, but we know that nothing else refers to it now, and + we are consuming its `forward' right in the message we send. */ + info->u.nextfree = freelist; + freelist = info; + } + else + this_type = MACH_MSG_TYPE_COPY_SEND; + + inp->msgh_bits = complex | MACH_MSGH_BITS (this_type, reply_type); + } + + if (inp->msgh_id == 3215) /* mach_port_insert_right */ + { + /* XXX + */ + msg_buf_ptr = (void *) inp + inp->msgh_size; + } + + /* Process the message data, wrapping ports and printing data. */ + while (msg_buf_ptr < (void *) inp + inp->msgh_size) + { + mach_msg_type_t *const type = msg_buf_ptr; + mach_msg_type_long_t *const lt = (void *) type; + void *data; + mach_msg_type_number_t nelt; /* Number of data items. */ + mach_msg_type_size_t eltsize; /* Bytes per item. */ + mach_msg_type_name_t name; /* MACH_MSG_TYPE_* code */ + + if (!type->msgt_longform) + { + name = type->msgt_name; + nelt = type->msgt_number; + eltsize = type->msgt_size / 8; + data = msg_buf_ptr = type + 1; + } + else + { + name = lt->msgtl_name; + nelt = lt->msgtl_number; + eltsize = lt->msgtl_size / 8; + data = msg_buf_ptr = lt + 1; + } + + if (!type->msgt_inline) + { + /* This datum is out-of-line, meaning the message actually + contains a pointer to a vm_allocate'd region of data. */ + data = *(void **) data; + msg_buf_ptr += sizeof (void *); + } + else + msg_buf_ptr += ((nelt * eltsize + sizeof(natural_t) - 1) + & ~(sizeof(natural_t) - 1)); + + /* Note that MACH_MSG_TYPE_PORT_NAME does not indicate a port right. + It indicates a port name, i.e. just an integer--and we don't know + 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)) + { + /* These are port rights. Translate them into wrappers. */ + mach_port_t *const portnames = data; + mach_msg_type_number_t i; + mach_msg_type_name_t newtypes[nelt]; + int poly; + + assert (complex); + assert (eltsize == sizeof (mach_port_t)); + + fprintf (ostream, "\t%d ports, type %d\n", nelt, name); + + poly = 0; + for (i = 0; i < nelt; ++i) + { + mach_port_t o=portnames[i]; + newtypes[i] = name; + err = rewrite_right (&portnames[i], &newtypes[i]); + + if (portnames[i] == MACH_PORT_NULL) + fprintf (ostream, "\t\t[%d] = null\n", i); + else if (portnames[i] == MACH_PORT_DEAD) + fprintf (ostream, "\t\t[%d] = dead name\n", i); + else + fprintf (ostream, + "\t\t[%d] = port %d, type %d => port %d, type %d\n", + i, o, name, portnames[i], newtypes[i]); + + if (i > 0 && newtypes[i] != newtypes[0]) + poly = 1; + } + + if (poly) + { + if (name == MACH_MSG_TYPE_MOVE_SEND_ONCE) + { + /* Some of the new rights are MAKE_SEND_ONCE. + Turn them all into MOVE_SEND_ONCE. */ + for (i = 0; i < nelt; ++i) + if (newtypes[i] == MACH_MSG_TYPE_MAKE_SEND_ONCE) + { + err = mach_port_insert_right (mach_task_self (), + portnames[i], + portnames[i], + newtypes[i]); + assert_perror (err); + } + else + assert (newtypes[i] == MACH_MSG_TYPE_MOVE_SEND_ONCE); + } + else + { + for (i = 0; i < nelt; ++i) + switch (newtypes[i]) + { + case MACH_MSG_TYPE_COPY_SEND: + err = mach_port_mod_refs (mach_task_self (), + portnames[i], + MACH_PORT_TYPE_SEND, +1); + assert_perror (err); + break; + case MACH_MSG_TYPE_MAKE_SEND: + err = mach_port_insert_right (mach_task_self (), + portnames[i], + portnames[i], + newtypes[i]); + assert_perror (err); + break; + default: + assert (newtypes[i] == MACH_MSG_TYPE_MOVE_SEND); + break; + } + + name = MACH_MSG_TYPE_MOVE_SEND; + } + (type->msgt_longform ? lt->msgtl_name : type->msgt_name) = name; + } + else if (newtypes[0] != name) + (type->msgt_longform ? lt->msgtl_name : type->msgt_name) + = newtypes[0]; + } + else + print_data (name, data, nelt, eltsize); + } /* Resend the message to the tracee. */ - mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0, - MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0, + MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if (err == MACH_SEND_INVALID_DEST) + { + /* The tracee port died. No doubt we are about to receive the dead-name + notification. */ + /* XXX MAKE_SEND, MAKE_SEND_ONCE rights in msg not handled */ + mach_msg_destroy (inp); + } + else + assert_perror (err); + + ports_port_deref (info); + + /* We already sent the message, so the server loop shouldn't do it again. */ + ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY; return 1; } + +/* This function runs in the tracing thread and drives all the tracing. */ static any_t -trace_thread_function (struct port_bucket *bucket) +trace_thread_function (void *arg) { + struct port_bucket *const bucket = arg; ports_manage_port_operations_one_thread (bucket, trace_and_forward, 0); return 0; } -static any_t -traced_thread_function (struct port_bucket *bucket) -{ - mach_port_t wrapper; - struct traced_info *info; - file_t file = file_name_path_lookup (cmd, getenv ("PATH"), O_EXEC, 0, 0); - - if (file == MACH_PORT_NULL) - error (1, errno, "cannot lookup `%s' in PATH", cmd); - task_create (mach_task_self (), 0, &traced_task); - - /* Create a trace wrapper for the task port. */ - ports_create_port (traced_class, traced_bucket, sizeof (*info), &info); - info->forward = traced_task; - ihash_add (traced_names, wrapper, info, NULL); - - /* Make sure the traced task uses this wrapper. */ - wrapper = ports_get_right (info); - mach_port_insert_right (mach_task_self (), wrapper, - wrapper, MACH_MSG_TYPE_MAKE_SEND); - task_set_special_port (traced_task, TASK_KERNEL_PORT, wrapper); - - /* Now actually run the command they told us to trace. */ - proc_child (getproc (), traced_task); - _hurd_exec (traced_task, file, cmd_argv, cmd_envp); - - /* FIXME: falls through immediately if we don't spin */ - for (;;) ; - return 0; -} int main (int argc, char **argv, char **envp) { + const char *outfile = 0; + char **cmd_argv = 0; + error_t err; /* Parse our options... */ error_t parse_opt (int key, char *arg, struct argp_state *state) @@ -224,20 +588,16 @@ main (int argc, char **argv, char **envp) switch (key) { case 'o': - ostream = fopen (arg, "w"); + outfile = arg; break; - + case ARGP_KEY_NO_ARGS: argp_usage (state); return EINVAL; case ARGP_KEY_ARG: - cmd = arg; - cmd_argv = &state->argv[state->next-1]; - cmd_envp = envp; + cmd_argv = &state->argv[state->next - 1]; state->next = state->argc; - traced_thread = cthread_fork ((cthread_fn_t) - traced_thread_function, 0); break; default: @@ -247,25 +607,97 @@ main (int argc, char **argv, char **envp) } const struct argp argp = { options, parse_opt, args_doc, doc }; - traced_class = ports_create_class (0, 0); - traced_bucket = ports_create_bucket(); - - ihash_create (&traced_names); - ostream = stderr; - - cthread_detach (cthread_fork ((cthread_fn_t)trace_thread_function, - traced_bucket)); /* Parse our arguments. */ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); - cthread_join (traced_thread); + if (outfile) + { + ostream = fopen (outfile, "w"); + if (!ostream) + error (1, errno, "%s", outfile); + } + else + ostream = stderr; + + traced_bucket = ports_create_bucket (); + traced_class = ports_create_class (0, &traced_dropweak); + + err = ihash_create (&traced_names); + assert_perror (err); + + /* Spawn a single thread that will receive intercepted messages, print + them, and interpose on the ports they carry. The access to the + `traced_info' and ihash data structures is all single-threaded, + happening only in this new thread. */ + cthread_detach (cthread_fork (trace_thread_function, traced_bucket)); + + /* Run the program on the command line and wait for it to die. + The other thread does all the tracing and interposing. */ + { + pid_t child, pid; + int status; + child = traced_spawn (cmd_argv, envp); + pid = waitpid (child, &status, 0); + sleep (1); /* XXX gives other thread time to print */ + if (pid != child) + error (1, errno, "waitpid"); + if (WIFEXITED (status)) + fprintf (ostream, "Child %d exited with %d\n", + pid, WEXITSTATUS (status)); + else + fprintf (ostream, "Child %d %s\n", pid, strsignal (WTERMSIG (status))); + } - fprintf (stderr, "***************hola***************\n"); - exit (0); + return 0; } + + +/* Run a child and have it do more or else `execvpe (argv, envp);'. */ +pid_t +traced_spawn (char **argv, char **envp) +{ + error_t err; + pid_t pid; + mach_port_t task_wrapper; + file_t file = file_name_path_lookup (argv[0], getenv ("PATH"), + O_EXEC, 0, 0); -/* - Local Variables: - compile-command: "gcc -Wall -g -D_GNU_SOURCE=1 -o rpctrace rpctrace.c -lthreads -lports -lihash" - End: -*/ + if (file == MACH_PORT_NULL) + error (1, errno, "command not found: %s", argv[0]); + + err = task_create (mach_task_self (), 0, &traced_task); + assert_perror (err); + + /* Create a trace wrapper for the task port. */ + task_wrapper = new_send_wrapper (traced_task);/* consumes ref */ + + /* Replace the task's kernel port with the wrapper. When this task calls + `mach_task_self ()', it will get our wrapper send right instead of its + own real task port. */ + err = mach_port_insert_right (mach_task_self (), task_wrapper, + task_wrapper, MACH_MSG_TYPE_MAKE_SEND); + assert_perror (err); + err = task_set_special_port (traced_task, TASK_KERNEL_PORT, task_wrapper); + assert_perror (err); + + /* Declare the new task to be our child. This is what a fork does. */ + err = proc_child (getproc (), traced_task); + if (err) + error (2, err, "proc_child"); + pid = task2pid (traced_task); + if (pid < 0) + error (2, errno, "task2pid"); + + /* Now actually run the command they told us to trace. We do the exec on + the actual task, so the RPCs to map in the program itself do not get + traced. Could have an option to use TASK_WRAPPER here instead. */ + err = _hurd_exec (traced_task, file, argv, envp); + if (err) + error (2, err, "cannot exec `%s'", argv[0]); + + /* We were keeping this send right alive so that the wrapper object + cannot die and hence our TRACED_TASK ref cannot have been released. */ + mach_port_deallocate (mach_task_self (), task_wrapper); + + return pid; +} |