scheme shell

From: Olin Shivers <>
Subject: Re: Schema, a Scheme-based O{S,E}
Date: 2000/04/26
Message-ID: <>
Organization: Artificial Intelligence Lab, MIT
Newsgroups: comp.lang.scheme

Some comments:

    Basically what I'm thinking of doing, at least to get some quick
    headway, is to utilize the hardware support that both X and the Linux
    kernel give.  Both of these software systems provide a vast amount of
    support for different hardware devices and platforms and I don't want to
    remake the wheel when it comes to implementing the low-level device and
    architecture support for an operating system.  

OSKit gets you up and running on the bare metal pretty painlessly. It's
been used to get Scheme, Java & ML systems up on raw machines.

    Scsh looks suitable as well, but I've never heard of a native compiler for
    S48 and I'm somewhat wary of implementing one for it.

Scsh is not primarily an implementation. It is a design and a huge pile of
source, both of which are available, for free, on the Net. You can take
it all and repurpose it to any end you like. Just the design work it
represents is not insignificant.

A full compiler for S48 would be some work, but it would be quite easy to do a
byte-code->x86 translator. That would get you a huge performance improvement.
Performance is just not the issue, though. If you build something -- anything
-- real and it's useful, people will figure out ways to make it faster.

The big outstanding issues with scsh (Scheme48) are how to get dynamic
module loading/linking, and separate byte compilation of source modules.
I've been waiting 9 years for these things. Adding them to S48 would have
a big impact on the useability of the system, in terms of startup time
and memory footprint.

    Runlevels are a supreme example of what I'm talking about.  What the
    hell is a runlevel, really?  If I check the manual page for init(8)
    ("man init" -- how obvious is that?) I read:

    Now all that init really does, it seems, (at least sysvinit, the init
    package used on my Linux box, but most other init packages for Unices
    are similar) is spawn some gettys and a few programs to handle signals
    (like shutting down on C-M-Delete), and run a big nasty wad of shell
    scripts (the heinous, unmaintainable crap in the /etc/rc.d directory).

If you don't like that stuff, you can replace it with something written in any
good Unix-based Scheme *without* getting into the mess of doing your own
OS. The init process can be anything you want it to be; its architecture is
not baked into the Unix kernel design. Its job is one very well suited to
Scheme. As are things like inetd and sendmail -- a Scheme-based mail system
would be a fine thing.

I have, over time, moved a lot of the /etc scripts on my notebook over to scsh
-- ppp dialup, pcmcia, config bits, backup dumps, etc. It's *very* pleasant to
do this kind of stuff in scsh.

    If you think I'm crazy then go ahead and say it, 

You are crazy, but that's not important. The only thing that matters is
whether or not you do anything. Do *anything*, and you matter.

Just for fun, I append a typical system script I use that's written in Scheme.
It does backups over the net; I use it almost every day.

#!/usr/local/bin/scsh \
-o let-opt -e main -s

;;; Dump a file system on my notebook computer out to a backed-up
;;; disk on a sessile system. The bits are compressed, encrypted,
;;; and copied over the net using ssh to a file named
;;;     $name$level.gz.2f.
;;; If you say
;;;     netdump 0 / root
;;; then you do a level 0 dump of the / file system to a file named
;;; in the fixed directory /home/c3/shivers/mk-backup/stable/.
;;; We play some games with ssh and su, because this script has to be run
;;; by root in order to have total access to the file system being dumped,
;;; but you must be someone less threatening (me) so that the remote machine
;;; will allow you to ssh over and write the bits.
;;;     -Olin

(define tdir "/opt/backups/spool/shivers") ; The target directory
(define me "shivers")
(define rhost "")

(define (useage)
  (format (error-output-port)
	  "Usage: netdump level dir name\nFiles backed up to ~a on ~a.\n"
	  tdir rhost)
  (exit -1))

;;; These guys are useful for root scripts.

(define-syntax exec/su			; (exec/su uname . epf)
  (syntax-rules ()			; Su to UNAME, then exec EPF.
    ((exec/su user . epf)
     (begin (set-uid (->uid user))
	    (exec-epf . epf)))))

(define-syntax run/su			; (run/su uname . epf)
  (syntax-rules ()			; Run command EPF as user UNAME
    ((run/su user . epf)		
     (wait (fork (lambda () (exec/su user . epf)))))))

(define (main args)
  (if (= 4 (length args))
      (let* ((level (cadr   args))
	     (dir   (caddr  args))
	     (name  (cadddr args))

	     (fmt (lambda args (apply format #f args))) ; abbreviation
	     (newfile    (fmt "~a/new/~a~a.tgz.2f"    tdir name level))
	     (stablefile (fmt "~a/stable/~a~a.tgz.2f" tdir name level)))

	(format (error-output-port) "Starting level ~a dump of ~a to ~a.\n"
		level dir newfile)

	;; The exit status of a pipeline is the exit status of the last element
	;; in the pipeline -- so (| (dump) (copy-to-remote-machine)) won't
	;; tell us if the dump succeeded. So we do it the hard way -- we
	;; explicitly fork off the dump and copy procs, pipe them together
	;; by hand, and check them both.

	;; Fork off the dump process: dump uf<level> - <dir>
	(receive (from-dump dump-proc)
	         (run/port+proc (dump ,(fmt "uf~a" level) - ,dir))

	  ;; Fork off the compress/encrypt/remote-copy process,
	  ;; sucking bits from dump's stdout.
	  (let ((copy-proc (fork (lambda ()
				   (exec/su me
					    (| (gzip)
					       (ssh ,rhost
						    ; "dd of=/dev/tape"
						    ,(fmt "cat > ~a" newfile)))
					    (= 0 ,from-dump))))))
	    (close from-dump)
	    (cond ((and (zero? (wait dump-proc))	; Wait for them both
			(zero? (wait copy-proc)))	; to finish.

		   ;; The dump&net-copy won; move the file to the stable dir.
		   (run/su me (ssh ,rhost mv ,newfile ,stablefile))
		   (format (error-output-port) "Done.\n"))

		  (else (format (error-output-port)	; Oops.
				"Had a problem dumping ~a = ~a!\n" dir name)))))