From 252043ce8701a48400929319552da5a8f31288d7 Mon Sep 17 00:00:00 2001
From: Justus Winter <4winter@informatik.uni-hamburg.de>
Date: Tue, 23 Dec 2014 15:03:44 +0100
Subject: [PATCH hurd 07/10] XXX bootshell
XXX hack in toplevel Makefile.
---
Makefile | 1 +
bootshell/Makefile | 69 +++
bootshell/boot.scm | 278 +++++++++++
bootshell/bootshell.h | 49 ++
bootshell/elf-exec.c | 200 ++++++++
bootshell/exceptions.c | 92 ++++
bootshell/exec-startup.c | 182 +++++++
bootshell/ffi.c | 1208 +++++++++++++++++++++++++++++++++++++++++++++
bootshell/ffi.h | 167 +++++++
bootshell/frob-task.c | 131 +++++
bootshell/fs.c | 111 +++++
bootshell/fsys.c | 123 +++++
bootshell/main.c | 264 ++++++++++
bootshell/mig-decls.h | 22 +
bootshell/mig-mutate.h | 27 +
bootshell/runsystem.scm | 211 ++++++++
bootshell/scheme-config.h | 31 ++
bootshell/startup.c | 508 +++++++++++++++++++
bootshell/startup.h | 36 ++
bootshell/utils.c | 118 +++++
config.make.in | 4 +
configure.ac | 15 +
22 files changed, 3847 insertions(+)
create mode 100644 bootshell/Makefile
create mode 100644 bootshell/boot.scm
create mode 100644 bootshell/bootshell.h
create mode 100644 bootshell/elf-exec.c
create mode 100644 bootshell/exceptions.c
create mode 100644 bootshell/exec-startup.c
create mode 100644 bootshell/ffi.c
create mode 100644 bootshell/ffi.h
create mode 100644 bootshell/frob-task.c
create mode 100644 bootshell/fs.c
create mode 100644 bootshell/fsys.c
create mode 100644 bootshell/main.c
create mode 100644 bootshell/mig-decls.h
create mode 100644 bootshell/mig-mutate.h
create mode 100644 bootshell/runsystem.scm
create mode 100644 bootshell/scheme-config.h
create mode 100644 bootshell/startup.c
create mode 100644 bootshell/startup.h
create mode 100644 bootshell/utils.c
diff --git a/Makefile b/Makefile
index 3178740..3a2c2ed 100644
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,7 @@ lib-subdirs = libshouldbeinlibc libihash libiohelp libports libthreads \
# Hurd programs
prog-subdirs = auth proc exec term \
+ bootshell \
ext2fs isofs tmpfs fatfs \
storeio pflocal pfinet defpager mach-defpager \
login daemons boot console \
diff --git a/bootshell/Makefile b/bootshell/Makefile
new file mode 100644
index 0000000..ea9bc60
--- /dev/null
+++ b/bootshell/Makefile
@@ -0,0 +1,69 @@
+# Makefile for bootshell subdirectory of hurd sources
+#
+# Copyright (C) 1999, 2000, 2002, 2007, 2010, 2012 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 this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := bootshell
+makemode:= server
+target := bootshell
+
+SCRIPTS := \
+ init.scm \
+ boot.scm \
+ runsystem.scm \
+
+SRCS := \
+ scheme.c \
+ main.c \
+ exceptions.c \
+ fs.c \
+ fsys.c \
+ exec-startup.c \
+ startup.c \
+ utils.c \
+ ffi.c \
+ elf-exec.c \
+ frob-task.c \
+
+SERVER_PROTOCOLS := exc exec_startup fs fsys startup
+USER_PROTOCOLS := startup startup_reply
+
+MIGSTUBS:= $(foreach p,$(SERVER_PROTOCOLS),$(p)Server.o) \
+ $(foreach p,$(USER_PROTOCOLS),$(p)User.o)
+OBJS := $(SRCS:.c=.o) $(SCRIPTS:.scm=.o) $(MIGSTUBS)
+
+HURDLIBS:= shouldbeinlibc
+# XXX why doesn't $(libreadline_LIBS) work ???
+OTHERLIBS:= $(libreadline_LIBS) -lreadline -lhistory -lncurses -ltinfo -lpthread
+CFLAGS += -imacros scheme-config.h
+LDFLAGS += -static
+MIGSFLAGS := -imacros mig-mutate.h
+
+%.o: %.scm
+ cat <$< >.$@
+ $(LD) -r --format=binary .$@ -o $@
+ rm .$@
+
+NOWARN := conversion sign-conversion switch unused-function
+CFLAGS := $(filter-out $(foreach flag,$NOWARN,-W$(flag)),$(CFLAGS))
+
+# XXX
+CFLAGS += -Wno-sign-conversion
+
+include ../Makeconf
diff --git a/bootshell/boot.scm b/bootshell/boot.scm
new file mode 100644
index 0000000..1cd1723
--- /dev/null
+++ b/bootshell/boot.scm
@@ -0,0 +1,278 @@
+;; Bootshell, a Scheme shell, a flexible multiserver bootstrap solution.
+;;
+;; 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 . */
+
+;; Missing library functions.
+(define (filter pred lst)
+ (cond ((null? lst) '())
+ ((pred (car lst))
+ (cons (car lst) (filter pred (cdr lst))))
+ (else (filter pred (cdr lst)))))
+
+(define (any p l)
+ (cond ((null? l) #f)
+ ((p (car l)) #t)
+ (else (any p (cdr l)))))
+
+;; Is s1 a prefix of s2 ?
+(define (string-prefix? s1 s2)
+ (and (>= (string-length s2) (string-length s1))
+ (string=? s1 (substring s2 0 (string-length s1)))))
+
+;; Given a list of prefixes, does s start with any of them ?
+(define (string-prefix-any? lp s)
+ (any (lambda (p) (string-prefix? p s)) lp))
+
+;; The `catch' from init.scm doesn't give the thrown value to the
+;; handler. As a crappy workaround, we set! `last-exception' to the
+;; last the exception.
+(define last-exception '())
+(define (throw . x)
+ (set! last-exception x)
+ (if (more-handlers?)
+ (apply (pop-handler))
+ (apply error x)))
+(define *error-hook* throw)
+
+;; Foreign function wrapper. Expects F to return a list with the
+;; first element being the `error_t' value returned by the foreign
+;; function. The error is thrown, or the cdr of the result is
+;; returned.
+(define (ffi-apply name f args)
+ (let ((result (apply f args)))
+ (cond
+ ((not (= (car result) 0))
+ (let ((args' (open-output-string)))
+ (write (cons (string->symbol name) args) args')
+ (throw (string-append
+ (get-output-string args') ": " (strerror (car result))))))
+ ((and (= (car result) 0) (pair? (cdr result))) (cadr result))
+ ((= (car result) 0) '())
+ (else
+ (throw "Weird.")))))
+
+;; Convenience functions.
+(define (echo . msg)
+ (map display msg)
+ (newline))
+
+(define (trace-show x)
+ (write x)
+ x)
+
+;; Semi-crappy repl using `prompt' function.
+(define (shell p)
+ (call/cc
+ (lambda (exit)
+ (let loop ((prefix ""))
+ (let ((line (prompt (p prefix))))
+ (if (and (not (eof-object? line)) (eqv? (string-length line) 0))
+ (exit (loop prefix)))
+ (if (not (eof-object? line))
+ (let* ((next (string-append prefix line))
+ (c (catch (begin (echo "Parse error: " last-exception)
+ (loop prefix))
+ (read (open-input-string next)))))
+ (if (not (eof-object? c))
+ (begin
+ (catch (echo "Error: " last-exception)
+ (echo " ===> " (eval c)))
+ (exit (loop ""))))
+ (exit (loop next)))))))))
+
+(define (prompt-append-prefix prompt prefix)
+ (string-append prompt (if (> (string-length prefix) 0)
+ (string-append prefix "...")
+ "> ")))
+
+;; Default repl run by the bootshell.
+(define (interactive-repl)
+ (shell (lambda (p) (prompt-append-prefix "(bootshell) " p))))
+
+;; Default repl run by `panic'.
+(define (emergency-shell)
+ (shell (lambda (p) (prompt-append-prefix "(emergency-shell) " p))))
+
+;; Display a message and run the emergency shell.
+(define (panic . msg)
+ (display "\n\npanic: ")
+ (map display msg)
+ (newline)
+ (emergency-shell))
+
+;; Mach port management.
+(define (mach-port-valid? p) (not (or (= p MACH_PORT_NULL)
+ (= p MACH_PORT_DEAD))))
+
+(define (make-send-right receive-right)
+ (mach-port-insert-right mach-task-self receive-right receive-right
+ MACH_MSG_TYPE_MAKE_SEND))
+(define (copy-send-right send-right)
+ (mach-port-insert-right mach-task-self send-right send-right
+ MACH_MSG_TYPE_COPY_SEND))
+
+;; Binds a send right to an identifier, much like `let'. Deallocates
+;; the send right once it goes out of scope.
+(macro (letport form)
+ (let ((result-sym (gensym)))
+ `((lambda ((,@(caaadr form)))
+ (let ((,result-sym
+ ,(if (= 1 (length (cadr form)))
+ `(begin ,@(cddr form))
+ `(letport ,(cdadr form) ,@(cddr form)))))
+ (if (mach-port-valid? ,(caaadr form))
+ (mach-port-deallocate mach-task-self ,(caaadr form)))
+ ,result-sym)) ,@(cdaadr form))))
+
+;; TinySCHEME doesn't have define-syntax :(
+;;
+;; (define-syntax letport
+;; (syntax-rules ()
+;; ((letport ((var expr) ...) body ...)
+;; ((lambda expressions
+;; (let ((result (apply (lambda (var ...) body ...) expressions)))
+;; (map (lambda (p) (mach-port-deallocate mach-task-self p))
+;; expressions)
+;; result)) expr ...))))
+
+;; task management
+
+(define (task-get-kernel-port t)
+ (task-get-special-port t TASK_KERNEL_PORT))
+(define (task-get-exception-port t)
+ (task-get-special-port t TASK_EXCEPTION_PORT))
+(define (task-get-bootstrap-port t)
+ (task-get-special-port t TASK_BOOTSTRAP_PORT))
+
+(define (task-set-kernel-port t p)
+ (task-set-special-port t TASK_KERNEL_PORT p))
+(define (task-set-exception-port t p)
+ (task-set-special-port t TASK_EXCEPTION_PORT p))
+(define (task-set-bootstrap-port t p)
+ (task-set-special-port t TASK_BOOTSTRAP_PORT p))
+
+;; Hurd server bootstrap.
+
+(define ESUCCESS 0) ;
+
+;; translator linkage
+
+(define (set-passive-translator path flags mode args)
+ (letport ((node (file-name-lookup path (logior O_NOTRANS flags) mode)))
+ (file-set-translator node FS_TRANS_SET 0 0 args
+ MACH_PORT_NULL MACH_MSG_TYPE_COPY_SEND)))
+
+(define (set-active-translator path flags mode active-control)
+ (letport ((node (file-name-lookup path (logior O_NOTRANS flags) mode)))
+ (file-set-translator node 0 FS_TRANS_SET 0 '()
+ active-control MACH_MSG_TYPE_COPY_SEND)))
+
+;; Wait for the predicate CONDITION to return #t, or throw 'timeout
+;; after T microseconds.
+(define (wait-for condition t)
+ (if (<= t 0)
+ (throw 'timeout)
+ (if (not (condition))
+ (begin (usleep 10000)
+ (wait-for condition (- t 10000))))))
+
+;; Read a word from port P.
+(define (read-word p)
+ (list->string
+ (let f ()
+ (let ((c (peek-char p)))
+ (cond
+ ((eof-object? c) '())
+ ((char-alphabetic? c)
+ (read-char p)
+ (cons c (f)))
+ (else '()))))))
+
+;; Read everything from port P.
+(define (read-all p)
+ (list->string
+ (let f ()
+ (let ((c (peek-char p)))
+ (cond
+ ((eof-object? c) '())
+ (else (read-char p)
+ (cons c (f))))))))
+
+;; Shell-like functions.
+
+(define cd chdir)
+(define (pwd) (echo (getcwd)))
+(define (cat path)
+ (display (call-with-input-file path read-all)))
+(define (hostname)
+ ((lambda (x) (if (string? x) x "unnamed"))
+ (call-with-input-file "/etc/hostname" read-word)))
+
+(define (print-banner)
+ (echo "
+Welcome to bootshell, a scheme shell. Type `(help)' for help.
+"))
+
+(define (reboot-hurd)
+ (letport ((startup (file-name-lookup "/servers/startup" 0 0)))
+ (startup-reboot startup host-priv RB_AUTOBOOT)))
+
+(define (halt-hurd)
+ (letport ((startup (file-name-lookup "/servers/startup" 0 0)))
+ (startup-reboot startup host-priv RB_HALT)))
+
+(define (reboot-mach) (host-reboot host-priv RB_AUTOBOOT))
+(define (halt-mach) (host-reboot host-priv RB_HALT))
+(define (kdb-mach) (host-reboot host-priv RB_DEBUGGER))
+
+(define (reboot)
+ (catch (reboot-mach)
+ (reboot-hurd)))
+(define (halt)
+ (catch (halt-mach)
+ (halt-hurd)))
+
+;; Online documentation.
+
+(define (help)
+ (echo "Welcome to the Hurd boot shell. XXX this is not up to date :(
+
+Functions
+ General shell-like functions
+ cat cd echo halt help hostname kdb mach-print panic prompt pwd
+ reboot shell sleep {reboot,halt}-{mach,hurd}
+" "
+ Mach related
+ mach-port-valid? {copy,make}-send-right task-{create,resume,terminate}
+ task-{g,s}et-{special,kernel,exception,bootstrap}-port host-reboot
+" "
+ Hurd related
+ file_name_lookup chdir getcwd startup-reboot
+ XXX write them
+ {s,g}etauth {s,g}etproc file_name_lookup_under file_name_path_lookup
+" "
+Environment:
+ mach-task-self exception-port bootstrap-port host-priv device-master
+ rootfs-task hello-task rootfs-control"))
+
+;; XXX
+
+(define log display)
+
+;; We're ready.
+(echo version ".")
diff --git a/bootshell/bootshell.h b/bootshell/bootshell.h
new file mode 100644
index 0000000..fd91ce9
--- /dev/null
+++ b/bootshell/bootshell.h
@@ -0,0 +1,49 @@
+/* Bootshell, a Scheme shell, a flexible multiserver bootstrap solution.
+
+ 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 . */
+
+#ifndef _HURD_BOOTSHELL_H
+#define _HURD_BOOTSHELL_H
+
+#include
+
+extern char **global_argv;
+extern const char *argp_program_version;
+
+extern mach_port_t portarray_template[];
+
+/* We catch exceptions using this port. */
+extern mach_port_t exception_port;
+
+extern mach_port_t console;
+extern mach_port_t rootnode;
+
+error_t init_exception_handling (void);
+error_t init_fs_server (void);
+
+mach_msg_return_t
+mach_msg_server_timeout_once (boolean_t (*demux) (mach_msg_header_t *request,
+ mach_msg_header_t *reply),
+ mach_msg_size_t max_size,
+ mach_port_t rcv_name,
+ mach_msg_option_t option,
+ mach_msg_timeout_t timeout);
+
+#define TRACE error (0, 0, "%s:%d", __FUNCTION__, __LINE__);
+
+#endif /* _HURD_BOOTSHELL_H */
diff --git a/bootshell/elf-exec.c b/bootshell/elf-exec.c
new file mode 100644
index 0000000..c8a8e14
--- /dev/null
+++ b/bootshell/elf-exec.c
@@ -0,0 +1,200 @@
+/* Load an ELF image into a task without using the exec server.
+
+ Copyright (C) 1993-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 /* For VM_XXX_ADDRESS */
+#include
+#include
+#include
+#include
+#include
+
+#include "ffi.h"
+
+error_t
+load_image (task_t t,
+ char *file,
+ vm_address_t *addr)
+{
+ error_t err;
+ int fd;
+ ssize_t count;
+ Elf32_Ehdr hdr;
+
+ fd = open (file, O_RDONLY, 0);
+ if (fd == -1)
+ return errno;
+
+ count = read (fd, &hdr, sizeof hdr);
+ if (count != sizeof hdr)
+ return errno;
+
+ if (*(Elf32_Word *) hdr.e_ident != *(Elf32_Word *) "\177ELF")
+ return EINVAL; // XXX
+
+ Elf32_Phdr phdrs[hdr.e_phnum], *ph;
+ lseek (fd, hdr.e_phoff, SEEK_SET);
+ read (fd, phdrs, sizeof phdrs);
+ for (ph = phdrs; ph < &phdrs[sizeof phdrs/sizeof phdrs[0]]; ++ph)
+ if (ph->p_type == PT_LOAD)
+ {
+ vm_address_t buf;
+ vm_size_t offs = ph->p_offset & (ph->p_align - 1);
+ vm_size_t bufsz = round_page (ph->p_filesz + offs);
+
+ buf = (vm_address_t) mmap (0, bufsz,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ lseek (fd, ph->p_offset, SEEK_SET);
+ read (fd, (void *)(buf + offs), ph->p_filesz);
+
+ ph->p_memsz = ((ph->p_vaddr + ph->p_memsz + ph->p_align - 1)
+ & ~(ph->p_align - 1));
+ ph->p_vaddr &= ~(ph->p_align - 1);
+ ph->p_memsz -= ph->p_vaddr;
+
+ err = vm_allocate (t, (vm_address_t*)&ph->p_vaddr, ph->p_memsz, 0);
+ if (err)
+ return err;
+
+ err = vm_write (t, ph->p_vaddr, buf, bufsz);
+ if (err)
+ return err;
+
+ munmap ((caddr_t) buf, bufsz);
+ err = vm_protect (t, ph->p_vaddr, ph->p_memsz, 0,
+ ((ph->p_flags & PF_R) ? VM_PROT_READ : 0) |
+ ((ph->p_flags & PF_W) ? VM_PROT_WRITE : 0) |
+ ((ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0));
+ if (err)
+ return err;
+ }
+
+ *addr = hdr.e_entry;
+ return 0;
+}
+
+/* XXX refactor this mess */
+error_t
+boot_script_exec_cmd (mach_port_t task, char *path, int argc,
+ char **argv, char *strings, int stringlen)
+{
+ error_t err;
+ char *args, *p;
+ int arg_len, i;
+ size_t reg_size;
+ void *arg_pos;
+ vm_offset_t stack_start, stack_end;
+ vm_address_t startpc, str_start;
+ thread_t thread;
+
+ err = load_image (task, path, &startpc);
+ if (err)
+ return err;
+
+ arg_len = stringlen + (argc + 2) * sizeof (char *) + sizeof (integer_t);
+ arg_len += 5 * sizeof (int);
+ stack_end = VM_MAX_ADDRESS;
+ stack_start = VM_MAX_ADDRESS - 16 * 1024 * 1024;
+ vm_allocate (task, &stack_start, stack_end - stack_start, FALSE);
+ arg_pos = (void *) ((stack_end - arg_len) & ~(sizeof (natural_t) - 1));
+ args = mmap (0, stack_end - trunc_page ((vm_offset_t) arg_pos),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ str_start = ((vm_address_t) arg_pos
+ + (argc + 2) * sizeof (char *) + sizeof (integer_t));
+ p = args + ((vm_address_t) arg_pos & (vm_page_size - 1));
+ *(int *) p = argc;
+ p = (void *) p + sizeof (int);
+ for (i = 0; i < argc; i++)
+ {
+ *(char **) p = argv[i] - strings + (char *) str_start;
+ p = (void *) p + sizeof (char *);
+ }
+ *(char **) p = 0;
+ p = (void *) p + sizeof (char *);
+ *(char **) p = 0;
+ p = (void *) p + sizeof (char *);
+ memcpy (p, strings, stringlen);
+ memset (args, 0, (vm_offset_t)arg_pos & (vm_page_size - 1));
+ vm_write (task, trunc_page ((vm_offset_t) arg_pos), (vm_address_t) args,
+ stack_end - trunc_page ((vm_offset_t) arg_pos));
+ munmap ((caddr_t) args,
+ stack_end - trunc_page ((vm_offset_t) arg_pos));
+
+ thread_create (task, &thread);
+
+#ifdef i386_THREAD_STATE_COUNT
+ {
+ struct i386_thread_state regs;
+ reg_size = i386_THREAD_STATE_COUNT;
+ thread_get_state (thread, i386_THREAD_STATE,
+ (thread_state_t) ®s, ®_size);
+ regs.eip = (int) startpc;
+ regs.uesp = (int) arg_pos;
+ thread_set_state (thread, i386_THREAD_STATE,
+ (thread_state_t) ®s, reg_size);
+ }
+#else
+# error needs to be ported
+#endif
+
+ err = thread_resume (thread);
+ if (err)
+ return err;
+
+ err = mach_port_deallocate (mach_task_self (), thread);
+ assert_perror (err);
+ return 0;
+}
+
+error_t
+elf_exec (mach_port_t task, char *argz, size_t argz_len)
+{
+ error_t err;
+ size_t argc;
+ char **argv;
+
+ argc = argz_count (argz, argz_len);
+ argv = malloc ((argc + 1) * sizeof *argv);
+ argz_extract (argz, argz_len, argv);
+
+ err = boot_script_exec_cmd (task, argz, argc, argv, argz, argz_len);
+ free (argv);
+ return err;
+}
+
+pointer
+do_elf_exec (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("elf-exec");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, pointer, arguments, list, args);
+ SC_ARGS_DONE (sc);
+ char *argz = NULL;
+ size_t argz_len = 0;
+ ffi_list2argz (sc, &argz, &argz_len, arguments);
+ err = elf_exec (task, argz, argz_len);
+ free (argz);
+ SC_RETURN (sc);
+}
diff --git a/bootshell/exceptions.c b/bootshell/exceptions.c
new file mode 100644
index 0000000..3102369
--- /dev/null
+++ b/bootshell/exceptions.c
@@ -0,0 +1,92 @@
+/* Mach exception handling.
+
+ 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 "bootshell.h"
+
+// eek #include "exc_S.h"
+
+error_t
+catch_exception_raise (mach_port_t e,
+ thread_t thread,
+ task_t task,
+ int exception, int code, int subcode)
+{
+ if (e != exception_port)
+ return EOPNOTSUPP;
+
+ fprintf (stderr, "catch_exception_raise (%d, %d, %d, %d, %d): ",
+ thread, task, exception, code, subcode);
+
+ if (task == mach_task_self ())
+ fprintf (stderr, "terminating bootshell. bye.\n");
+ else
+ fprintf (stderr, "terminating task %d.\n", task);
+
+ task_terminate (task);
+ return 0;
+}
+
+static void *
+service_exception_requests (void *arg)
+{
+ extern boolean_t exc_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ while (1)
+ mach_msg_server (exc_server, 0, exception_port);
+
+ /* Not reached. */
+ return NULL;
+}
+
+error_t
+init_exception_handling (void)
+{
+ error_t err;
+ pthread_t t;
+
+ err = mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE,
+ &exception_port);
+ if (err)
+ return err;
+
+ /* Make a thread to service exception requests. */
+ err = pthread_create (&t, NULL, service_exception_requests, NULL);
+ if (err)
+ return err;
+ pthread_detach (t);
+
+ err = mach_port_insert_right (mach_task_self (),
+ exception_port,
+ exception_port,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ return err;
+
+ err = task_set_exception_port (mach_task_self (), exception_port);
+ if (err)
+ return err;
+
+ return err;
+}
+
diff --git a/bootshell/exec-startup.c b/bootshell/exec-startup.c
new file mode 100644
index 0000000..026687f
--- /dev/null
+++ b/bootshell/exec-startup.c
@@ -0,0 +1,182 @@
+/* Handles the exec_startup protocol.
+
+ 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
+
+// eek #include "fsys_S.h"
+
+#include "bootshell.h"
+#include "ffi.h"
+
+/* XXX would be nice not to use a global variable, maybe with
+ payloads. */
+static struct
+{
+ /* Filled by caller. */
+ mach_port_t bootstrap_port;
+ char *argz;
+ size_t argz_len;
+
+ /* Filled by the server function. */
+} exec_startup_get_info_args;
+
+/* We look like an execserver to the execserver itself; it makes this
+ call (as does any task) to get its state. We can't give it all of
+ its ports (we'll provide those with a later call to exec_init). */
+kern_return_t
+S_exec_startup_get_info (mach_port_t bootstrap_port,
+ vm_address_t *user_entry,
+ vm_address_t *phdr_data,
+ vm_size_t *phdr_size,
+ vm_address_t *base_addr,
+ vm_size_t *stack_size,
+ int *flags,
+ char **argz,
+ mach_msg_type_number_t *argz_len,
+ char **envz,
+ mach_msg_type_number_t *envz_len,
+ mach_port_t **dtableP,
+ mach_msg_type_name_t *dtablepoly,
+ mach_msg_type_number_t *dtablelen,
+ mach_port_t **portarrayP,
+ mach_msg_type_name_t *portarraypoly,
+ mach_msg_type_number_t *portarraylen,
+ int **intarrayP,
+ mach_msg_type_number_t *intarraylen)
+{
+ error_t err;
+ mach_port_t *portarray, *dtable;
+
+ if (bootstrap_port != exec_startup_get_info_args.bootstrap_port)
+ return EOPNOTSUPP;
+
+ *user_entry = 0;
+ *phdr_data = *base_addr = 0;
+ *phdr_size = *stack_size = 0;
+
+ *flags = 0;
+
+ /* Arguments. */
+ *argz = NULL;
+ *argz_len = exec_startup_get_info_args.argz_len;
+ if (*argz_len == 0)
+ /* We have no args for it. Tell it to look on its stack
+ for the args placed there by the boot loader. */
+ *flags |= EXEC_STACK_ARGS;
+ else
+ {
+ err = vm_allocate (mach_task_self (),
+ (vm_address_t *)argz, *argz_len, TRUE);
+ if (err)
+ return err;
+ memcpy (*argz, exec_startup_get_info_args.argz, *argz_len);
+ }
+
+ /* Environment. */
+ *envz = NULL;
+ *envz_len = 0;
+
+ /* File descriptors. */
+ if (*dtablelen < 3)
+ *dtableP = mmap (0, 3 * sizeof (mach_port_t), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ dtable = *dtableP;
+ *dtablepoly = MACH_MSG_TYPE_COPY_SEND;
+ *dtablelen = 3;
+ dtable[0] = dtable[1] = dtable[2] = console;
+
+ /* Initial ports. */
+ if (*portarraylen < INIT_PORT_MAX)
+ *portarrayP = mmap (0, INIT_PORT_MAX * sizeof (mach_port_t),
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ portarray = *portarrayP;
+ *portarraylen = INIT_PORT_MAX;
+ memcpy (portarray, portarray_template, INIT_PORT_MAX * sizeof *portarray);
+ portarray[INIT_PORT_BOOTSTRAP] = bootstrap_port; /* use the same port */
+ *portarraypoly = MACH_MSG_TYPE_COPY_SEND;
+
+ /* Initial ints. */
+ *intarrayP = NULL;
+ *intarraylen = 0;
+
+ return 0;
+}
+
+boolean_t
+exec_startup_get_info_demuxer (mach_msg_header_t *request,
+ mach_msg_header_t *reply)
+{
+ extern boolean_t exec_startup_server (mach_msg_header_t *,
+ mach_msg_header_t *);
+ if (request->msgh_id != 30500) /* XXX hardcoded msgh_id */
+ {
+ /* Return MIG_BAD_ID. */
+ mig_reply_setup (request, reply);
+ return FALSE;
+ }
+ return exec_startup_server (request, reply);
+}
+
+error_t
+service_exec_startup_request (mach_port_t bootstrap,
+ char *argz,
+ size_t argz_len,
+ mach_msg_timeout_t timeout)
+{
+ error_t err;
+
+ if (! MACH_PORT_VALID (bootstrap))
+ return EINVAL;
+
+ exec_startup_get_info_args.bootstrap_port = bootstrap;
+ exec_startup_get_info_args.argz = argz;
+ exec_startup_get_info_args.argz_len = argz_len;
+
+ err = mach_msg_server_timeout_once (exec_startup_get_info_demuxer,
+ 0, bootstrap,
+ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
+ timeout);
+ if (err != MACH_MSG_SUCCESS)
+ return err;
+
+ return 0;
+}
+
+pointer
+do_handle_exec_startup (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("handle-exec-startup");
+ SC_ARG (sc, mach_port_t, bootstrap, number, args);
+ SC_ARG (sc, pointer, arguments, list, args);
+ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
+ SC_ARGS_DONE (sc);
+ char *argz = NULL;
+ size_t argz_len = 0;
+ ffi_list2argz (sc, &argz, &argz_len, arguments);
+ err = service_exec_startup_request (bootstrap, argz, argz_len, timeout);
+ free (argz);
+ SC_RETURN (sc);
+}
diff --git a/bootshell/ffi.c b/bootshell/ffi.c
new file mode 100644
index 0000000..d3751d9
--- /dev/null
+++ b/bootshell/ffi.c
@@ -0,0 +1,1208 @@
+/* Mach, Hurd, and libc primitives for the Scheme environment.
+
+ 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
+
+#if HAVE_LIBREADLINE
+#include
+#include
+#endif
+
+#include "bootshell.h"
+#include "ffi.h"
+
+#include "startup.h"
+
+static mach_port_t
+copy_send_right (mach_port_t right)
+{
+ error_t err;
+ if (! MACH_PORT_VALID (right))
+ return right;
+
+ err = mach_port_insert_right (mach_task_self (),
+ right,
+ right,
+ MACH_MSG_TYPE_COPY_SEND);
+ if (err)
+ {
+ error (0, err, "mach_port_insert_right");
+ return MACH_PORT_NULL;
+ }
+
+ return right;
+}
+
+
+pointer
+do_logand (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("logand");
+ unsigned int acc = ~0;
+ while (args != sc->NIL)
+ {
+ SC_ARG (sc, unsigned int, v, number, args);
+ acc &= v;
+ }
+ SC_RETURN_INT (sc, acc);
+}
+
+pointer
+do_logior (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("logior");
+ unsigned int acc = 0;
+ while (args != sc->NIL)
+ {
+ SC_ARG (sc, unsigned int, v, number, args);
+ acc |= v;
+ }
+ SC_RETURN_INT (sc, acc);
+}
+
+pointer
+do_logxor (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("logxor");
+ unsigned int acc = 0;
+ while (args != sc->NIL)
+ {
+ SC_ARG (sc, unsigned int, v, number, args);
+ acc ^= v;
+ }
+ SC_RETURN_INT (sc, acc);
+}
+
+pointer
+do_lognot (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("lognot");
+ SC_ARG (sc, unsigned int, v, number, args);
+ SC_ARGS_DONE (sc);
+ SC_RETURN_INT (sc, ~v);
+}
+
+
+
+
+int use_libreadline;
+
+/* Read a string, and return a pointer to it. Returns NULL on EOF. */
+char *
+rl_gets (const char *prompt)
+{
+ static char *line = NULL;
+ free (line);
+
+#if HAVE_LIBREADLINE
+ if (use_libreadline)
+ {
+ line = readline (prompt);
+ if (line && *line)
+ add_history (line);
+ }
+ else
+#endif
+ {
+ size_t max_size = 0xff;
+ printf ("%s", prompt);
+ fflush (stdout);
+ line = malloc (max_size);
+ if (line != NULL)
+ fgets (line, max_size, stdin);
+ }
+
+ /* Strip trailing whitespace. */
+ if (line && strlen (line) > 0)
+ for (char *p = &line[strlen (line) - 1]; isspace (*p); p--)
+ *p = 0;
+
+ return line;
+}
+
+pointer
+do_enable_readline (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("enable-readline");
+ SC_ARGS_DONE (sc);
+ use_libreadline = 1;
+ SC_RETURN (sc);
+}
+
+pointer
+do_prompt (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("prompt");
+ SC_ARG (sc, char *, prompt, string, args);
+ SC_ARGS_DONE (sc);
+ const char *line = rl_gets (prompt);
+ ffi_update (sc);
+ if (! line)
+ SC_RETURN_POINTER (sc, sc->EOF_OBJ);
+
+ SC_RETURN_STRING (sc, line);
+}
+
+#define is_false(p) ((p) == sc->F)
+
+pointer
+do_host_reboot (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("host-reboot");
+ SC_ARG (sc, mach_port_t, host_priv, number, args);
+ SC_ARG (sc, int, flags, number, args);
+ SC_ARGS_DONE (sc);
+ err = host_reboot (host_priv, flags);
+ SC_RETURN (sc);
+}
+
+pointer
+do_task_create (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-create");
+ SC_ARG (sc, task_t, parent, number, args);
+ SC_ARG (sc, boolean_t, inherit_memory, number, args);
+ SC_ARGS_DONE (sc);
+ task_t task;
+ err = task_create (parent, inherit_memory, &task);
+ SC_RETURN_INT (sc, task);
+}
+
+pointer
+do_task_suspend (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-suspend");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARGS_DONE (sc);
+ err = task_suspend (task);
+ SC_RETURN (sc);
+}
+
+pointer
+do_task_resume (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-resume");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARGS_DONE (sc);
+ err = task_resume (task);
+ SC_RETURN (sc);
+}
+
+pointer
+do_task_terminate (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-terminate");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARGS_DONE (sc);
+ err = task_terminate (task);
+ SC_RETURN (sc);
+}
+
+pointer
+do_task_set_name (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-terminate");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, char *, name, path, args);
+ SC_ARGS_DONE (sc);
+ err = task_set_name (task, name);
+ SC_RETURN (sc);
+}
+
+pointer
+do_vm_set_default_memory_manager (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-terminate");
+ SC_ARG (sc, mach_port_t, host_priv, number, args);
+ SC_ARG (sc, mach_port_t, defpager, number, args);
+ SC_ARGS_DONE (sc);
+ err = vm_set_default_memory_manager (host_priv, &defpager);
+ SC_RETURN_INT (sc, defpager);
+}
+
+
+pointer
+do_sleep (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("sleep");
+ SC_ARG (sc, unsigned int, seconds, number, args);
+ SC_ARGS_DONE (sc);
+ sleep (seconds);
+ ffi_update (sc);
+ SC_RETURN (sc);
+}
+
+pointer
+do_usleep (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("usleep");
+ SC_ARG (sc, useconds_t, microseconds, number, args);
+ SC_ARGS_DONE (sc);
+ usleep (microseconds);
+ ffi_update (sc);
+ SC_RETURN (sc);
+}
+
+pointer
+do_file_name_lookup (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("file-name-lookup");
+ SC_ARG (sc, char *, name, path, args);
+ SC_ARG (sc, int, flags, number, args);
+ SC_ARG (sc, mode_t, mode, number, args);
+ SC_ARGS_DONE (sc);
+ file_t file = file_name_lookup (name, flags, mode);
+ if (! MACH_PORT_VALID (file))
+ SC_RETURN_ERR (sc, errno);
+ SC_RETURN_INT (sc, file);
+}
+
+pointer
+do_chdir (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("chdir");
+ SC_ARG (sc, char *, name, path, args);
+ SC_ARGS_DONE (sc);
+ if (chdir (name))
+ SC_RETURN_ERR (sc, errno);
+ SC_RETURN (sc);
+}
+
+pointer
+do_strerror (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("strerror");
+ SC_ARG (sc, int, error, number, args);
+ SC_ARGS_DONE (sc);
+ char *s, buf[128];
+ s = strerror_r (error, buf, sizeof buf);
+ SC_RETURN_STRING (sc, s);
+}
+
+pointer
+do_getproc (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("getproc");
+ SC_ARGS_DONE (sc);
+ SC_RETURN_INT (sc, getproc ());
+}
+
+pointer
+do_getcwd (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("getcwd");
+ SC_ARGS_DONE (sc);
+ SC_RETURN_STRING (sc, get_current_dir_name ());
+}
+
+pointer
+do_mach_port_allocate (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("mach-port-allocate");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, mach_port_t, right, number, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t name;
+ err = mach_port_allocate (task, right, &name);
+ SC_RETURN_INT (sc, name);
+}
+
+pointer
+do_mach_port_deallocate (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("mach-port-deallocate");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, mach_port_t, right, number, args);
+ SC_ARGS_DONE (sc);
+ err = mach_port_deallocate (task, right);
+ SC_RETURN (sc);
+}
+
+pointer
+do_mach_port_destroy (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("mach-port-destroy");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, mach_port_t, right, number, args);
+ SC_ARGS_DONE (sc);
+ err = mach_port_destroy (task, right);
+ SC_RETURN (sc);
+}
+
+pointer
+do_mach_port_insert_right (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("mach-port-insert-right");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, mach_port_t, name, number, args);
+ SC_ARG (sc, mach_port_t, right, number, args);
+ SC_ARG (sc, mach_msg_type_name_t, right_type, number, args);
+ SC_ARGS_DONE (sc);
+ err = mach_port_insert_right (task, name, right, right_type);
+ SC_RETURN_INT (sc, right);
+}
+
+pointer
+do_task_get_special_port (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-get-special-port");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, int, which, number, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t special_port;
+ err = task_get_special_port (task, which, &special_port);
+ SC_RETURN_INT (sc, special_port);
+}
+
+pointer
+do_task_set_special_port (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("task-set-special-port");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, int, which, number, args);
+ SC_ARG (sc, mach_port_t, special_port, number, args);
+ SC_ARGS_DONE (sc);
+ err = task_set_special_port (task, which, special_port);
+ SC_RETURN (sc);
+}
+
+pointer
+do_device_open (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("device-open");
+ SC_ARG (sc, mach_port_t, master, number, args);
+ SC_ARG (sc, int, flags, number, args);
+ SC_ARG (sc, char *, name, path, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t device;
+ err = device_open (master, flags, name, &device);
+ SC_RETURN_INT (sc, device);
+}
+
+/* Hurd functions. */
+pointer
+do_bind_root (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("_bind-root");
+ SC_ARG (sc, mach_port_t, control, number, args);
+ SC_ARG (sc, file_t, dotdot_node, number, args);
+ SC_ARGS_DONE (sc);
+ if (! MACH_PORT_VALID (control)
+ || ! MACH_PORT_VALID (dotdot_node))
+ SC_RETURN_ERR (sc, EINVAL);
+
+ if (_hurd_ports)
+ SC_RETURN_ERR (sc, EPERM); /* XXX */
+
+ uid_t uids[1] = { 0 };
+ size_t uids_len = 1;
+ gid_t gids[1] = { 0 };
+ size_t gids_len = 1;
+
+ retry_type retry;
+ char retryname[1024]; /* XXX */
+ file_t root;
+ err = fsys_getroot (control,
+ dotdot_node,
+ MACH_MSG_TYPE_MAKE_SEND,
+ uids, uids_len,
+ gids, gids_len,
+ (O_READ|O_EXEC),
+ &retry,
+ retryname,
+ &root);
+ if (err)
+ SC_RETURN (sc);
+
+ // XXX check root
+ portarray_template[INIT_PORT_CRDIR] = root;
+ portarray_template[INIT_PORT_CWDIR] = root;
+
+ err = mach_port_mod_refs (mach_task_self (),
+ root, MACH_PORT_RIGHT_SEND, +2);
+ assert_perror (err);
+
+ /* We have no portarray or intarray because there was no
+ exec_startup data; _hurd_init was never called. We now have the
+ crucial ports, so create a portarray and call _hurd_init. */
+ mach_port_t *portarray;
+ portarray = mmap (0, INIT_PORT_MAX * sizeof *portarray,
+ PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ memcpy (portarray, portarray_template, INIT_PORT_MAX * sizeof *portarray);
+ /* XXX intarray */
+ _hurd_init (0, global_argv, portarray, INIT_PORT_MAX, NULL, 0);
+ SC_RETURN (sc);
+}
+
+pointer
+do_bind_proc (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("bind-proc");
+ SC_ARG (sc, mach_port_t, procserver, number, args);
+ SC_ARGS_DONE (sc);
+ /* Give the library our proc server port. */
+ err = mach_port_mod_refs (mach_task_self (),
+ procserver, MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], procserver);
+
+ /* When we called _hurd_init, the proc server wasn't around. */
+ _hurd_new_proc_init (global_argv, NULL, 0); /* XXX intarray */
+ SC_RETURN (sc);
+}
+
+pointer
+do_bind_auth (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("bind-auth");
+ SC_ARG (sc, mach_port_t, authserver, number, args);
+ SC_ARGS_DONE (sc);
+ /* Give the library our auth server port. */
+ _hurd_port_set (&_hurd_ports[INIT_PORT_AUTH], authserver);
+ portarray_template[INIT_PORT_AUTH] = authserver;
+ err = mach_port_mod_refs (mach_task_self (),
+ authserver, MACH_PORT_RIGHT_SEND, +2);
+ assert_perror (err);
+ SC_RETURN (sc);
+}
+
+pointer
+do_bind_term (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("bind-term");
+ SC_ARG (sc, mach_port_t, term, number, args);
+ SC_ARGS_DONE (sc);
+ int new_fd0, new_fd1, new_fd2;
+ FILE *new_stdin, *new_stdout, *new_stderr;
+
+ new_fd0 = openport (copy_send_right (term), O_RDONLY);
+ if (new_fd0 < 0)
+ SC_RETURN_ERR (sc, errno);
+
+ new_fd1 = openport (copy_send_right (term), O_WRONLY);
+ if (new_fd1 < 0)
+ SC_RETURN_ERR (sc, errno);
+
+ new_fd2 = openport (copy_send_right (term), O_WRONLY);
+ if (new_fd2 < 0)
+ SC_RETURN_ERR (sc, errno);
+
+ dup2 (new_fd0, 0);
+ close (new_fd0);
+ dup2 (new_fd1, 1);
+ close (new_fd1);
+ dup2 (new_fd2, 2);
+ close (new_fd2);
+
+ // XXX proper error handling, restore all state on error
+
+ new_stdin = fdopen (0, "r");
+ if (new_stdin == NULL)
+ SC_RETURN_ERR (sc, errno);
+
+ new_stdout = fdopen (1, "w");
+ if (new_stdout == NULL)
+ SC_RETURN_ERR (sc, errno);
+
+ new_stderr = fdopen (2, "w");
+ if (new_stderr == NULL)
+ SC_RETURN_ERR (sc, errno);
+
+ fclose (stdin);
+ fclose (stdout);
+ fclose (stderr);
+ stdin = new_stdin;
+ stdout = new_stdout;
+ stderr = new_stderr;
+
+ scheme_set_input_port_file (sc, stdin);
+ scheme_set_output_port_file (sc, stdout);
+
+ SC_RETURN (sc);
+}
+
+pointer
+do_fsys_init (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("fsys-init!");
+ SC_ARG (sc, mach_port_t, fsys, number, args);
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARG (sc, mach_port_t, auth, number, args);
+ SC_ARGS_DONE (sc);
+ err = fsys_init (fsys, proc, MACH_MSG_TYPE_COPY_SEND, auth);
+ SC_RETURN (sc);
+}
+
+pointer
+do_proc_task2proc (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc-task2proc");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t result;
+ err = proc_task2proc (proc, task, &result);
+ SC_RETURN_INT (sc, result);
+}
+
+pointer
+do_proc_task2pid (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc-task2pid");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARGS_DONE (sc);
+ pid_t pid;
+ err = proc_task2pid (proc, task, &pid);
+ SC_RETURN_INT (sc, pid);
+}
+
+pointer
+do_proc_pid2task (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc-pid2task");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARG (sc, pid_t, pid, number, args);
+ SC_ARGS_DONE (sc);
+ task_t task;
+ err = proc_pid2task (proc, pid, &task);
+ SC_RETURN_INT (sc, task);
+}
+
+pointer
+do_proc_wait (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc-wait");
+ SC_ARG (sc, process_t, proc, number, args);
+ SC_ARG (sc, pid_t, pid, number, args);
+ SC_ARG (sc, int, options, number, args);
+ SC_ARGS_DONE (sc);
+ int status, sigcode;
+ rusage_t rusage; // XXX
+ pid_t pid_status;
+ err = proc_wait (proc, pid, options,
+ &status, &sigcode, &rusage, &pid_status);
+#define IMC(A, B) _cons (sc, sc->vptr->mk_integer (sc, A), (B), 1)
+ SC_RETURN_POINTER (sc, IMC (status,
+ IMC (sigcode,
+ IMC (pid_status, sc->NIL))));
+#undef IMC
+}
+
+pointer
+do_proc_mark_exec (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc-mark-exec!");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARGS_DONE (sc);
+ err = proc_mark_exec (proc);
+ SC_RETURN (sc);
+}
+
+pointer
+do_proc_mark_important (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc->mark-important!");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARGS_DONE (sc);
+ err = proc_mark_important (proc);
+ SC_RETURN (sc);
+}
+
+pointer
+do_proc_child (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc->proc->child!");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARG (sc, mach_port_t, child, number, args);
+ SC_ARGS_DONE (sc);
+ err = proc_child (proc, child);
+ if (err) error (0, err, "proc_child");
+ SC_RETURN (sc);
+}
+
+pointer
+do_proc_set_init_task (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc->task->set-init-task!");
+ SC_ARG (sc, mach_port_t, proc, number, args);
+ SC_ARG (sc, mach_port_t, task, number, args);
+ SC_ARGS_DONE (sc);
+ err = proc_set_init_task (proc, task);
+ SC_RETURN (sc);
+}
+
+/* Set up the initial value of the standard exec data. */
+/* XXX: Provide primitives and implement in Scheme. */
+pointer
+do_proc_auth_set_std_execdata (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc->auth->set-std-execdata!");
+ SC_ARG (sc, process_t, proc, number, args);
+ SC_ARG (sc, auth_t, auth, number, args);
+ SC_ARGS_DONE (sc);
+
+ auth_t nullauth;
+ mach_port_t pt;
+ mach_port_t ref;
+ mach_port_t *std_port_array;
+ int *std_int_array;
+ int i;
+
+ std_port_array = alloca (sizeof (mach_port_t) * INIT_PORT_MAX);
+ memset (std_port_array, 0, sizeof(mach_port_t) * INIT_PORT_MAX);
+ std_int_array = alloca (sizeof (int) * INIT_INT_MAX);
+ memset (std_int_array, 0, sizeof(int) * INIT_INT_MAX);
+
+ err = auth_makeauth (auth,
+ NULL, MACH_MSG_TYPE_COPY_SEND, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ &nullauth);
+ if (err)
+ goto out;
+
+ /* MAKE_SEND is safe in these transactions because we destroy REF
+ ourselves each time. */
+ pt = getcwdir ();
+ ref = mach_reply_port ();
+ err = io_reauthenticate (pt, ref, MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ goto out;
+
+ err = auth_user_authenticate (nullauth, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &std_port_array[INIT_PORT_CWDIR]);
+ if (err)
+ goto out;
+
+ mach_port_destroy (mach_task_self (), ref);
+ mach_port_deallocate (mach_task_self (), pt);
+
+ pt = getcrdir ();
+ ref = mach_reply_port ();
+ err = io_reauthenticate (pt, ref, MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ goto out;
+
+ err = auth_user_authenticate (nullauth, ref, MACH_MSG_TYPE_MAKE_SEND,
+ &std_port_array[INIT_PORT_CRDIR]);
+ if (err)
+ goto out;
+
+ mach_port_destroy (mach_task_self (), ref);
+ mach_port_deallocate (mach_task_self (), pt);
+
+ std_port_array[INIT_PORT_AUTH] = nullauth;
+ std_int_array[INIT_UMASK] = CMASK;
+
+ err = proc_setexecdata (proc, std_port_array,
+ MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ std_int_array, INIT_INT_MAX);
+
+ out:
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ if (MACH_PORT_VALID (std_port_array[i]))
+ {
+ error_t err;
+ err = mach_port_deallocate (mach_task_self (), std_port_array[i]);
+ assert_perror (err);
+ }
+ SC_RETURN (sc);
+}
+
+pointer
+do_proc_make_login_coll (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc->make-login-coll!");
+ SC_ARG (sc, process_t, proc, number, args);
+ SC_ARGS_DONE (sc);
+ err = proc_make_login_coll (proc);
+ SC_RETURN (sc);
+}
+
+pointer
+do_proc_setsid (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("proc->setsid!");
+ SC_ARG (sc, process_t, proc, number, args);
+ SC_ARGS_DONE (sc);
+ err = proc_setsid (proc);
+ SC_RETURN (sc);
+}
+
+pointer
+do_tcsetpgrp (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("tcsetpgrp");
+ SC_ARG (sc, int, fd, number, args);
+ SC_ARG (sc, pid_t, pgrp, number, args);
+ SC_ARGS_DONE (sc);
+ tcsetpgrp (fd, pgrp);
+ SC_RETURN (sc);
+}
+
+pointer
+ffi_argz2list (scheme *sc, const char *argz, size_t argz_len, const char *entry)
+{
+ entry = argz_next (argz, argz_len, entry);
+ if (argz == NULL || argz_len == 0 || entry == NULL)
+ return sc->NIL;
+ return _cons (sc,
+ mk_string (sc, entry),
+ ffi_argz2list (sc, argz, argz_len, entry),
+ 1);
+}
+
+void
+ffi_list2argz (scheme *sc, char **argz, size_t *argz_len, pointer list)
+{
+ while (sc->vptr->is_pair (list))
+ {
+ char *v;
+ if (sc->vptr->is_string (sc->vptr->pair_car (list)))
+ v = sc->vptr->string_value (sc->vptr->pair_car (list));
+ else if (sc->vptr->is_symbol (sc->vptr->pair_car (list)))
+ v = sc->vptr->symname (sc->vptr->pair_car (list));
+ else
+ continue; // XXX this just silently drops values
+ argz_add (argz, argz_len, v);
+ list = sc->vptr->pair_cdr (list);
+ }
+}
+
+pointer
+do_file_set_translator (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("file-set-translator");
+ SC_ARG (sc, file_t, node, number, args);
+ SC_ARG (sc, int, passive_flags, number, args);
+ SC_ARG (sc, int, active_flags, number, args);
+ SC_ARG (sc, int, goaway_flags, number, args);
+ SC_ARG (sc, pointer, arguments, list, args);
+ char *argz = NULL;
+ size_t argz_len = 0;
+ ffi_list2argz (sc, &argz, &argz_len, arguments);
+ SC_ARG (sc, mach_port_t, active_control, number, args);
+ SC_ARG (sc, mach_msg_type_name_t, active_controlPoly, number, args);
+ SC_ARGS_DONE (sc);
+ err = file_set_translator (node,
+ passive_flags, active_flags, goaway_flags,
+ argz, argz_len,
+ active_control, active_controlPoly);
+ SC_RETURN (sc);
+}
+
+// XXX
+pointer
+do__exec (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("_exec");
+ process_t proc, child_proc = MACH_PORT_NULL;
+ mach_port_t dtable[STDERR_FILENO+1];
+ mach_port_t portarray[INIT_PORT_MAX];
+ int default_ints[INIT_INT_MAX];
+ char *argz = NULL;
+ size_t argz_len = 0;
+ int i;
+
+ SC_ARG (sc, file_t, file, number, args);
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, pointer, arguments, list, args);
+ ffi_list2argz (sc, &argz, &argz_len, arguments);
+ SC_ARG (sc, mach_port_t, bootstrap, number, args);
+ SC_ARGS_DONE (sc);
+
+ proc = getproc ();
+ if (MACH_PORT_VALID (proc))
+ {
+ err = proc_task2proc (proc, task, &child_proc);
+ mach_port_deallocate (mach_task_self (), proc);
+ if (err)
+ SC_RETURN (sc);
+ }
+
+ dtable[STDIN_FILENO] = getdport (STDIN_FILENO);
+ dtable[STDOUT_FILENO] = getdport (STDOUT_FILENO);
+ dtable[STDERR_FILENO] = getdport (STDERR_FILENO);
+
+ memcpy (portarray, portarray_template, INIT_PORT_MAX * sizeof *portarray);
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ copy_send_right (portarray[i]);
+
+ portarray[INIT_PORT_CWDIR] = getcwdir ();
+ portarray[INIT_PORT_CRDIR] = getcrdir ();
+ portarray[INIT_PORT_PROC] = child_proc;
+ portarray[INIT_PORT_BOOTSTRAP] = copy_send_right (bootstrap);
+
+ memset (default_ints, 0, INIT_INT_MAX * sizeof *default_ints);
+ /* All programs we start should ignore job control stop signals.
+ That way Posix.1 B.2.2.2 is satisfied where it says that programs
+ not run under job control shells are protected. */
+ default_ints[INIT_SIGIGN] = (sigmask (SIGTSTP)
+ | sigmask (SIGTTIN)
+ | sigmask (SIGTTOU));
+
+ err = task_set_name (task, argz);
+ if (err)
+ {
+ error (0, err, "task_set_name");
+ goto lose;
+ }
+
+ err = file_exec (file, task, 0,
+ argz, argz_len,
+ NULL, 0, /* env, env_len */
+ dtable, MACH_MSG_TYPE_COPY_SEND, STDERR_FILENO+1,
+ portarray, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
+ default_ints, INIT_INT_MAX,
+ NULL, 0, NULL, 0);
+
+ lose:
+ for (i = 0; i < STDERR_FILENO+1; i++)
+ if (MACH_PORT_VALID (dtable[i]))
+ {
+ error_t err = mach_port_deallocate (mach_task_self (), dtable[i]);
+ assert_perror (err);
+ }
+ for (i = 0; i < INIT_PORT_MAX; i++)
+ if (MACH_PORT_VALID (portarray[i]))
+ {
+ error_t err = mach_port_deallocate (mach_task_self (), portarray[i]);
+ assert_perror (err);
+ }
+
+ SC_RETURN_INT (sc, task);
+}
+
+pointer
+do_file_get_fs_options (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("file-get-fs-options");
+ SC_ARG (sc, file_t, node, number, args);
+ SC_ARGS_DONE (sc);
+ char *argz = NULL;
+ size_t argz_len = 0;
+ pointer result;
+ err = file_get_fs_options (node, &argz, &argz_len);
+ if (err)
+ SC_RETURN (sc);
+ result = ffi_argz2list (sc, argz, argz_len, NULL);
+ vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len);
+ SC_RETURN_POINTER (sc, result);
+}
+
+const char *
+schemify_name (const char *s, int macro)
+{
+ char *n = strdup (s), *p;
+ if (n == NULL)
+ return s;
+ for (p = n; *p; p++)
+ {
+ *p = (char) tolower (*p);
+ /* We convert _ to - in identifiers. We allow, however, for
+ function names to start with a leading _. The functions in
+ this namespace are not yet finalized and might change or
+ vanish without warning. Use them with care. */
+ if (! macro
+ && p != n
+ && *p == '_')
+ *p = '-';
+ }
+ return n;
+}
+
+void
+ffi_update (scheme *sc)
+{
+ // XXX nothng
+}
+
+void
+ffi_init (scheme *sc)
+{
+ char *version;
+ asprintf (&version, "%s/%s", argp_program_version, "TinyScheme 1.41");
+ define_ (sc, "version", mk_string (sc, version));
+ free (version);
+
+ /* bitwise arithmetic */
+ define_function (sc, "logand", logand);
+ define_function (sc, "logior", logior);
+ define_function (sc, "logxor", logxor);
+ define_function (sc, "lognot", lognot);
+
+ /* Mach stuff. */
+ define_constant (sc, MACH_PORT_NULL);
+ define_constant (sc, MACH_PORT_DEAD); // XXX signedness
+
+ define_constant (sc, MACH_MSG_TYPE_UNSTRUCTURED);
+ define_constant (sc, MACH_MSG_TYPE_BIT);
+ define_constant (sc, MACH_MSG_TYPE_BOOLEAN);
+ define_constant (sc, MACH_MSG_TYPE_INTEGER_16);
+ define_constant (sc, MACH_MSG_TYPE_INTEGER_32);
+ define_constant (sc, MACH_MSG_TYPE_CHAR);
+ define_constant (sc, MACH_MSG_TYPE_BYTE);
+ define_constant (sc, MACH_MSG_TYPE_INTEGER_8);
+ define_constant (sc, MACH_MSG_TYPE_REAL);
+ define_constant (sc, MACH_MSG_TYPE_INTEGER_64);
+ define_constant (sc, MACH_MSG_TYPE_STRING);
+ define_constant (sc, MACH_MSG_TYPE_STRING_C);
+ define_constant (sc, MACH_MSG_TYPE_MOVE_RECEIVE);
+ define_constant (sc, MACH_MSG_TYPE_MOVE_SEND);
+ define_constant (sc, MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ define_constant (sc, MACH_MSG_TYPE_COPY_SEND);
+ define_constant (sc, MACH_MSG_TYPE_MAKE_SEND);
+ define_constant (sc, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ define_constant (sc, MACH_MSG_TYPE_PORT_NAME);
+ define_constant (sc, MACH_MSG_TYPE_PORT_RECEIVE);
+ define_constant (sc, MACH_MSG_TYPE_PORT_SEND);
+ define_constant (sc, MACH_MSG_TYPE_PORT_SEND_ONCE);
+ define_constant (sc, MACH_MSG_TYPE_PROTECTED_PAYLOAD);
+ define_constant (sc, MACH_MSG_TYPE_LAST);
+ define_constant (sc, MACH_MSG_TYPE_POLYMORPHIC);
+
+ define_constant (sc, MACH_PORT_RIGHT_SEND);
+ define_constant (sc, MACH_PORT_RIGHT_RECEIVE);
+ define_constant (sc, MACH_PORT_RIGHT_SEND_ONCE);
+ define_constant (sc, MACH_PORT_RIGHT_PORT_SET);
+ define_constant (sc, MACH_PORT_RIGHT_DEAD_NAME);
+ define_constant (sc, MACH_PORT_RIGHT_NUMBER);
+
+ define_constant (sc, KERN_SUCCESS);
+ define_constant (sc, KERN_INVALID_ADDRESS);
+ define_constant (sc, KERN_PROTECTION_FAILURE);
+ define_constant (sc, KERN_NO_SPACE);
+ define_constant (sc, KERN_INVALID_ARGUMENT);
+ define_constant (sc, KERN_FAILURE);
+ define_constant (sc, KERN_RESOURCE_SHORTAGE);
+ define_constant (sc, KERN_NOT_RECEIVER);
+ define_constant (sc, KERN_NO_ACCESS);
+ define_constant (sc, KERN_MEMORY_FAILURE);
+ define_constant (sc, KERN_MEMORY_ERROR);
+ define_constant (sc, KERN_NOT_IN_SET);
+ define_constant (sc, KERN_NAME_EXISTS);
+ define_constant (sc, KERN_ABORTED);
+ define_constant (sc, KERN_INVALID_NAME);
+ define_constant (sc, KERN_INVALID_TASK);
+ define_constant (sc, KERN_INVALID_RIGHT);
+ define_constant (sc, KERN_INVALID_VALUE);
+ define_constant (sc, KERN_UREFS_OVERFLOW);
+ define_constant (sc, KERN_INVALID_CAPABILITY);
+ define_constant (sc, KERN_RIGHT_EXISTS);
+ define_constant (sc, KERN_INVALID_HOST);
+ define_constant (sc, KERN_MEMORY_PRESENT);
+ define_constant (sc, KERN_WRITE_PROTECTION_FAILURE);
+ define_constant (sc, KERN_TERMINATED);
+ define_constant (sc, MACH_MSG_SUCCESS);
+ define_constant (sc, MACH_MSG_MASK);
+ define_constant (sc, MACH_MSG_IPC_SPACE);
+ define_constant (sc, MACH_MSG_VM_SPACE);
+ define_constant (sc, MACH_MSG_IPC_KERNEL);
+ define_constant (sc, MACH_MSG_VM_KERNEL);
+ define_constant (sc, MACH_SEND_IN_PROGRESS);
+ define_constant (sc, MACH_SEND_INVALID_DATA);
+ define_constant (sc, MACH_SEND_INVALID_DEST);
+ define_constant (sc, MACH_SEND_TIMED_OUT);
+ define_constant (sc, MACH_SEND_WILL_NOTIFY);
+ define_constant (sc, MACH_SEND_NOTIFY_IN_PROGRESS);
+ define_constant (sc, MACH_SEND_INTERRUPTED);
+ define_constant (sc, MACH_SEND_MSG_TOO_SMALL);
+ define_constant (sc, MACH_SEND_INVALID_REPLY);
+ define_constant (sc, MACH_SEND_INVALID_RIGHT);
+ define_constant (sc, MACH_SEND_INVALID_NOTIFY);
+ define_constant (sc, MACH_SEND_INVALID_MEMORY);
+ define_constant (sc, MACH_SEND_NO_BUFFER);
+ define_constant (sc, MACH_SEND_NO_NOTIFY);
+ define_constant (sc, MACH_SEND_INVALID_TYPE);
+ define_constant (sc, MACH_SEND_INVALID_HEADER);
+ define_constant (sc, MACH_RCV_IN_PROGRESS);
+ define_constant (sc, MACH_RCV_INVALID_NAME);
+ define_constant (sc, MACH_RCV_TIMED_OUT);
+ define_constant (sc, MACH_RCV_TOO_LARGE);
+ define_constant (sc, MACH_RCV_INTERRUPTED);
+ define_constant (sc, MACH_RCV_PORT_CHANGED);
+ define_constant (sc, MACH_RCV_INVALID_NOTIFY);
+ define_constant (sc, MACH_RCV_INVALID_DATA);
+ define_constant (sc, MACH_RCV_PORT_DIED);
+ define_constant (sc, MACH_RCV_IN_SET);
+ define_constant (sc, MACH_RCV_HEADER_ERROR);
+ define_constant (sc, MACH_RCV_BODY_ERROR);
+
+ define_constant (sc, TASK_KERNEL_PORT);
+ define_constant (sc, TASK_EXCEPTION_PORT);
+ define_constant (sc, TASK_BOOTSTRAP_PORT);
+
+ define_constant (sc, RB_DEBUGGER);
+ define_constant (sc, RB_HALT);
+ define_constant (sc, RB_AUTOBOOT);
+
+ define_ (sc, "mach-task-self", mk_integer (sc, mach_task_self ()));
+
+ define_function (sc, "mach-port-allocate", mach_port_allocate);
+ define_function (sc, "mach-port-deallocate", mach_port_deallocate);
+ define_function (sc, "mach-port-destroy", mach_port_destroy);
+ //define_function (sc, "mach-port-get-refs", mach_port_get_refs);
+ //define_function (sc, "mach-port-mod-refs", mach_port_mod_refs);
+ define_function (sc, "mach-port-insert-right", mach_port_insert_right);
+ //define_function (sc, "mach-port-extract-right", mach_port_extract_right);
+
+ define_function (sc, "task-create", task_create);
+ define_function (sc, "task-suspend", task_suspend);
+ define_function (sc, "task-resume", task_resume);
+ define_function (sc, "task-terminate", task_terminate);
+ define_function (sc, "task-set-name", task_set_name);
+ define_function (sc, "task-get-special-port", task_get_special_port);
+ define_function (sc, "task-set-special-port", task_set_special_port);
+ define_function (sc, "host-reboot", host_reboot);
+
+ define_function (sc, "vm-set-default-memory-manager",
+ vm_set_default_memory_manager);
+
+ /* Device protocol. */
+ define_constant (sc, D_READ);
+ define_constant (sc, D_WRITE);
+ define_function (sc, "device-open", device_open);
+
+ /* Hurd stuff. */
+ define_constant (sc, EXEC_NEWTASK);
+ define_constant (sc, EXEC_SECURE);
+ define_constant (sc, EXEC_DEFAULTS);
+ define_constant (sc, EXEC_SIGTRAP);
+ define_constant (sc, EXEC_STACK_ARGS);
+ define_constant (sc, FS_TRANS_FORCE);
+ define_constant (sc, FS_TRANS_EXCL);
+ define_constant (sc, FS_TRANS_SET);
+ define_constant (sc, FS_TRANS_ORPHAN);
+ define_constant (sc, FS_RETRY_NORMAL);
+ define_constant (sc, FS_RETRY_REAUTH);
+ define_constant (sc, FS_RETRY_MAGICAL);
+ define_constant (sc, FSYS_GOAWAY_NOWAIT);
+ define_constant (sc, FSYS_GOAWAY_NOSYNC);
+ define_constant (sc, FSYS_GOAWAY_FORCE);
+ define_constant (sc, FSYS_GOAWAY_UNLINK);
+ define_constant (sc, FSYS_GOAWAY_RECURSE);
+ define_constant (sc, INIT_PORT_CWDIR);
+ define_constant (sc, INIT_PORT_CRDIR);
+ define_constant (sc, INIT_PORT_AUTH);
+ define_constant (sc, INIT_PORT_PROC);
+ define_constant (sc, INIT_PORT_CTTYID);
+ define_constant (sc, INIT_PORT_BOOTSTRAP);
+ define_constant (sc, INIT_PORT_MAX);
+ define_constant (sc, INIT_UMASK);
+ define_constant (sc, INIT_SIGMASK);
+ define_constant (sc, INIT_SIGIGN);
+ define_constant (sc, INIT_SIGPENDING);
+ define_constant (sc, INIT_TRACEMASK);
+ define_constant (sc, INIT_INT_MAX);
+
+ define_constant (sc, O_RDONLY);
+ define_constant (sc, O_WRONLY);
+ define_constant (sc, O_RDWR);
+ define_constant (sc, O_EXEC);
+ define_constant (sc, O_CREAT);
+ define_constant (sc, O_NOTRANS);
+
+ define_variable (sc, exception_port);
+ define_variable (sc, rootnode);
+
+ /* glibc. */
+ define_function (sc, "sleep", sleep);
+ define_function (sc, "usleep", usleep);
+ define_function (sc, "getcwd", getcwd);
+ define_function (sc, "chdir", chdir);
+ define_function (sc, "strerror", strerror);
+ define_function (sc, "getproc", getproc);
+
+ /* Boot process */
+ define_function (sc, "bind-root", bind_root);
+ define_function (sc, "bind-proc", bind_proc);
+ define_function (sc, "bind-auth", bind_auth);
+ define_function (sc, "bind-term", bind_term);
+ define_function (sc, "fsys-init", fsys_init);
+
+ /* Early bootstrap protocols. */
+ define_function (sc, "handle-startup-procinit", handle_startup_procinit);
+ define_function (sc, "handle-startup-authinit", handle_startup_authinit);
+ define_function (sc, "startup-procinit-reply", startup_procinit_reply);
+ define_function (sc, "startup-authinit-reply", startup_authinit_reply);
+
+ define_function (sc, "startup-essential-task", startup_essential_task);
+ define_function (sc, "startup-request-notification",
+ startup_request_notification);
+ define_function (sc, "startup-reboot", startup_reboot);
+
+ /* Process and translator startup. */
+ define_function (sc, "handle-exec-startup", handle_exec_startup);
+ define_function (sc, "handle-fsys-startup", handle_fsys_startup);
+
+ /* Hurd fs API */
+ define_function (sc, "file-name-lookup", file_name_lookup);
+ define_function (sc, "file-set-translator", file_set_translator);
+ define_function (sc, "file-get-fs-options", file_get_fs_options);
+
+ /* Hurd process API */
+ define_function (sc, "proc-wait!", proc_wait);
+ define_function (sc, "proc->task->proc", proc_task2proc);
+ define_function (sc, "proc->task->pid", proc_task2pid);
+ define_function (sc, "proc->pid->task", proc_pid2task);
+ define_function (sc, "proc->mark-important!", proc_mark_important);
+ define_function (sc, "proc->mark-exec!", proc_mark_exec);
+ define_function (sc, "proc->task->child!", proc_child);
+ define_function (sc, "proc->task->set-init-task!", proc_set_init_task);
+ define_function (sc, "proc->make-login-coll!", proc_make_login_coll);
+ define_function (sc, "proc->setsid!", proc_setsid);
+
+ /* Terminal magic. */
+ define_function (sc, "tcsetpgrp", tcsetpgrp);
+
+ /* Hurd hacks. */
+ define_function (sc, "frob-task", frob_task);
+ define_function (sc, "elf-exec", elf_exec);
+ define_function (sc, "_exec", _exec);
+ define_function (sc, "start-handling-early-startup",
+ start_handling_early_startup);
+ define_function (sc, "get-essential-tasks", get_essential_tasks);
+ define_function (sc, "get-registered-tasks", get_registered_tasks);
+ define_function (sc, "proc->auth->set-std-execdata!",
+ proc_auth_set_std_execdata);
+
+ /* User interface. */
+ define_function (sc, "enable-readline", enable_readline);
+ define_function (sc, "prompt", prompt);
+
+ /* XXX */
+ ffi_update (sc);
+}
diff --git a/bootshell/ffi.h b/bootshell/ffi.h
new file mode 100644
index 0000000..a563454
--- /dev/null
+++ b/bootshell/ffi.h
@@ -0,0 +1,167 @@
+/* Convenience functions and macros for the foreign function interface.
+
+ 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 . */
+
+#ifndef _HURD_BOOTSHELL_FFI_H
+#define _HURD_BOOTSHELL_FFI_H
+
+#include
+#include
+
+#include "scheme.h"
+#include "scheme-private.h"
+
+// XXX drop name
+#define SC_FFI_PROLOG(NAME) \
+ const char *__ffi_name __attribute__ ((unused)) = NAME; \
+ unsigned int __ffi_arg_index __attribute__ ((unused)) = 1; \
+ error_t err = 0; \
+
+#define CONVERSION_number(SC, X) ivalue
+#define CONVERSION_string(SC, X) string_value
+#define CONVERSION_list(SC, X)
+#define CONVERSION_path(SC, X) ((SC)->vptr->is_string (X) \
+ ? (SC)->vptr->string_value \
+ : (SC)->vptr->symname)
+
+#define IS_A_number(SC, X) (SC)->vptr->is_number (X)
+#define IS_A_string(SC, X) (SC)->vptr->is_string (X)
+#define IS_A_list(SC, X) (SC)->vptr->is_list (SC, X)
+#define IS_A_path(SC, X) ((SC)->vptr->is_string (X) \
+ || (SC)->vptr->is_symbol (X))
+
+// XXX proper error handling
+#define SC_ARG(SC, CTYPE, TARGET, WANT, ARGS) \
+ if ((ARGS) == (SC)->NIL) { \
+ fprintf (stderr, "Error: %s: too few arguments: " \
+ "want " #TARGET "("#WANT"/"#CTYPE")\n", __ffi_name); \
+ return (SC)->NIL; \
+ } \
+ if (! IS_A_##WANT ((SC), pair_car (ARGS))) { \
+ fprintf (stderr, "Error: %s: argument %d must be: " \
+ #WANT "\n", __ffi_name, __ffi_arg_index); \
+ return (SC)->NIL; \
+ } \
+ CTYPE TARGET = CONVERSION_##WANT (SC, pair_car (ARGS)) (pair_car (ARGS)); \
+ ARGS = pair_cdr (ARGS); \
+ __ffi_arg_index += 1; \
+
+#define SC_ARGS_DONE(SC) \
+ /* XXX */
+
+#define SC_RETURN_ERR(SC, ERR) \
+ return _cons ((SC), mk_integer ((SC), (ERR)), (SC)->NIL, 1)
+
+#define SC_RETURN(SC) SC_RETURN_ERR (SC, err)
+
+#define SC_RETURN_POINTER(SC, X) \
+ return _cons ((SC), mk_integer ((SC), err), \
+ _cons ((SC), (X), (SC)->NIL, 1), 1)
+#define SC_RETURN_INT(SC, X) \
+ SC_RETURN_POINTER ((SC), mk_integer ((SC), (X)))
+#define SC_RETURN_STRING(SC, X) \
+ SC_RETURN_POINTER ((SC), mk_string ((SC), (X)))
+
+const char *schemify_name (const char *s, int macro);
+
+#define define_function(S, P, F) \
+ ({ \
+ char _sc_buf[256]; \
+ scheme_define ((S), \
+ (S)->global_env, \
+ mk_symbol ((S), schemify_name ("_" #F, 0)), \
+ mk_foreign_func ((S), (do_##F))); \
+ snprintf (_sc_buf, sizeof _sc_buf, \
+ "(define (%1$s . a) (ffi-apply \"%1$s\" %2$s a))", \
+ P, schemify_name ("_" #F, 0)); \
+ scheme_load_string (S, _sc_buf); \
+ })
+
+#define define_constant(S, C) \
+ scheme_define ((S), \
+ (S)->global_env, \
+ mk_symbol ((S), schemify_name (#C, 1)), \
+ mk_integer ((S), (C)))
+
+#define define_(S, SYM, EXP) \
+ scheme_define ((S), (S)->global_env, mk_symbol ((S), (SYM)), EXP)
+
+#define define_variable(S, C) \
+ scheme_define ((S), \
+ (S)->global_env, \
+ mk_symbol ((S), schemify_name (#C, 0)), \
+ mk_integer ((S), (C)))
+
+#define define_variable_pointer(S, C, P) \
+ scheme_define ((S), \
+ (S)->global_env, \
+ mk_symbol ((S), schemify_name (#C, 0)), \
+ (P))
+
+#define define_variable_string(S, C) \
+ define_variable_pointer (S, C, (S)->vptr->mk_string (S, C ?: ""))
+
+/* A variant of scheme_load_string that does not require the string to
+ be zero-terminated. */
+void scheme_load_mem (scheme *, const char *, const char *);
+
+#define declare_embedded_script(X) \
+ extern char X##_size[] asm("_binary__"#X"_o_size"); \
+ extern char X##_start[] asm("_binary__"#X"_o_start"); \
+ extern char X##_end[] asm("_binary__"#X"_o_end")
+
+#define load_embedded_script(S, X) \
+ ({ \
+ scheme_load_mem ((S), X##_start, X##_end); \
+ if ((S)->retcode != 0) \
+ fprintf (stderr, "Errors encountered evaluating %s\n", #X); \
+ })
+
+declare_embedded_script (init);
+declare_embedded_script (boot);
+declare_embedded_script (runsystem);
+
+void ffi_update (scheme *sc);
+void ffi_init (scheme *sc);
+
+pointer ffi_argz2list (scheme *sc,
+ const char *argz, size_t argz_len, const char *entry);
+void ffi_list2argz (scheme *sc, char **argz, size_t *argz_len, pointer list);
+
+// XXX
+error_t service_fsys_request (mach_port_t bootstrap,
+ mach_port_t realnode,
+ mach_msg_type_name_t realnodePoly,
+ mach_msg_timeout_t timeout,
+ mach_port_t *control);
+
+/* Forward declarations. */
+
+/* exec-startup.c */
+pointer do_handle_exec_startup (scheme *sc, pointer args);
+
+/* elf-exec.c */
+pointer do_elf_exec (scheme *sc, pointer args);
+
+/* fsys.c */
+pointer do_handle_fsys_startup (scheme *sc, pointer args);
+
+/* frob_task.c */
+pointer do_frob_task (scheme *sc, pointer args);
+
+#endif /* _HURD_BOOTSHELL_FFI_H */
diff --git a/bootshell/frob-task.c b/bootshell/frob-task.c
new file mode 100644
index 0000000..05d0238
--- /dev/null
+++ b/bootshell/frob-task.c
@@ -0,0 +1,131 @@
+/* Supply a task (e.g. the kernel) with the command line arguments.
+
+ Copyright (C) 1993-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 "ffi.h"
+
+/* Frobnicate the given TASK and the proc server's idea of it, so the
+ command line can be read as for a normal Hurd process. */
+error_t
+frob_task (task_t kernel_task, const char *argz, size_t argz_len)
+{
+ error_t err;
+ process_t proc, kernel_proc = MACH_PORT_NULL;
+
+ int argc, i;
+ const char *entry;
+ size_t windowsz;
+ vm_address_t mine, his;
+
+ proc = getproc ();
+ if (! MACH_PORT_VALID (proc))
+ return EINVAL;
+
+ err = proc_task2proc (proc, kernel_task, &kernel_proc);
+ if (err)
+ goto out;
+
+ /* Mark the kernel task as an essential task so that we or the proc server
+ never want to task_terminate it. */
+ err = proc_mark_important (kernel_proc);
+ if (err)
+ goto out;
+
+ /* Our arguments make up the multiboot command line used to boot the
+ kernel. We'll write into the kernel task a page containing a
+ canonical argv array and argz of those words. */
+
+ argc = argz_count (argz, argz_len);
+
+ windowsz = round_page (((argc + 1) * sizeof (char *)) + argz_len);
+
+ mine = (vm_address_t) mmap (0, windowsz, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ if (mine == (vm_address_t) -1)
+ {
+ err = errno;
+ goto out;
+ }
+
+ err = vm_allocate (kernel_task, &his, windowsz, 1);
+ if (err)
+ {
+ error (0, err, "cannot allocate %Zu bytes in kernel task", windowsz);
+ munmap ((caddr_t) mine, windowsz);
+ goto out;
+ }
+
+ for (i = 0, entry = argz; entry != NULL;
+ ++i, entry = argz_next (argz, argz_len, entry))
+ ((char **) mine)[i] = ((char *) &((char **) his)[argc + 1]
+ + (entry - argz));
+ ((char **) mine)[argc] = NULL;
+ memcpy (&((char **) mine)[argc + 1], argz, argz_len);
+
+ /* We have the data all set up in our copy, now just write it over. */
+ err = vm_write (kernel_task, his, mine, windowsz);
+ munmap ((caddr_t) mine, windowsz);
+ if (err)
+ goto out;
+
+ /* The argument vector is set up in the kernel task at address HIS.
+ Finally, we can inform the proc server where to find it. */
+ err = proc_set_arg_locations (kernel_proc,
+ his, his + (argc * sizeof (char *)));
+ if (err)
+ error (0, err, "proc_set_arg_locations for kernel task");
+
+ out:
+ {
+ error_t e;
+ e = mach_port_deallocate (mach_task_self (), proc);
+ assert_perror (e);
+
+ if (MACH_PORT_VALID (kernel_proc))
+ e = mach_port_deallocate (mach_task_self (), kernel_proc);
+ assert_perror (e);
+ }
+ return err;
+}
+
+pointer
+do_frob_task (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("frob-task");
+ SC_ARG (sc, task_t, task, number, args);
+ SC_ARG (sc, pointer, arguments, list, args);
+ SC_ARGS_DONE (sc);
+ char *argz = NULL;
+ size_t argz_len = 0;
+ ffi_list2argz (sc, &argz, &argz_len, arguments);
+ err = frob_task (task, argz, argz_len);
+ free (argz);
+ SC_RETURN (sc);
+}
diff --git a/bootshell/fs.c b/bootshell/fs.c
new file mode 100644
index 0000000..88536d8
--- /dev/null
+++ b/bootshell/fs.c
@@ -0,0 +1,111 @@
+/* Handles the fs protocol.
+
+ 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
+
+// eek #include "fs_S.h"
+
+#include "bootshell.h"
+
+mach_port_t rootnode;
+
+/* A top-level function for the paging thread that just services paging
+ requests. */
+static void *
+service_fs_requests (void *arg)
+{
+ extern boolean_t fs_server ();
+
+ int trace_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+ {
+ error (0, 0, "(fs-server: %d)", inp->msgh_id);
+ int i = fs_server (inp, outp);
+ return i;
+ }
+
+ // XXX specialized demuxer
+ while (1)
+ mach_msg_server (0? trace_demuxer: fs_server, 0, rootnode);
+
+ /* Not reached. */
+ return NULL;
+}
+
+error_t
+init_fs_server (void)
+{
+ error_t err;
+ pthread_t t;
+
+ err = mach_port_allocate (mach_task_self (),
+ MACH_PORT_RIGHT_RECEIVE,
+ &rootnode);
+ if (err)
+ return err;
+
+ /* Make a thread to service the fs protocol. */
+ err = pthread_create (&t, NULL, service_fs_requests, NULL);
+ if (err)
+ return err;
+ pthread_detach (t);
+
+ err = mach_port_insert_right (mach_task_self (),
+ rootnode,
+ rootnode,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (err)
+ return err;
+
+ setcrdir (rootnode); // XXX do we want this? not sure what for tbh.
+ setcwdir (rootnode);
+ portarray_template[INIT_PORT_CRDIR] = rootnode;
+ portarray_template[INIT_PORT_CWDIR] = rootnode;
+
+ return err;
+}
+
+error_t
+S_dir_lookup (file_t file,
+ char *path,
+ int flags,
+ mode_t mode,
+ enum retry_type *retry,
+ char *retryname,
+ file_t *returned_port,
+ mach_msg_type_name_t *returned_port_poly)
+{
+ if (file != rootnode)
+ return EOPNOTSUPP;
+
+ if (portarray_template[INIT_PORT_CRDIR] == rootnode)
+ /* Still no root filesystem. */
+ return EOPNOTSUPP;
+
+ *retry = FS_RETRY_NORMAL;
+ strncpy (retryname, path, sizeof (string_t));
+ *returned_port = portarray_template[INIT_PORT_CRDIR];
+ *returned_port_poly = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+}
diff --git a/bootshell/fsys.c b/bootshell/fsys.c
new file mode 100644
index 0000000..a92b90d
--- /dev/null
+++ b/bootshell/fsys.c
@@ -0,0 +1,123 @@
+/* Handles the fsys protocol.
+
+ 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
+
+// eek #include "fsys_S.h"
+
+#include "bootshell.h"
+#include "ffi.h"
+
+/* XXX would be nice not to use a global variable, maybe with
+ payloads. */
+static struct
+{
+ /* Filled by caller. */
+ mach_port_t bootstrap_port;
+ mach_port_t realnode;
+ mach_msg_type_name_t realnodePoly;
+
+ /* Filled by the server function. */
+ mach_port_t control_port;
+} fsys_startup_args;
+
+error_t
+S_fsys_startup (mach_port_t bootstrap,
+ int openflags,
+ mach_port_t control_port,
+ mach_port_t *realnode,
+ mach_msg_type_name_t *realnodePoly)
+{
+ assert (MACH_PORT_VALID (fsys_startup_args.bootstrap_port));
+ if (bootstrap != fsys_startup_args.bootstrap_port)
+ return EOPNOTSUPP;
+
+ fsys_startup_args.control_port = control_port;
+ *realnode = fsys_startup_args.realnode;
+ *realnodePoly = fsys_startup_args.realnodePoly;
+ return 0;
+}
+
+static boolean_t
+fsys_startup_demuxer (mach_msg_header_t *request,
+ mach_msg_header_t *reply)
+{
+ // XXX
+ extern boolean_t fsys_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ switch (request->msgh_id)
+ {
+ case 22000: /* fsys_startup */
+ return fsys_server (request, reply);
+ }
+
+ /* Return MIG_BAD_ID. */
+ mig_reply_setup (request, reply);
+ return FALSE;
+}
+
+error_t
+service_fsys_request (mach_port_t bootstrap,
+ mach_port_t realnode,
+ mach_msg_type_name_t realnodePoly,
+ mach_msg_timeout_t timeout,
+ mach_port_t *control)
+{
+ error_t err;
+
+ if (! MACH_PORT_VALID (bootstrap))
+ return EINVAL;
+
+ fsys_startup_args.bootstrap_port = bootstrap;
+ fsys_startup_args.realnode = realnode;
+ fsys_startup_args.realnodePoly = realnodePoly;
+ fsys_startup_args.control_port = MACH_PORT_NULL;
+
+ err = mach_msg_server_timeout_once (fsys_startup_demuxer, 0, bootstrap,
+ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
+ timeout);
+ if (err != MACH_MSG_SUCCESS)
+ return err;
+
+ *control = fsys_startup_args.control_port;
+ return 0;
+}
+
+pointer
+do_handle_fsys_startup (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("handle-fsys-startup");
+ SC_ARG (sc, mach_port_t, bootstrap, number, args);
+ SC_ARG (sc, mach_port_t, realnode, number, args);
+ SC_ARG (sc, mach_msg_type_name_t, realnodePoly, number, args);
+ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t control;
+ err = service_fsys_request (bootstrap,
+ realnode,
+ realnodePoly,
+ timeout,
+ &control);
+ SC_RETURN_INT (sc, control);
+}
diff --git a/bootshell/main.c b/bootshell/main.c
new file mode 100644
index 0000000..958b40b
--- /dev/null
+++ b/bootshell/main.c
@@ -0,0 +1,264 @@
+/* Bootshell, a Scheme shell, a flexible multiserver bootstrap solution.
+
+ Copyright (C) 1995-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 "scheme.h"
+#include "scheme-private.h"
+
+#include "bootshell.h"
+#include "ffi.h"
+
+task_t bootscript_task;
+task_t rootfs_server_task;
+task_t exec_server_task;
+
+#define _HURD_RUNSYSTEM "/hurd/runsystem.scm"
+#define _BOOT_COMMAND "(boot)"
+
+char *multiboot_command_line;
+char *boot_init_program;
+boolean_t boot_pause;
+char *boot_command = _BOOT_COMMAND;
+char **global_argv;
+boolean_t booted;
+boolean_t interactive;
+
+scheme scm;
+
+/* We catch exceptions using this port. */
+mach_port_t exception_port;
+
+mach_port_t console;
+
+mach_port_t portarray_template[INIT_PORT_MAX];
+
+const char *argp_program_version = STANDARD_HURD_VERSION (bootshell);
+
+#define OPT_HOST_PRIV_PORT (-1)
+#define OPT_DEVICE_MASTER_PORT (-2)
+#define OPT_BOOTSCRIPT_TASK (-3)
+#define OPT_ROOTFS_SERVER_TASK (-4)
+#define OPT_EXEC_SERVER_TASK (-5)
+#define OPT_BOOT_CMDLINE (-6)
+#define OPT_BOOT_COMMAND (-7)
+#define OPT_BOOT_INIT_PROGRAM (-8)
+#define OPT_BOOT_PAUSE (-9)
+
+static const struct argp_option options[] =
+{
+ {"interactive", 'I', NULL, 0, "start interactive repl"},
+ {0,0,0,0, "Boot options:", -2},
+ {"multiboot-command-line", OPT_BOOT_CMDLINE, "ARGS", 0,
+ "The multiboot kernel command line"},
+ {"bootflags", 0, 0, OPTION_ALIAS|OPTION_HIDDEN},
+ {"boot-debug-pause", OPT_BOOT_PAUSE, NULL, 0,
+ "Pause for keystroke before starting bootstrap programs"},
+ {"boot-command", OPT_BOOT_COMMAND, "S-EXPRESSION", 0,
+ "Command to run, default: " _BOOT_COMMAND},
+ {"host-priv-port", OPT_HOST_PRIV_PORT, "PORT"},
+ {"device-master-port", OPT_DEVICE_MASTER_PORT, "PORT"},
+ {"bootscript-task", OPT_BOOTSCRIPT_TASK, "PORT"},
+ {"rootfs-server-task", OPT_ROOTFS_SERVER_TASK, "PORT"},
+ {"exec-server-task", OPT_EXEC_SERVER_TASK, "PORT"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ /* Boot options */
+ case 'I':
+ interactive = 1;
+ break;
+ case OPT_DEVICE_MASTER_PORT:
+ _hurd_device_master = atoi (arg); break;
+ case OPT_HOST_PRIV_PORT:
+ _hurd_host_priv = atoi (arg); break;
+ case OPT_BOOTSCRIPT_TASK:
+ bootscript_task = atoi (arg); break;
+ case OPT_ROOTFS_SERVER_TASK:
+ rootfs_server_task = atoi (arg); break;
+ case OPT_EXEC_SERVER_TASK:
+ exec_server_task = atoi (arg); break;
+ case OPT_BOOT_CMDLINE:
+ multiboot_command_line = arg; break;
+ case OPT_BOOT_INIT_PROGRAM:
+ boot_init_program = arg; break;
+ case OPT_BOOT_PAUSE:
+ boot_pause = 1; break;
+ case OPT_BOOT_COMMAND:
+ boot_command = arg; break;
+ case ARGP_KEY_END:
+ global_argv = state->argv; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ break;
+ }
+ return 0;
+}
+
+static const char doc[] =
+ "Start and maintain hurd core servers and system run state";
+
+static const struct argp argp =
+{ options, parse_opt, 0, doc };
+
+void
+panic (const char *msg)
+{
+ puts (msg);
+ fflush (stdout);
+ _exit (127);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ /* XXX */
+ setenv ("TERM", "mach", 1);
+ setenv ("COLS", "80", 1);
+ setenv ("LINES", "25", 1);
+
+ argp_parse (&argp, argc, argv, /*ARGP_NO_ERRS|*/ARGP_IN_ORDER, 0, 0);
+ global_argv = argv; /* For calling _hurd_new_proc_init later. */
+
+ {
+ mach_port_t proc = getproc ();
+ if (MACH_PORT_VALID (proc))
+ {
+ booted = 1;
+ err = mach_port_deallocate (mach_task_self (), proc);
+ assert_perror (err);
+ }
+ }
+
+ if (! booted)
+ {
+ err = init_exception_handling ();
+ if (err)
+ error (1, err, "init_exception_handling");
+ }
+
+ err = init_fs_server (); // XXX don't start automatically
+ if (err)
+ error (1, err, "init_fs_server");
+
+ if (MACH_PORT_VALID (_hurd_device_master))
+ {
+ err = device_open (_hurd_device_master, D_READ|D_WRITE,
+ "console", &console);
+ if (err)
+ panic ("Failed to open console.");
+
+ stdin = mach_open_devstream (console, "r");
+ stdout = stderr = mach_open_devstream (console, "w");
+ if (! stdin || ! stdout)
+ panic ("Failed to open device stream.");
+
+ setvbuf (stdout, NULL, _IONBF, 0);
+ }
+
+ if (! scheme_init (&scm))
+ error (1, errno, "scheme_init");
+
+ scheme_set_input_port_file (&scm, stdin);
+ scheme_set_output_port_file (&scm, stdout);
+
+ ffi_init (&scm);
+
+ load_embedded_script (&scm, init);
+ load_embedded_script (&scm, boot);
+ load_embedded_script (&scm, runsystem);
+
+ define_variable (&scm, bootscript_task);
+ define_variable (&scm, rootfs_server_task);
+ define_variable (&scm, exec_server_task);
+ define_variable (&scm, boot_pause);
+
+ define_ (&scm, "host-priv",
+ scm.vptr->mk_integer (&scm, _hurd_host_priv));
+ define_ (&scm, "device-master",
+ scm.vptr->mk_integer (&scm, _hurd_device_master));
+
+ define_variable_string (&scm, multiboot_command_line);
+ define_variable_string (&scm, boot_init_program);
+ define_variable_string (&scm, boot_command);
+ define_variable (&scm, boot_pause);
+ {
+ char *argz = NULL;
+ size_t argz_len = 0;
+ err = argz_create (argv, &argz, &argz_len);
+ assert_perror (err);
+ define_ (&scm, "argv", ffi_argz2list (&scm, argz, argz_len, NULL));
+ }
+
+ if (MACH_PORT_VALID (bootscript_task))
+ {
+ vm_size_t size;
+ vm_prot_t prot, max_prot;
+ mach_port_t obj;
+ vm_offset_t addr = 0, offs;
+ vm_inherit_t inh;
+ int shared;
+
+ err =
+ vm_region (bootscript_task, &addr, &size, &prot, &max_prot, &inh, &shared,
+ &obj, &offs);
+ if (err)
+ error (12, err, "vm_region");
+
+ vm_offset_t script;
+ size_t count;
+ err = vm_read (bootscript_task, addr, size, &script, &count);
+ if (err)
+ error (12, err, "vm_read");
+ scheme_load_mem (&scm, (char *) script, (char *) script + size);
+ if (scm.retcode) {
+ fprintf (stderr, "Error: %d\n", scm.retcode);
+ }
+ }
+
+ if (! interactive)
+ scheme_load_string (&scm, boot_command);
+
+ while (1)
+ scheme_load_string (&scm, "(interactive-repl)");
+
+ /* Not reached. */
+ scheme_deinit(&scm);
+ return 0;
+}
diff --git a/bootshell/mig-decls.h b/bootshell/mig-decls.h
new file mode 100644
index 0000000..76a67ff
--- /dev/null
+++ b/bootshell/mig-decls.h
@@ -0,0 +1,22 @@
+/* MIG declarations for bootshell.
+
+ 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
+
+#define MIG_EOPNOTSUPP ({ abort (); EOPNOTSUPP; })
diff --git a/bootshell/mig-mutate.h b/bootshell/mig-mutate.h
new file mode 100644
index 0000000..145e83a
--- /dev/null
+++ b/bootshell/mig-mutate.h
@@ -0,0 +1,27 @@
+/* MIG mutations for bootshell.
+
+ 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 . */
+
+#define HURD_DEFAULT_PAYLOAD_TO_PORT 1
+
+#define FILE_IMPORTS \
+ import "mig-decls.h";
+#define FSYS_IMPORTS \
+ import "mig-decls.h";
+#define STARTUP_IMPORTS \
+ import "mig-decls.h";
diff --git a/bootshell/runsystem.scm b/bootshell/runsystem.scm
new file mode 100644
index 0000000..befb07c
--- /dev/null
+++ b/bootshell/runsystem.scm
@@ -0,0 +1,211 @@
+;; The Hurd server bootstrap.
+;;
+;; 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 . */
+
+(define timeout 1000) ; 1 second
+
+(define (pause)
+ (if (= 1 boot-pause) (prompt "Press enter to continue...")))
+
+;; Tests if a device with the given NAME exists.
+(define (devprobe? name)
+ (letport ((device (catch MACH_PORT_NULL
+ (device-open device-master D_READ name))))
+ (mach-port-valid? device)))
+
+;; Insert RIGHT into TASK. Returns the name of RIGHT in TASK.
+(define (task-insert-send-right task right)
+ (let loop ((name 1))
+ (catch (loop (+ name 1))
+ (mach-port-insert-right task name right MACH_MSG_TYPE_COPY_SEND)
+ name)))
+
+;; Inserts RIGHT into TASK and returns a command line argument OPTION
+;; with the value set to the name of RIGHT in TASK.
+(define (make-arg option task right)
+ (string-append "--" option "="
+ (number->string (task-insert-send-right task right))))
+
+;; Returns a function that can be passed to `bootstrap-translator' to
+;; resume a translator loaded by the traditional bootscript mechanism.
+(define (resume-translator task args)
+ (lambda (bootstrap)
+ (task-set-exception-port task (make-send-right exception-port))
+ (task-set-bootstrap-port task bootstrap)
+ (pause)
+ (task-resume task)
+ (handle-exec-startup bootstrap args timeout)
+ task))
+
+;; Returns a function that can be passed to `bootstrap-translator' to
+;; start a translator that is loaded from a disk.
+(define (start-translator task argv)
+ (lambda (bootstrap)
+ (letport ((proc (getproc)))
+ (pause)
+ (_exec (file-name-lookup (car argv) O_EXEC 0) task argv bootstrap)
+ (if (mach-port-valid? proc)
+ (let ((child-proc (proc->task->proc proc task)))
+ (proc->mark-exec! child-proc)
+ (proc->mark-important! child-proc)
+ (proc->task->child! proc task))))))
+
+;; Bootstraps a translator using the fsys protocol.
+(define (bootstrap-translator prepare-task realnode)
+ (let* ((bootstrap
+ (mach-port-allocate mach-task-self MACH_PORT_RIGHT_RECEIVE))
+ (task
+ (prepare-task (make-send-right bootstrap))))
+ (handle-fsys-startup bootstrap realnode MACH_MSG_TYPE_COPY_SEND timeout)))
+
+;; Bootstraps the proc server using the startup protocol.
+(define (bootstrap-proc prepare-task)
+ (let* ((bootstrap
+ (mach-port-allocate mach-task-self MACH_PORT_RIGHT_RECEIVE))
+ (task
+ (prepare-task (make-send-right bootstrap))))
+ (handle-startup-procinit bootstrap timeout)))
+
+;; Bootstraps the auth server using the startup protocol.
+(define (bootstrap-auth prepare-task)
+ (let* ((bootstrap
+ (mach-port-allocate mach-task-self MACH_PORT_RIGHT_RECEIVE))
+ (task
+ (prepare-task (make-send-right bootstrap))))
+ (handle-startup-authinit bootstrap timeout)))
+
+;; Bootstraps a translator using the fsys protocol and installs it as
+;; root filesystem.
+(define (bind-root prepare-task)
+ (let ((control
+ (bootstrap-translator prepare-task (make-send-right rootnode))))
+ (_bind-root control rootnode)
+ control))
+
+;; Bootstraps a translator using the fsys protocol and installs it as
+;; active translator for the node PATH.
+(define (bind path prepare-task)
+ (letport ((realnode (file-name-lookup path O_NOTRANS #o666)) ;; XXX mode 0 should be fine
+ (control (bootstrap-translator prepare-task realnode)))
+ (set-active-translator path 0 0 control)
+ (copy-send-right control)))
+
+;; We start servers when the proc server is not yet around. Once the
+;; proc server is available, we use this function to update its state
+;; related to TASK.
+(define (fixup-task task)
+ (letport ((myproc (getproc))
+ (p (proc->task->proc myproc task)))
+ (proc->task->child! myproc task)
+ (proc->mark-important! p)
+ (proc->mark-exec! p)))
+
+(define (bootstrap rootfs-args exec-args)
+ (log "Hurd server bootstrap: ")
+
+ (log "rootfs ")
+ (let ((rootfs-control (bind-root (resume-translator rootfs-server-task
+ rootfs-args)))
+ (startup-control (mach-port-allocate mach-task-self
+ MACH_PORT_RIGHT_RECEIVE))
+ (proc-task (task-create mach-task-self 0))
+ (auth-task (task-create mach-task-self 0))
+ ;; Projections for the cookies returned by bootstrap-*.
+ (:reply car) (:replyPoly cadr) (:server caddr))
+ (start-handling-early-startup startup-control)
+ (set-active-translator "/servers/startup" 0 0
+ (make-send-right startup-control))
+ (log "exec ")
+ (bind "/servers/exec" (resume-translator exec-server-task exec-args))
+
+ ;; Starting proc and auth is tricky, we need to do it simultaneously.
+ (let ((pc (bootstrap-proc (start-translator proc-task '("/hurd/proc"))))
+ (ac (bootstrap-auth (start-translator auth-task '("/hurd/auth")))))
+ (log "proc ")
+ (startup-procinit-reply (:reply pc) (:replyPoly pc) ESUCCESS
+ mach-task-self (:server ac)
+ host-priv device-master)
+ (bind-proc (:server pc))
+
+ ;; Declare that these tasks are our children, and fix them up.
+ (map fixup-task (list rootfs-server-task exec-server-task
+ proc-task auth-task))
+
+ (log "auth ")
+ (startup-authinit-reply (:reply ac) (:replyPoly ac) ESUCCESS
+ (proc->task->proc (:server pc) auth-task))
+ (bind-auth (:server ac))
+
+ ;; Give the rootfs its proc and auth port.
+ (fsys-init rootfs-control
+ (proc->task->proc (:server pc) rootfs-server-task)
+ (:server ac))
+
+ ;; Supply the proc server with a standard template.
+ (proc->auth->set-std-execdata! (:server pc) (:server ac))
+
+ (mach-port-deallocate mach-task-self (:server pc))
+ (mach-port-deallocate mach-task-self (:server ac))))
+
+ (log "console ")
+ (bind "/dev/console"
+ (start-translator (task-create mach-task-self 0)
+ '("/hurd/term" "/dev/console" "device" "console")))
+
+ (letport ((term (file-name-lookup "/dev/console" O_RDWR 0)))
+ (bind-term term))
+
+ ;; If we made it this far, we can use libreadline!
+ (enable-readline)
+
+ ;; The standalone startup server watches essential servers, and
+ ;; handles the system shutdown.
+ (log "startup ")
+ (bind "/servers/startup"
+ (start-translator (task-create mach-task-self 0)
+ '("/hurd/startup-standalone")))
+
+ ;; Now that we have startup, register all servers to it.
+ (letport
+ ((startup (file-name-lookup "/servers/startup" 0 0)))
+ (let ((:port car) (:name cdr)) ;; Projections.
+ ;; We are essential.
+ (startup-essential-task startup mach-task-self MACH_PORT_NULL
+ "bootshell" host-priv)
+ (map (lambda (c)
+ (startup-essential-task startup (:port c) MACH_PORT_NULL
+ (:name c) host-priv))
+ (get-essential-tasks))
+ (map (lambda (c)
+ (startup-request-notification startup (:port c) (:name c)))
+ (get-registered-tasks))))
+
+ (log "pflocal ")
+ (bind "/servers/socket/1"
+ (start-translator (task-create mach-task-self 0)
+ '("/hurd/pflocal")))
+
+ (log "done.\n"))
+
+(define (boot)
+ (catch (panic "Hurd bootstrap failed: " (car last-exception) "\n")
+ (bootstrap '() '()))
+
+ (shell (lambda (prefix)
+ (prompt-append-prefix
+ (string-append "runsystem@" (hostname) " " (getcwd) " ") prefix))))
diff --git a/bootshell/scheme-config.h b/bootshell/scheme-config.h
new file mode 100644
index 0000000..127df9f
--- /dev/null
+++ b/bootshell/scheme-config.h
@@ -0,0 +1,31 @@
+/* TinyScheme configuration.
+
+ 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 . */
+
+#define STANDALONE 0
+#define USE_MATH 0
+#define USE_CHAR_CLASSIFIERS 1
+#define USE_ASCII_NAMES 1
+#define USE_STRING_PORTS 1
+#define USE_ERROR_HOOK 1
+#define USE_TRACING 1
+#define USE_COLON_HOOK 0
+#define USE_DL 0
+#define USE_PLIST 0
+#define USE_INTERFACE 1
+#define SHOW_ERROR_LINE 1
diff --git a/bootshell/startup.c b/bootshell/startup.c
new file mode 100644
index 0000000..4b9f71d
--- /dev/null
+++ b/bootshell/startup.c
@@ -0,0 +1,508 @@
+/* Handles the startup protocol.
+
+ 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 "startup_reply_U.h"
+// eek #include "startup_S.h"
+// eek #include "fsys_S.h"
+
+extern boolean_t startup_server (mach_msg_header_t *, mach_msg_header_t *);
+extern boolean_t fsys_server (mach_msg_header_t *, mach_msg_header_t *);
+
+#include "bootshell.h"
+#include "ffi.h"
+
+/* Handling of `startup_essential_task'. */
+
+static mach_port_t early_startup_port;
+
+/* This structure keeps track of each registered essential task. */
+struct port_string_tuple
+ {
+ struct port_string_tuple *next;
+ task_t port;
+ char *name;
+ };
+
+static struct port_string_tuple *essential_tasks;
+static struct port_string_tuple *registered_tasks;
+
+/* Record an essential task in the list. */
+static error_t
+add_tuple (struct port_string_tuple **list, mach_port_t port, const char *name)
+{
+ struct port_string_tuple *et;
+
+ et = malloc (sizeof *et);
+ if (et == NULL)
+ goto out;
+
+ et->port = port;
+ et->name = strdup (name);
+ if (et->name == NULL)
+ goto out;
+
+ et->next = *list;
+ *list = et;
+ return 0;
+
+ out:
+ free (et);
+ return ENOMEM;
+}
+
+/* fsys_goaway for early-boot /servers/startup. */
+error_t
+S_fsys_goaway (mach_port_t fsys,
+ int flags)
+{
+ if (fsys != early_startup_port)
+ return EOPNOTSUPP;
+ // XXX keep going = 0
+ return 0;
+}
+
+/* fsys_getroot for early-boot /servers/startup. */
+error_t
+S_fsys_getroot (mach_port_t fsys,
+ mach_port_t dotdotnode,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids,
+ int flags,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *ret,
+ mach_msg_type_name_t *rettype)
+{
+ if (fsys != early_startup_port)
+ return EOPNOTSUPP;
+
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *ret = early_startup_port;
+ *rettype = MACH_MSG_TYPE_MAKE_SEND;
+ return 0;
+}
+
+error_t
+S_startup_essential_task (startup_t server,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_portPoly,
+ mach_port_t task,
+ mach_port_t excpt,
+ string_t name,
+ mach_port_t credential)
+{
+ error_t err;
+ if (server != early_startup_port)
+ return EOPNOTSUPP;
+ if (credential != _hurd_host_priv)
+ return EPERM;
+
+ err = mach_port_deallocate (mach_task_self (), credential);
+ assert_perror (err);
+
+ if (MACH_PORT_VALID (excpt))
+ {
+ error (0, 0,
+ "Oh dear, someone actually send us their exception port.\n"
+ "I'm going to destroy it. Please investigate.");
+ err = mach_port_destroy (mach_task_self (), excpt);
+ assert_perror (err);
+ }
+
+ err = add_tuple (&essential_tasks, task, name);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+kern_return_t
+S_startup_request_notification (mach_port_t server,
+ mach_port_t notify,
+ char *name)
+{
+ if (server != early_startup_port)
+ return EOPNOTSUPP;
+
+ return add_tuple (®istered_tasks, notify, name);
+}
+
+static boolean_t
+early_startup_demuxer (mach_msg_header_t *request,
+ mach_msg_header_t *reply)
+{
+ /* XXX hardcoded msgh_ids */
+ switch (request->msgh_id)
+ {
+ case 29000: /* startup_essential_task */
+ case 29001: /* startup_request_notification */
+ return startup_server (request, reply);
+ case 22001: /* fsys_goaway */
+ case 22002: /* fsys_getroot */
+ return fsys_server (request, reply);
+ }
+
+ /* Return MIG_BAD_ID. */
+ mig_reply_setup (request, reply);
+ return FALSE;
+}
+
+static void *
+service_early_startup_requests (void *arg)
+{
+ // XXX while (keep_going) ...
+ while (1)
+ mach_msg_server (early_startup_demuxer, 0,
+ early_startup_port);
+
+ /* Not reached. */
+ return NULL;
+}
+
+static error_t
+start_handling_early_startup (mach_port_t startup_port)
+{
+ error_t err;
+ pthread_t t;
+
+ if (MACH_PORT_VALID (early_startup_port))
+ return EINVAL;
+ early_startup_port = startup_port;
+
+ /* Make a thread to service `startup_essential_task' requests. */
+ err = pthread_create (&t, NULL, service_early_startup_requests,
+ NULL);
+ if (err)
+ return err;
+ pthread_detach (t);
+
+ return err;
+}
+
+pointer
+do_start_handling_early_startup (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("start-handling-early-startup");
+ SC_ARG (sc, mach_port_t, server_port, number, args);
+ SC_ARGS_DONE (sc);
+ err = start_handling_early_startup (server_port);
+ SC_RETURN (sc);
+}
+
+pointer
+do_get_essential_tasks (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("get-essential-tasks");
+ SC_ARGS_DONE (sc);
+ pointer result = sc->NIL;
+ struct port_string_tuple *et;
+ for (et = essential_tasks; et; et = et->next)
+#define IMC(A, B) _cons (sc, (A), (B), 1)
+ result = IMC (IMC (mk_integer (sc, et->port),
+ mk_string (sc, et->name)),
+ result);
+#undef IMC
+ SC_RETURN_POINTER (sc, result);
+}
+
+pointer
+do_get_registered_tasks (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("get-registered-tasks");
+ SC_ARGS_DONE (sc);
+ pointer result = sc->NIL;
+ struct port_string_tuple *rt;
+ for (rt = registered_tasks; rt; rt = rt->next)
+#define IMC(A, B) _cons (sc, (A), (B), 1)
+ result = IMC (IMC (mk_integer (sc, rt->port),
+ mk_string (sc, rt->name)),
+ result);
+#undef IMC
+ SC_RETURN_POINTER (sc, result);
+}
+
+/* Client stubs for startup. */
+pointer
+do_startup_essential_task (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("startup-essential-task");
+ SC_ARG (sc, mach_port_t, startup, number, args);
+ SC_ARG (sc, mach_port_t, task, number, args);
+ SC_ARG (sc, mach_port_t, exception, number, args);
+ SC_ARG (sc, char *, name, string, args);
+ SC_ARG (sc, mach_port_t, credential, number, args);
+ SC_ARGS_DONE (sc);
+ err = startup_essential_task (startup, task, exception, name, credential);
+ SC_RETURN (sc);
+}
+
+pointer
+do_startup_request_notification (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("startup-request-notification");
+ SC_ARG (sc, mach_port_t, startup, number, args);
+ SC_ARG (sc, mach_port_t, notify_port, number, args);
+ SC_ARG (sc, char *, name, string, args);
+ SC_ARGS_DONE (sc);
+ err = startup_request_notification (startup,
+ notify_port, MACH_MSG_TYPE_COPY_SEND,
+ name);
+ SC_RETURN (sc);
+}
+
+pointer
+do_startup_reboot (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("startup-reboot");
+ SC_ARG (sc, mach_port_t, startup, number, args);
+ SC_ARG (sc, mach_port_t, credential, number, args);
+ SC_ARG (sc, int, flags, number, args);
+ SC_ARGS_DONE (sc);
+ err = startup_reboot (startup, credential, flags);
+ SC_RETURN (sc);
+}
+
+/* Handling of `startup_procinit'. */
+
+/* XXX would be nice not to use a global variable, maybe with
+ payloads. */
+static struct
+{
+ /* Filled by caller. */
+ mach_port_t bootstrap_port;
+
+ /* Filled by the server function. */
+ mach_port_t reply;
+ mach_msg_type_name_t replyPoly;
+ process_t procserver;
+} startup_procinit_args;
+
+kern_return_t
+S_startup_procinit (startup_t bootstrap,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ process_t procserver,
+ mach_port_t *startuptask,
+ auth_t *auth,
+ mach_port_t *hostpriv,
+ mach_msg_type_name_t *hostprivPoly,
+ mach_port_t *devmaster,
+ mach_msg_type_name_t *devmasterPoly)
+{
+ if (bootstrap != startup_procinit_args.bootstrap_port)
+ return EOPNOTSUPP;
+
+ startup_procinit_args.reply = reply;
+ startup_procinit_args.replyPoly = replyPoly;
+ startup_procinit_args.procserver = procserver;
+ return MIG_NO_REPLY;
+}
+
+boolean_t
+startup_procinit_demuxer (mach_msg_header_t *request,
+ mach_msg_header_t *reply)
+{
+ if (request->msgh_id != 29003) /* XXX hardcoded msgh_id */
+ {
+ /* Return MIG_BAD_ID. */
+ mig_reply_setup (request, reply);
+ return FALSE;
+ }
+ return startup_server (request, reply);
+}
+
+error_t
+service_startup_procinit_request (mach_port_t bootstrap,
+ mach_msg_timeout_t timeout,
+ mach_port_t *reply,
+ mach_msg_type_name_t *replyPoly,
+ process_t *procserver)
+{
+ error_t err;
+
+ if (! MACH_PORT_VALID (bootstrap))
+ return EINVAL;
+
+ startup_procinit_args.bootstrap_port = bootstrap;
+
+ err = mach_msg_server_timeout_once (startup_procinit_demuxer, 0, bootstrap,
+ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
+ timeout);
+ if (err != MACH_MSG_SUCCESS)
+ return err;
+
+ *reply = startup_procinit_args.reply;
+ *replyPoly = startup_procinit_args.replyPoly;
+ *procserver = startup_procinit_args.procserver;
+ return 0;
+}
+
+pointer
+do_handle_startup_procinit (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("handle-startup-procinit");
+ SC_ARG (sc, mach_port_t, bootstrap, number, args);
+ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t reply;
+ mach_msg_type_name_t replyPoly;
+ mach_port_t proc;
+ err = service_startup_procinit_request (bootstrap, timeout,
+ &reply, &replyPoly, &proc);
+#define IMC(A, B) _cons (sc, sc->vptr->mk_integer (sc, A), (B), 1)
+ SC_RETURN_POINTER (sc, IMC (reply, IMC (replyPoly, IMC (proc, sc->NIL))));
+#undef IMC
+}
+
+pointer
+do_startup_procinit_reply (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("startup-procinit-reply");
+ SC_ARG (sc, mach_port_t, reply, number, args);
+ SC_ARG (sc, mach_msg_type_name_t, replyPoly, number, args);
+ SC_ARG (sc, int, retCode, number, args);
+ SC_ARG (sc, mach_port_t, startup_task, number, args);
+ SC_ARG (sc, mach_port_t, authserver, number, args);
+ SC_ARG (sc, mach_port_t, host_priv, number, args);
+ SC_ARG (sc, mach_port_t, device_master, number, args);
+ SC_ARGS_DONE (sc);
+ err = startup_procinit_reply (reply, replyPoly, retCode,
+ startup_task, authserver,
+ host_priv, MACH_MSG_TYPE_COPY_SEND,
+ device_master, MACH_MSG_TYPE_COPY_SEND);
+ SC_RETURN (sc);
+}
+
+/* Handling of `startup_authinit'. */
+
+/* XXX would be nice not to use a global variable, maybe with
+ payloads. */
+static struct
+{
+ /* Filled by caller. */
+ mach_port_t bootstrap_port;
+
+ /* Filled by the server function. */
+ mach_port_t reply;
+ mach_msg_type_name_t replyPoly;
+ mach_port_t authserver;
+} startup_authinit_args;
+
+/* Called by the auth server when it starts up. */
+
+kern_return_t
+S_startup_authinit (startup_t bootstrap,
+ mach_port_t reply,
+ mach_msg_type_name_t replyPoly,
+ mach_port_t auth,
+ mach_port_t *proc,
+ mach_msg_type_name_t *procPoly)
+{
+ if (bootstrap != startup_authinit_args.bootstrap_port)
+ return EOPNOTSUPP;
+
+ startup_authinit_args.reply = reply;
+ startup_authinit_args.replyPoly = replyPoly;
+ startup_authinit_args.authserver = auth;
+ return MIG_NO_REPLY;
+}
+
+boolean_t
+startup_authinit_demuxer (mach_msg_header_t *request,
+ mach_msg_header_t *reply)
+{
+ if (request->msgh_id != 29004) /* XXX hardcoded msgh_id */
+ {
+ /* Return MIG_BAD_ID. */
+ mig_reply_setup (request, reply);
+ return FALSE;
+ }
+ return startup_server (request, reply);
+}
+
+error_t
+service_startup_authinit_request (mach_port_t bootstrap,
+ mach_msg_timeout_t timeout,
+ mach_port_t *reply,
+ mach_msg_type_name_t *replyPoly,
+ mach_port_t *authserver)
+{
+ error_t err;
+
+ if (! MACH_PORT_VALID (bootstrap))
+ return EINVAL;
+
+ startup_authinit_args.bootstrap_port = bootstrap;
+
+ err = mach_msg_server_timeout_once (startup_authinit_demuxer, 0, bootstrap,
+ MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT,
+ timeout);
+ if (err != MACH_MSG_SUCCESS)
+ return err;
+
+ *reply = startup_authinit_args.reply;
+ *replyPoly = startup_authinit_args.replyPoly;
+ *authserver = startup_authinit_args.authserver;
+ return 0;
+}
+
+pointer
+do_handle_startup_authinit (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("handle-startup-authinit");
+ SC_ARG (sc, mach_port_t, bootstrap, number, args);
+ SC_ARG (sc, mach_msg_timeout_t, timeout, number, args);
+ SC_ARGS_DONE (sc);
+ mach_port_t reply;
+ mach_msg_type_name_t replyPoly;
+ mach_port_t auth;
+ err = service_startup_authinit_request (bootstrap, timeout,
+ &reply, &replyPoly, &auth);
+#define IMC(A, B) _cons (sc, sc->vptr->mk_integer (sc, A), (B), 1)
+ SC_RETURN_POINTER (sc, IMC (reply, IMC (replyPoly, IMC (auth, sc->NIL))));
+#undef IMC
+}
+
+pointer
+do_startup_authinit_reply (scheme *sc, pointer args)
+{
+ SC_FFI_PROLOG ("startup-authinit-reply");
+ SC_ARG (sc, mach_port_t, reply, number, args);
+ SC_ARG (sc, mach_msg_type_name_t, replyPoly, number, args);
+ SC_ARG (sc, int, retCode, number, args);
+ SC_ARG (sc, mach_port_t, authproc, number, args);
+ SC_ARGS_DONE (sc);
+ err = startup_authinit_reply (reply, replyPoly, retCode, authproc,
+ MACH_MSG_TYPE_COPY_SEND);
+ SC_RETURN (sc);
+}
diff --git a/bootshell/startup.h b/bootshell/startup.h
new file mode 100644
index 0000000..0fa9b4c
--- /dev/null
+++ b/bootshell/startup.h
@@ -0,0 +1,36 @@
+/* Handles the startup protocol.
+
+ 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 . */
+
+#ifndef _HURD_BOOTSHELL_STARTUP_H
+#define _HURD_BOOTSHELL_STARTUP_H
+
+#include
+
+pointer do_handle_startup_procinit (scheme *sc, pointer args);
+pointer do_handle_startup_authinit (scheme *sc, pointer args);
+pointer do_startup_procinit_reply (scheme *sc, pointer args);
+pointer do_startup_authinit_reply (scheme *sc, pointer args);
+pointer do_start_handling_early_startup (scheme *sc, pointer args);
+pointer do_get_essential_tasks (scheme *sc, pointer args);
+pointer do_get_registered_tasks (scheme *sc, pointer args);
+pointer do_startup_essential_task (scheme *sc, pointer args);
+pointer do_startup_request_notification (scheme *sc, pointer args);
+pointer do_startup_reboot (scheme *sc, pointer args);
+
+#endif /* _HURD_BOOTSHELL_STARTUP_H */
diff --git a/bootshell/utils.c b/bootshell/utils.c
new file mode 100644
index 0000000..9fcf17c
--- /dev/null
+++ b/bootshell/utils.c
@@ -0,0 +1,118 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+mach_msg_return_t
+mach_msg_server_timeout_once (boolean_t (*demux) (mach_msg_header_t *request,
+ mach_msg_header_t *reply),
+ mach_msg_size_t max_size,
+ mach_port_t rcv_name,
+ mach_msg_option_t option,
+ mach_msg_timeout_t timeout)
+{
+ mig_reply_header_t *request, *reply;
+ mach_msg_return_t mr;
+
+ if (! MACH_PORT_VALID (rcv_name))
+ return EINVAL;
+
+ if (max_size == 0)
+ {
+#ifdef MACH_RCV_LARGE
+ option |= MACH_RCV_LARGE;
+ max_size = 2 * __vm_page_size; /* Generic. Good? XXX */
+#else
+ max_size = 4 * __vm_page_size; /* XXX */
+#endif
+ }
+
+ request = alloca (max_size);
+ reply = alloca (max_size);
+
+ mr = mach_msg (&request->Head, MACH_RCV_MSG|option,
+ 0, max_size, rcv_name,
+ timeout, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ /* We have a request message. Pass it to DEMUX for processing. */
+ (void) (*demux) (&request->Head, &reply->Head);
+ assert (reply->Head.msgh_size <= max_size);
+
+ switch (reply->RetCode)
+ {
+ case KERN_SUCCESS:
+ /* Hunky dory. */
+ break;
+
+ case MIG_NO_REPLY:
+ /* The server function wanted no reply sent.
+ Loop for another request. */
+ return 0;
+
+ default:
+ /* Some error; destroy the request message to release any
+ port rights or VM it holds. Don't destroy the reply port
+ right, so we can send an error message. */
+ request->Head.msgh_remote_port = MACH_PORT_NULL;
+ mach_msg_destroy (&request->Head);
+ break;
+ }
+
+ if (reply->Head.msgh_remote_port == MACH_PORT_NULL)
+ {
+ /* No reply port, so destroy the reply. */
+ if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
+ mach_msg_destroy (&reply->Head);
+ return reply->RetCode;
+ }
+
+ /* Send the reply. */
+ mr = mach_msg (&reply->Head,
+ MACH_SEND_MSG|option,
+ reply->Head.msgh_size, max_size, rcv_name,
+ timeout, MACH_PORT_NULL);
+
+ /* See if a message error occurred. */
+ if (mr == MACH_SEND_INVALID_DEST)
+ /* The reply can't be delivered, so destroy it. This error
+ indicates only that the requester went away, so we
+ continue and get the next request. */
+ mach_msg_destroy (&reply->Head);
+
+ return mr != 0 ? mr : reply->RetCode;
+}
+
+/* Fill in default response. */
+void
+mig_reply_setup (
+ const mach_msg_header_t *in,
+ mach_msg_header_t *out)
+{
+ static const mach_msg_type_t RetCodeType = {
+ /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
+ /* msgt_size = */ 32,
+ /* msgt_number = */ 1,
+ /* msgt_inline = */ TRUE,
+ /* msgt_longform = */ FALSE,
+ /* msgt_deallocate = */ FALSE,
+ /* msgt_unused = */ 0
+ };
+
+#define InP (in)
+#define OutP ((mig_reply_header_t *) out)
+ OutP->Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InP->msgh_bits), 0);
+ OutP->Head.msgh_size = sizeof *OutP;
+ OutP->Head.msgh_remote_port = InP->msgh_remote_port;
+ OutP->Head.msgh_local_port = MACH_PORT_NULL;
+ OutP->Head.msgh_seqno = 0;
+ OutP->Head.msgh_id = InP->msgh_id + 100;
+ OutP->RetCodeType = RetCodeType;
+ OutP->RetCode = MIG_BAD_ID;
+#undef InP
+#undef OutP
+}
diff --git a/config.make.in b/config.make.in
index 0f1390a..012bcd5 100644
--- a/config.make.in
+++ b/config.make.in
@@ -95,6 +95,10 @@ HAVE_BLKID = @HAVE_BLKID@
libblkid_CFLAGS = @libblkid_CFLAGS@
libblkid_LIBS = @libblkid_LIBS@
+# How to compile and link against libreadline.
+HAVE_READLINE = @HAVE_READLINE@
+libreadline_LIBS = @libreadline_LIBS@
+
# Whether Sun RPC support is available.
HAVE_SUN_RPC = @HAVE_SUN_RPC@
diff --git a/configure.ac b/configure.ac
index 71f3a0e..898564e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -333,6 +333,21 @@ PKG_CHECK_MODULES([libblkid], [blkid],
AC_SUBST([libblkid_LIBS])
AC_SUBST([libblkid_CFLAGS])
+AC_ARG_WITH([readline],
+ [AS_HELP_STRING([--without-readline], [disable support for readline])],
+ [],
+ [with_readline=yes])
+
+LIBREADLINE=
+AS_IF([test "x$with_readline" != xno],
+ [AC_CHECK_LIB([readline], [main],
+ [AC_SUBST([libreadline_LIBS], ["-lreadline -lhistory -lncurses"])
+ AC_DEFINE([HAVE_LIBREADLINE], [1], [Define if you have libreadline])
+ ],
+ [AC_MSG_FAILURE(
+ [readline test failed (--without-readline to disable)])],
+ [-lncurses])])
+
AC_CONFIG_FILES([config.make ${makefiles}])
AC_OUTPUT
--
2.1.4