/* Copyright (C) 2009 Free Software Foundation, Inc. Written by Zheng Da. 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. */ /* This manages the master ports obtained when opening the libmachdev-based translator node. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* mach console */ #include "libdiskfs/diskfs.h" #include "startup_notify_S.h" #include "device_S.h" #include "libports/notify_S.h" #include "fsys_S.h" #include "mach_i386_S.h" #include "trivfs_server.h" #include "libmachdev/machdev.h" 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; /* Our port classes */ struct port_class *trivfs_cntl_class; struct port_class *trivfs_protid_class; /* Our control struct */ struct trivfs_control *control; /* Are we providing bootstrap translator? */ static boolean_t bootstrapping; /* Our underlying node in the FS for bootstrap */ static mach_port_t underlying; /* The FS control port */ static mach_port_t control_port; /* Our device path for injecting bootstrapped translator onto */ static char *devnode; /* Startup and shutdown notifications management */ struct port_class *machdev_shutdown_notify_class; static void arrange_shutdown_notification (void); /* Our parent's task, if applicable */ static task_t parent_task; /* Our argument vector */ static char **machdev_argv; /* Our trivfs control port to use in server loop */ static struct trivfs_control *global_fsys; static void install_as_translator (mach_port_t bootport) { error_t err; underlying = file_name_lookup (devnode, O_NOTRANS, 0); if (! MACH_PORT_VALID (underlying)) return; /* Install translator */ err = file_set_translator (underlying, 0, FS_TRANS_SET, 0, NULL, 0, bootport, MACH_MSG_TYPE_COPY_SEND); assert_perror_backtrace (err); } boolean_t machdev_is_master_device (mach_port_t port) { struct port_info *pi0 = ports_lookup_port (port_bucket, port, trivfs_protid_class); struct port_info *pi1 = ports_lookup_port (port_bucket, port, trivfs_cntl_class); boolean_t ret; ret = pi0 != NULL || pi1 != NULL; if (pi0 != NULL) ports_port_deref (pi0); if (pi1 != NULL) ports_port_deref (pi1); return ret; } error_t trivfs_append_args (struct trivfs_control *fsys, char **argz, size_t *argz_len) { error_t err = 0; #define ADD_OPT(fmt, args...) \ do { char buf[100]; \ if (! err) { \ snprintf (buf, sizeof buf, fmt , ##args); \ err = argz_add (argz, argz_len, buf); } } while (0) #undef ADD_OPT return err; } kern_return_t S_i386_set_ldt (mach_port_t target_thread, int first_selector, const_descriptor_list_t desc_list, mach_msg_type_number_t desc_listCnt, boolean_t desc_listSCopy) { return EOPNOTSUPP; } kern_return_t S_i386_get_ldt (mach_port_t target_thread, int first_selector, int selector_count, descriptor_list_t *desc_list, mach_msg_type_number_t *desc_listCnt) { return EOPNOTSUPP; } kern_return_t S_i386_io_perm_modify (mach_port_t target_task, mach_port_t io_perm, boolean_t enable) { return EOPNOTSUPP; } kern_return_t S_i386_set_gdt (mach_port_t target_thread, int *selector, descriptor_t desc) { return EOPNOTSUPP; } kern_return_t S_i386_get_gdt (mach_port_t target_thread, int selector, descriptor_t *desc) { return EOPNOTSUPP; } kern_return_t S_i386_io_perm_create (mach_port_t master_port, io_port_t from, io_port_t to, io_perm_t *io_perm) { /* Make sure we are the master device */ if (! machdev_is_master_device(master_port)) return EINVAL; return i386_io_perm_create (_hurd_device_master, from, to, io_perm); } kern_return_t trivfs_S_fsys_startup (mach_port_t bootport, mach_port_t reply, mach_msg_type_name_t replytype, int flags, mach_port_t cntl, mach_port_t *realnode, mach_msg_type_name_t *realnodetype) { mach_port_t mybootport; control_port = cntl; *realnode = MACH_PORT_NULL; *realnodetype = MACH_MSG_TYPE_COPY_SEND; task_get_bootstrap_port (mach_task_self (), &mybootport); if (mybootport) fsys_startup (mybootport, flags, control_port, MACH_MSG_TYPE_COPY_SEND, realnode); return 0; } static void essential_task (void) { mach_port_t host, startup; error_t err; err = get_privileged_ports (&host, 0); if (err) { mach_print ("WARNING: Cannot register as essential task\n"); return; } startup = file_name_lookup (_SERVERS_STARTUP, 0, 0); if (startup == MACH_PORT_NULL) { mach_print ("WARNING: Cannot register as essential task\n"); mach_port_deallocate (mach_task_self (), host); return; } startup_essential_task (startup, mach_task_self (), MACH_PORT_NULL, program_invocation_short_name, host); mach_port_deallocate (mach_task_self (), startup); mach_port_deallocate (mach_task_self (), host); } kern_return_t trivfs_S_fsys_init (struct trivfs_control *fsys, mach_port_t reply, mach_msg_type_name_t replytype, mach_port_t procserver, mach_port_t authhandle) { error_t err; mach_port_t *portarray; unsigned int i; uid_t idlist[] = {0, 0, 0}; mach_port_t root, bootstrap; retry_type retry; string_t retry_name; mach_port_t right = MACH_PORT_NULL; /* Traverse to the bootstrapping server first */ task_get_bootstrap_port (mach_task_self (), &bootstrap); if (bootstrap) { process_t parent_proc; err = proc_task2proc (procserver, parent_task, &parent_proc); assert_perror_backtrace (err); /* We don't need this anymore. */ mach_port_deallocate (mach_task_self (), parent_task); parent_task = MACH_PORT_NULL; proc_mark_exec(parent_proc); err = fsys_init (bootstrap, parent_proc, MACH_MSG_TYPE_COPY_SEND, authhandle); assert_perror_backtrace (err); mach_port_deallocate (mach_task_self (), parent_proc); } err = fsys_getroot (control_port, MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND, idlist, 3, idlist, 3, 0, &retry, retry_name, &root); assert_perror_backtrace (err); assert_backtrace (retry == FS_RETRY_NORMAL); assert_backtrace (retry_name[0] == '\0'); assert_backtrace (root != MACH_PORT_NULL); portarray = mmap (0, INIT_PORT_MAX * sizeof *portarray, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); for (i = 0; i < INIT_PORT_MAX; ++i) portarray[i] = MACH_PORT_NULL; portarray[INIT_PORT_PROC] = procserver; portarray[INIT_PORT_AUTH] = authhandle; portarray[INIT_PORT_CRDIR] = root; portarray[INIT_PORT_CWDIR] = root; _hurd_init (0, machdev_argv, portarray, INIT_PORT_MAX, NULL, 0); #ifdef HAVE__HURD_LIBC_PROC_INIT _hurd_libc_proc_init(machdev_argv); #endif if (bootstrapping) { if (devnode) { /* Install the bootstrap port on /dev/something so users * can still access the bootstrapped device */ right = ports_get_send_right (&control->pi); install_as_translator (right); control->underlying = underlying; } /* Mark us as essential if bootstrapping */ essential_task (); } arrange_shutdown_notification (); return 0; } static void arrange_shutdown_notification (void) { error_t err; mach_port_t initport, notify; struct port_info *pi; machdev_shutdown_notify_class = ports_create_class (0, 0); /* Arrange to get notified when the system goes down */ err = ports_create_port (machdev_shutdown_notify_class, port_bucket, sizeof (struct port_info), &pi); if (err) return; initport = file_name_lookup (_SERVERS_STARTUP, 0, 0); if (initport == MACH_PORT_NULL) { mach_print ("WARNING: machdev not registered for shutdown\n"); return; } notify = ports_get_send_right (pi); ports_port_deref (pi); startup_request_notification (initport, notify, MACH_MSG_TYPE_MAKE_SEND, program_invocation_short_name); mach_port_deallocate (mach_task_self (), notify); mach_port_deallocate (mach_task_self (), initport); } /* Override the privileged ports for booting the system */ kern_return_t trivfs_S_fsys_getpriv (struct diskfs_control *init_bootstrap_port, mach_port_t reply, mach_msg_type_name_t reply_type, mach_port_t *host_priv, mach_msg_type_name_t *hp_type, mach_port_t *dev_master, mach_msg_type_name_t *dm_type, mach_port_t *fstask, mach_msg_type_name_t *task_type) { error_t err; mach_port_t right; /* FIXME: check init_bootstrap_port */ right = ports_get_send_right (&control->pi); err = get_privileged_ports (host_priv, NULL); if (!err) { *dev_master = right; *fstask = mach_task_self (); *hp_type = *dm_type = MACH_MSG_TYPE_MOVE_SEND; *task_type = MACH_MSG_TYPE_COPY_SEND; } return err; } static void resume_bootstrap_server(mach_port_t server_task, const char *server_name) { error_t err; mach_port_t right; mach_port_t dev, cons; assert_backtrace (server_task != MACH_PORT_NULL); right = ports_get_send_right (&control->pi); err = task_set_special_port (server_task, TASK_BOOTSTRAP_PORT, right); assert_perror_backtrace (err); err = mach_port_deallocate (mach_task_self (), right); assert_perror_backtrace (err); err = task_resume (server_task); assert_perror_backtrace (err); /* Make sure we have a console */ err = get_privileged_ports (NULL, &dev); assert_perror_backtrace (err); err = device_open (dev, D_READ|D_WRITE, "console", &cons); mach_port_deallocate (mach_task_self (), dev); assert_perror_backtrace (err); stdin = mach_open_devstream (cons, "r"); stdout = stderr = mach_open_devstream (cons, "w"); mach_port_deallocate (mach_task_self (), cons); printf ("%s ", server_name); fflush (stdout); } int machdev_trivfs_init(int argc, char **argv, mach_port_t bootstrap_resume_task, const char *name, const char *path, mach_port_t *bootstrap) { mach_port_t mybootstrap = MACH_PORT_NULL; port_bucket = ports_create_bucket (); trivfs_cntl_class = ports_create_class (trivfs_clean_cntl, 0); trivfs_protid_class = ports_create_class (trivfs_clean_protid, 0); trivfs_create_control (MACH_PORT_NULL, trivfs_cntl_class, port_bucket, trivfs_protid_class, 0, &control); *bootstrap = MACH_PORT_NULL; machdev_argv = argv; task_get_bootstrap_port (mach_task_self (), &mybootstrap); if (mybootstrap) { *bootstrap = mybootstrap; if (bootstrap_resume_task != MACH_PORT_NULL) { mach_port_t host_priv; mach_port_t device_master; if (fsys_getpriv (*bootstrap, &host_priv, &device_master, &parent_task) == 0) { _hurd_host_priv = host_priv; _hurd_device_master = device_master; } } } if (bootstrap_resume_task != MACH_PORT_NULL) { if (path) devnode = strdup(path); resume_bootstrap_server(bootstrap_resume_task, name); /* We need to install as a translator later */ bootstrapping = TRUE; } else { if (*bootstrap == MACH_PORT_NULL) error (1, 0, "must be started as a translator"); /* We do not need to install as a translator later */ bootstrapping = FALSE; } return 0; } /* The system is going down, sync data. */ kern_return_t S_startup_dosync (mach_port_t handle) { struct port_info *inpi = ports_lookup_port (port_bucket, handle, machdev_shutdown_notify_class); if (!inpi) return EOPNOTSUPP; ports_port_deref (inpi); /* Sync device(s) */ machdev_device_sync (); return 0; } error_t trivfs_goaway (struct trivfs_control *fsys, int flags) { int count; /* Stop new requests. */ ports_inhibit_class_rpcs (trivfs_cntl_class); ports_inhibit_class_rpcs (trivfs_protid_class); count = ports_count_class (trivfs_protid_class); if (count && !(flags & FSYS_GOAWAY_FORCE)) { /* We won't go away, so start things going again... */ ports_enable_class (trivfs_protid_class); ports_resume_class_rpcs (trivfs_cntl_class); ports_resume_class_rpcs (trivfs_protid_class); return EBUSY; } exit (0); } static int demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) { mig_routine_t routine; if ((routine = device_server_routine (inp)) || (routine = ports_notify_server_routine (inp)) || (routine = mach_i386_server_routine (inp)) || (routine = startup_notify_server_routine (inp)) || (routine = NULL, trivfs_demuxer (inp, outp))) { if (routine) (*routine) (inp, outp); return TRUE; } else return FALSE; } void trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *stat) { } void machdev_trivfs_server_startup(mach_port_t bootstrap) { int err; if (bootstrapping == FALSE) { /* This path is executed when a parent exists */ err = trivfs_startup (bootstrap, 0, trivfs_cntl_class, port_bucket, trivfs_protid_class, port_bucket, &global_fsys); mach_port_deallocate (mach_task_self (), bootstrap); if (err) error (1, err, "Contacting parent"); arrange_shutdown_notification (); } else { global_fsys = control; } } void * machdev_trivfs_server_loop(void *arg) { /* Launch. */ do { ports_manage_port_operations_one_thread (port_bucket, demuxer, 0); } while (trivfs_goaway (global_fsys, 0)); return NULL; }