/* An RPC scanner for the Hurd.
Copyright (C) 2015 Free Software Foundation, Inc.
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2, or (at
your option) any later version.
The GNU Hurd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with the GNU Hurd. If not, see . */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "msgids.h"
/* Send messages with arbitrary message ids. */
struct Request {
mach_msg_header_t Head;
} RequestTemplate;
struct Reply {
mach_msg_header_t Head;
mach_msg_type_t RetCodeType;
kern_return_t RetCode;
char Body[4096];
};
union {
struct Request Request;
struct Reply Reply;
} Message;
error_t
setup (mach_port_t request, mach_msg_type_name_t requestType)
{
RequestTemplate.Head.msgh_remote_port = request;
if (! MACH_PORT_VALID (RequestTemplate.Head.msgh_local_port))
RequestTemplate.Head.msgh_local_port = mach_reply_port ();
RequestTemplate.Head.msgh_bits =
MACH_MSGH_BITS (requestType, MACH_MSG_TYPE_MAKE_SEND_ONCE);
return 0;
}
error_t
send (mach_msg_id_t msgid)
{
error_t err;
Message.Request = RequestTemplate;
Message.Request.Head.msgh_id = msgid;
err = mach_msg (&Message.Request.Head,
MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE,
sizeof Message.Request,
sizeof Message.Reply,
Message.Request.Head.msgh_local_port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (err)
return err;
/* XXX typecheck */
return Message.Reply.RetCode;
}
typedef error_t (*setup_function_t) ();
setup_function_t setup_target;
void *setup_argument;
error_t
setup_task_target (void)
{
error_t err;
static task_t task;
static mach_msg_type_name_t taskType = MACH_MSG_TYPE_COPY_SEND;
if (MACH_PORT_VALID (task))
{
task_terminate (task);
mach_port_deallocate (mach_task_self (), task);
}
err = task_create (mach_task_self (), 0, &task);
if (err)
return err;
return setup (task, taskType);
}
error_t
setup_thread_target (void)
{
error_t err;
static task_t task;
static thread_t thread;
if (MACH_PORT_VALID (thread))
{
thread_terminate (thread);
mach_port_deallocate (mach_task_self (), thread);
}
if (MACH_PORT_VALID (task))
{
task_terminate (task);
mach_port_deallocate (mach_task_self (), task);
}
err = task_create (mach_task_self (), 0, &task);
if (err)
return err;
err = thread_create (task, &thread);
if (err)
return err;
return setup (thread, MACH_MSG_TYPE_COPY_SEND);
}
error_t
setup_proc_target (void)
{
error_t err;
static task_t task;
static process_t proc, target;
static mach_msg_type_name_t targetType = MACH_MSG_TYPE_COPY_SEND;
if (! MACH_PORT_VALID (proc))
proc = getproc ();
if (MACH_PORT_VALID (task))
mach_port_deallocate (mach_task_self (), task);
if (MACH_PORT_VALID (target))
mach_port_deallocate (mach_task_self (), target);
err = task_create (mach_task_self (), 0, &task);
if (err)
return err;
err = proc_task2proc (proc, task, &target);
if (err)
return err;
return setup (target, targetType);
}
error_t
setup_auth_target (void)
{
static auth_t auth;
static mach_msg_type_name_t authType = MACH_MSG_TYPE_COPY_SEND;
if (MACH_PORT_VALID (auth))
mach_port_deallocate (mach_task_self (), auth);
auth = getauth ();
if (! MACH_PORT_VALID (auth))
return errno;
return setup (auth, authType);
}
error_t
setup_hurd_target (void)
{
char *name = (char *) setup_argument;
mach_port_t request;
mach_msg_type_name_t requestType;
request = file_name_lookup (name, 0, 0);
if (! MACH_PORT_VALID (request))
return errno;
requestType = MACH_MSG_TYPE_COPY_SEND;
return setup (request, requestType);
}
task_t extract_target_task;
mach_port_t extract_target_port;
mach_msg_type_name_t extract_target_type;
error_t
setup_extract_target (void)
{
error_t err;
mach_port_t request;
mach_msg_type_name_t requestType;
err = mach_port_extract_right (extract_target_task,
extract_target_port,
extract_target_type,
&request,
&requestType);
if (err)
error (1, err, "mach_port_extract_right");
if (err)
return err;
requestType = MACH_MSG_TYPE_COPY_SEND;
return setup (request, requestType);
}
const char *argp_program_version = STANDARD_HURD_VERSION (rpcscan);
char **cmd_argv;
int verbose;
int numeric;
int subsystem;
#define OPT_TARGET_TASK -1
#define OPT_TARGET_THREAD -2
#define OPT_TARGET_PROC -3
#define OPT_TARGET_AUTH -4
#define OPT_TARGET_EXTRACT -5
static const struct argp_option options[] =
{
{NULL, 0, NULL, OPTION_DOC, "Target selection", 1},
{"task", OPT_TARGET_TASK, NULL, 0, "target a task port", 1},
{"thread", OPT_TARGET_THREAD, NULL, 0, "target a thread port", 1},
{"proc", OPT_TARGET_PROC, NULL, 0, "target a proc port", 1},
{"auth", OPT_TARGET_AUTH, NULL, 0, "target an auth port", 1},
{"extract", OPT_TARGET_EXTRACT, "PID.PORT", 0, "target port PORT of PID", 1},
{NULL, 0, NULL, OPTION_DOC, "Options", 2},
{"verbose", 'v', NULL, 0, "be verbose", 2},
{"numeric", 'n', NULL, 0, "show numeric message ids", 2},
{"subsystem", 's', NULL, 0, "show subsystem names", 2},
{0}
};
static const char args_doc[] = "[FILE]";
static const char doc[] = "Scan a given Mach port.";
/* Parse our options... */
error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
error_t err;
switch (key)
{
case 'v':
verbose = 1;
break;
case 'n':
numeric = 1;
break;
case 's':
subsystem = 1;
break;
#define SELECT_TARGET(target) \
if (setup_target) \
argp_error (state, "Multiple targets specified."); \
setup_target = target;
case OPT_TARGET_TASK:
SELECT_TARGET (setup_task_target);
break;
case OPT_TARGET_THREAD:
SELECT_TARGET (setup_thread_target);
break;
case OPT_TARGET_PROC:
SELECT_TARGET (setup_proc_target);
break;
case OPT_TARGET_AUTH:
SELECT_TARGET (setup_auth_target);
break;
case OPT_TARGET_EXTRACT:;
process_t proc;
pid_t pid;
char *end;
pid = strtol (arg, &end, 10);
if (arg == end || *end != '.')
argp_error (state, "Expected format PID.PORT, got `%s'.", arg);
arg = end + 1;
extract_target_port = strtol (arg, &end, 10);
if (arg == end || *end != '\0')
argp_error (state, "Expected format PORT, got `%s'.", arg);
proc = getproc ();
err = proc_pid2task (proc, pid, &extract_target_task);
if (err)
argp_failure (state, 1, err,
"Could not get task of process %d", pid);
extract_target_type = MACH_MSG_TYPE_COPY_SEND; /* XXX */
SELECT_TARGET (setup_extract_target);
break;
case ARGP_KEY_ARG:
SELECT_TARGET (setup_hurd_target);
setup_argument = arg;
break;
#undef SELECT_TARGET
case ARGP_KEY_NO_ARGS:
if (setup_target == NULL)
argp_usage (state);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
const struct argp_child children[] =
{
{ .argp=&msgid_argp, },
{ 0 }
};
const struct argp argp = { options, parse_opt, args_doc, doc, children };
void
format_msgid (char *buf, size_t len, mach_msg_id_t id)
{
if (numeric)
numeric:
snprintf (buf, len, "%d", id);
else
{
const struct msgid_info *info;
info = msgid_info (id);
if (info == NULL)
goto numeric;
if (subsystem)
snprintf (buf, len, "%s/%s", info->subsystem, info->name);
else
snprintf (buf, len, "%s", info->name);
}
}
int
main (int argc, char **argv)
{
error_t err;
mach_msg_id_t msgid;
/* Parse our arguments. */
argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
err = setup_target ();
if (err)
/* Initial setup failed. Bail out. */
error (1, err, "%s",
setup_target == setup_hurd_target? (char *) setup_argument: "setup");
for (msgid = 0; msgid < 500000; msgid++)
{
err = send (msgid);
switch (err)
{
case MACH_SEND_INVALID_DEST:
err = setup_target ();
if (err)
error (1, err, "setup");
msgid--; /* redo */
continue;
case MIG_BAD_ID:
/* do nothing */
break;
default:;
char buf[80];
format_msgid (buf, sizeof buf, msgid);
if (verbose)
error (0, err, "%s", buf);
else
fprintf (stdout, "%s\n", buf);
}
}
return EXIT_SUCCESS;
}