summaryrefslogtreecommitdiff
path: root/trans
diff options
context:
space:
mode:
Diffstat (limited to 'trans')
-rw-r--r--trans/crash.c578
1 files changed, 578 insertions, 0 deletions
diff --git a/trans/crash.c b/trans/crash.c
new file mode 100644
index 00000000..ee9c71f0
--- /dev/null
+++ b/trans/crash.c
@@ -0,0 +1,578 @@
+/* GNU Hurd standard crash dump server.
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+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; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <sys/wait.h>
+#include <error.h>
+
+#include "crash_S.h"
+#include "crash_reply_U.h"
+#include "msg_S.h"
+
+
+process_t procserver; /* Our proc port, for easy access. */
+
+/* Port bucket we service requests on. */
+struct port_bucket *port_bucket;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ|O_WRITE|O_EXEC;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+struct trivfs_control *fsys;
+
+
+/* This data structure describes a crashing task which we have suspended.
+ This is attached to a receive right we have set as the process's message
+ port, so we can get a msg_sig_post RPC to continue or kill the process. */
+
+struct crasher
+ {
+ struct port_info pi;
+
+ /* How to reply to the crash_dump_task RPC. The RPC remains "in
+ progress" while the process is in crash suspension. If the process
+ is resumed with SIGCONT, we will dump a core file and then reply to
+ the RPC. */
+ mach_port_t reply_port;
+ mach_msg_type_name_t reply_type;
+
+ task_t task;
+ file_t core_file;
+ string_t target;
+ int signo, sigcode, sigerror;
+
+ mach_port_t original_msgport; /* Restore on resume. */
+ mach_port_t sidport; /* Session ID port for SIGCONT auth. */
+ process_t proc; /* Proc server port. */
+ };
+
+struct port_class *crasher_portclass;
+
+
+kern_return_t
+S_crash_dump_task (mach_port_t port,
+ mach_port_t reply_port, mach_msg_type_name_t reply_type,
+ task_t task, file_t core_file, char *target,
+ int signo, int sigcode, int sigerror)
+
+{
+ error_t err;
+ struct trivfs_protid *cred;
+ mach_port_t user_proc;
+
+ cred = ports_lookup_port (port_bucket, port, trivfs_protid_portclasses[0]);
+ if (! cred)
+ return EOPNOTSUPP;
+
+ /* Suspend the task first thing before being twiddling it. */
+ err = task_suspend (task);
+
+ if (! err)
+ {
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (! err)
+ {
+ struct crasher *c;
+
+ c = ports_allocate_port (port_bucket, sizeof *c, crasher_portclass);
+ if (! c)
+ err = ENOMEM;
+ else
+ {
+ /* Install our port as the crasher's msgport.
+ We will wait for signals to resume (crash) it. */
+ mach_port_t msgport = ports_get_right (c);
+ mach_port_insert_right (mach_task_self (), msgport,
+ msgport, MACH_MSG_TYPE_MAKE_SEND);
+ err = proc_setmsgport (user_proc, msgport, &c->original_msgport);
+ mach_port_deallocate (mach_task_self (), msgport);
+
+ c->reply_port = reply_port;
+ c->reply_type = reply_type;
+ if (proc_getsidport (user_proc, &c->sidport))
+ c->sidport = MACH_PORT_NULL;
+ c->proc = user_proc;
+
+ /* Tell the proc server the crasher stopped. */
+ proc_mark_stop (user_proc, signo);
+
+ c->task = task;
+ c->core_file = core_file;
+ strcpy (c->target, target);
+ c->signo = signo;
+ c->sigcode = sigcode;
+ c->sigerror = sigerror;
+
+ err = MIG_NO_REPLY;
+ ports_port_deref (c);
+ }
+ }
+ if (err != MIG_NO_REPLY)
+ task_resume (task);
+ }
+ ports_port_deref (cred);
+ return err;
+}
+
+
+
+/* Handle an attempt to send a signal to crashing task C. */
+
+static error_t
+signal_crasher (struct crasher *c, int signo, mach_port_t refport)
+{
+ error_t err;
+
+ if (refport != c->task && (refport != c->sidport || signo != SIGCONT))
+ err = EPERM;
+ else
+ switch (signo)
+ {
+ case SIGTERM:
+ case SIGKILL:
+ /* Kill it as asked. */
+ proc_mark_exit (c->proc, W_EXITCODE (0, signo));
+ err = task_terminate (c->task);
+ break;
+
+ case SIGCONT:
+ case SIGQUIT:
+ {
+ /* Resuming the process should make it dump core. */
+
+ mach_port_t old;
+
+ /* First, restore its msg port. */
+ err = proc_setmsgport (c->proc, c->original_msgport, &old);
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Tell the proc server it has resumed. */
+ proc_mark_cont (c->proc);
+
+ /* Reset the proc server port stored in C, and then destroy the
+ receive right. The null proc port tells dead_crasher to dump
+ a core file. */
+ mach_port_deallocate (mach_task_self (), c->proc);
+ c->proc = MACH_PORT_NULL;
+ ports_destroy_right (c);
+ }
+ break;
+
+ default:
+ err = EBUSY;
+ break;
+ }
+
+ ports_port_deref (c);
+ return err;
+}
+
+kern_return_t
+S_msg_sig_post (mach_port_t port,
+ mach_port_t reply_port, mach_msg_type_name_t reply_type,
+ int signo, mach_port_t refport)
+{
+ struct crasher *c = ports_lookup_port (port_bucket, port, crasher_portclass);
+
+ if (! c)
+ return EOPNOTSUPP;
+
+ return signal_crasher (c, signo, refport);
+}
+
+kern_return_t
+S_msg_sig_post_untraced (mach_port_t port,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int signo, mach_port_t refport)
+{
+ error_t err;
+ struct crasher *c = ports_lookup_port (port_bucket, port, crasher_portclass);
+
+ if (! c)
+ return EOPNOTSUPP;
+
+ if (signo != 0 && signo != c->signo)
+ return signal_crasher (c, signo, refport);
+
+ if (refport != c->task)
+ err = EPERM;
+ else
+ {
+ /* Debugger attaching to the process and continuing it.
+ The debugger is welcome to this crasher, so let's
+ just restore his msgport and forget him. */
+
+ mach_port_t old;
+
+ /* First, restore its msg port. */
+ err = proc_setmsgport (c->proc, c->original_msgport, &old);
+ if (! err)
+ {
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Tell the proc server it has stopped (again)
+ with the original crash signal. */
+ proc_mark_stop (c->proc, c->signo);
+
+ /* We don't need to listen on this msgport any more. */
+ ports_destroy_right (c);
+ }
+ }
+
+ ports_port_deref (c);
+ return err;
+}
+
+error_t
+dump_core (task_t task, file_t core_file, const char *target,
+ int signo, long int sigcode, int sigerror)
+{
+ return ENOSYS; /* XXX */
+}
+
+/* This gets called when the receive right for a crasher message port dies. */
+
+void
+dead_crasher (void *ptr)
+{
+ struct crasher *c = ptr;
+
+ if (c->proc != MACH_PORT_NULL)
+ {
+ /* This message port just died. Clean it up. */
+ mach_port_deallocate (mach_task_self (), c->proc);
+ if (c->reply_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), c->reply_port);
+ }
+ else
+ {
+ /* C->proc was cleared in S_msg_sig_post as a marker that
+ this crasher should get a core dump when we clean him up. */
+ error_t err = dump_core (c->task, c->core_file, c->target,
+ c->signo, c->sigcode, c->sigerror);
+ /* Now reply to the crasher's original RPC which started this whole
+ party. He should now report his own death (with core dump iff ERR
+ reports successful dumping) to his proc server. */
+ crash_dump_task_reply (c->reply_port, c->reply_type, err);
+ /* Resume the task so it can receive our reply and die happily. */
+ task_resume (c->task);
+ }
+
+ /* Deallocate the other saved ports. */
+ mach_port_deallocate (mach_task_self (), c->original_msgport);
+ mach_port_deallocate (mach_task_self (), c->sidport);
+ mach_port_deallocate (mach_task_self (), c->task);
+ mach_port_deallocate (mach_task_self (), c->core_file);
+ mach_port_deallocate (mach_task_self (), c->sidport);
+
+ /* The port data structures are cleaned up when we return. */
+
+ /* See if we are going away and this was the last thing keeping us up. */
+ if (ports_count_class (trivfs_cntl_portclasses[0]) == 0)
+ {
+ /* We have no fsys control port, so we are detached from the
+ parent filesystem. Maybe we have no users left either. */
+ if (ports_count_class (trivfs_protid_portclasses[0]) == 0)
+ {
+ /* We have no user ports left. Maybe we have no crashers still
+ around either. */
+ if (ports_count_class (crasher_portclass) == 0)
+ /* Nobody talking. Time to die. */
+ exit (0);
+ ports_enable_class (crasher_portclass);
+ }
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ }
+ ports_enable_class (trivfs_cntl_portclasses[0]);
+}
+
+
+
+static int
+crash_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int crash_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ extern int msg_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return (crash_server (inp, outp) ||
+ msg_server (inp, outp) ||
+ trivfs_demuxer (inp, outp));
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Fetch our proc server port for easy use. */
+ procserver = getproc ();
+
+ port_bucket = ports_create_bucket ();
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+ crasher_portclass = ports_create_class (dead_crasher, 0);
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (port_bucket, crash_demuxer,
+ 10 * 1000, /* idle thread */
+ 10 * 60 * 1000, /* idle server */
+ 0, MACH_PORT_NULL);
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+ while (trivfs_goaway (fsys, 0));
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_fstype = FSTYPE_MISC;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int count;
+
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+ /* Are there any extant user ports for the /servers/crash file? */
+ count = ports_count_class (trivfs_protid_portclasses[0]);
+ if (count == 0 || (flags & FSYS_GOAWAY_FORCE))
+ {
+ /* No users. Disconnect from the filesystem. */
+ mach_port_deallocate (mach_task_self (), fsys->underlying);
+
+ /* Are there any crasher message ports we are listening on? */
+ count = ports_count_class (crasher_portclass);
+ if (count == 0)
+ /* Nope. We got no reason to live. */
+ exit (0);
+
+ /* Continue babysitting crashing tasks we previously suspended. */
+ ports_enable_class (crasher_portclass);
+
+ /* No more communication with the parent filesystem. */
+ ports_destroy_right (fsys);
+
+ return 0;
+ }
+ else
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+}
+
+/* Stubs for unused msgport RPCs. */
+
+kern_return_t
+S_msg_proc_newids (mach_port_t process,
+ mach_port_t task,
+ pid_t ppid,
+ pid_t pgrp,
+ int orphaned)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_add_auth (mach_port_t process,
+ auth_t auth)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_del_auth (mach_port_t process,
+ mach_port_t task,
+ intarray_t uids,
+ mach_msg_type_number_t uidsCnt,
+ intarray_t gids,
+ mach_msg_type_number_t gidsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t port)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *ports,
+ mach_msg_type_name_t *portsPoly,
+ mach_msg_type_number_t *portsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t ports,
+ mach_msg_type_number_t portsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int *value)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int value)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t *values,
+ mach_msg_type_number_t *valuesCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t values,
+ mach_msg_type_number_t valuesCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *dtable,
+ mach_msg_type_name_t *dtablePoly,
+ mach_msg_type_number_t *dtableCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t dtable,
+ mach_msg_type_number_t dtableCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t port)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_environment (mach_port_t process,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_environment (mach_port_t process,
+ mach_port_t refport,
+ data_t value,
+ mach_msg_type_number_t valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_env_variable (mach_port_t process,
+ string_t variable,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_env_variable (mach_port_t process,
+ mach_port_t refport,
+ string_t variable,
+ string_t value,
+ boolean_t replace)
+{ return EBUSY; }
+kern_return_t
+S_msg_startup_dosync (mach_port_t process)
+{ return EBUSY; }
+kern_return_t
+S_msg_get_exec_flags (mach_port_t process, mach_port_t refport, int *flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_set_all_exec_flags (mach_port_t process, mach_port_t refport, int flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_set_some_exec_flags (mach_port_t process, mach_port_t refport, int flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_clear_some_exec_flags (mach_port_t process, mach_port_t refport,
+ int flags)
+{ return EBUSY; }
+error_t
+S_msg_report_wait (mach_port_t process, thread_t thread,
+ string_t desc, int *rpc)
+{ return EBUSY; }