Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

directory search path for EXEC -- new design

1 view
Skip to first unread message

Olin Shivers

unread,
Mar 11, 2001, 1:42:18 PM3/11/01
to
A while back, a design problem surfaced with the EXEC-PATH system call in scsh.
The call
(exec-path "ls" "-l" "/tmp")
causes scsh to search through an internal list of file directories for
one containing file "ls". When it finds one, it execs this binary with
the given arg vector. This is what happens when you say something like

(run (ls -l /tmp))

which expands into

(wait (fork (lambda () (exec-path "ls" "-l" "/tmp"))))

OK, from where does this list of directories to be searched come?
It is the value of the global variable

exec-path-list

which is initialised on startup to the contents of the $PATH env var:

(set! exec-path-list
(cond ((getenv "PATH") => split-colon-list)
(else (if (not quietly?)
(warn "Starting up with no path ($PATH)."))
'())))

So it is typically something like

'("/bin" "/usr/bin" "/usr/local/bin")

Now... suppose you'd like to change that. Maybe you'd like to write
a script that is insulated from $PATH, so nobody can mess with your
search path. You put this as the first line in your script:

(set! exec-path-list '("/sbin" "/bin" "/usr/bin"))

Oops, in the current 0.5.2 release, you rapidly discover that EXEC-PATH-LIST
is sitting inside another package, and you aren't allowed to SET! it from
the user package.

But even if you could, it would be wrong. Imagine a scsh server sitting
on a host. People authenticate to the server and get a connection to
a repl thread. You don't want *my* EXEC-PATH-LIST frobbing to interfere
with *your* thread, right?

This is just a specific example of a general principle: global state bad.

The answer is to provide this bit of state as a *fluid* cell. Fluid cells are
essentially cells whose contents are bound with dynamic scope, so threads can
share them or not share them, as desired.

(MAKE-FLUID init-value) => fluid create
(FLUID fluid) => value dereference
(LET-FLUID fluid value thunk) => value(s) of thunk bind
(SET-FLUID! fluid value) => undefined

I propose turning EXEC-PATH-LIST into a fluid. Your scripts
(if they open the FLUIDS package) can dereference it, rebind it,
and side-effect it.

That means that any current code that references EXEC-PATH-LIST is
going to BREAK. But I don't expect there *is* any such code, since
no one's been allowed to frob the variable.

Unfortunately, my archives of scsh posts are kind of a mess since I shifted
my center of gravity from Cambridge to Atlanta, and dejanews is a mess,
so I can't find the original reports & discussion. If anyone has these
posts, could you send them to me? If anyone has a *complete* archive
of comp.lang.scheme.scsh, I'd love to hear about that, too.

Any comments? I'm planning to put it into scsh, so now's a good time to
speak up.
-Olin

Friedrich Dominicus

unread,
Mar 12, 2001, 12:45:31 AM3/12/01
to
Olin Shivers <shi...@tokyo.cc.gatech.edu> writes:

>
> The answer is to provide this bit of state as a *fluid* cell. Fluid cells are
> essentially cells whose contents are bound with dynamic scope, so threads can
> share them or not share them, as desired.
>
> (MAKE-FLUID init-value) => fluid create
> (FLUID fluid) => value dereference
> (LET-FLUID fluid value thunk) => value(s) of thunk bind
> (SET-FLUID! fluid value) => undefined

Just a small sidekick. Common Lisp uses defvar and defparameter for it
;-) and there such things are called special ;-). The "usual" name for
Scheme might be define-dynamic-scoped-variable ;-)

It's a bit ironical that "Scheme" has gone the lexical scoped way
fully and that Common Lisp has said "live and let live". It seems that
in this case Common Lisp has done better ;-)


>
> I propose turning EXEC-PATH-LIST into a fluid. Your scripts
> (if they open the FLUIDS package) can dereference it, rebind it,
> and side-effect it.

Let me ask the other way. What alternatives are there? How can one
share a Variable? If we know alternatives one might know better.

Regards
Friedrich

David Rush

unread,
Mar 12, 2001, 3:51:16 AM3/12/01
to
Olin Shivers <shi...@tokyo.cc.gatech.edu> writes:
> A while back, a design problem surfaced with the EXEC-PATH system
> call in scsh.

...

> The answer is to provide this bit of state as a *fluid* cell.

How widely supported are fluids by other Schemes? (there is no
fluid-let SRFI) This could have a large impact on the porting of Scsh
(well at lest the process language) to other Scheme substrates.

Or is the (long-rumored) PLT port truly dead and Scheme48 the future
forever more?

david rush
--
Coffee should be black as hell, strong as death and sweet as love.
-- Turkish proverb

Tim Bradshaw

unread,
Mar 17, 2001, 10:40:11 AM3/17/01
to
* David Rush wrote:

> How widely supported are fluids by other Schemes? (there is no
> fluid-let SRFI) This could have a large impact on the porting of Scsh
> (well at lest the process language) to other Scheme substrates.

I think the flip side of this would be: how could you do this
correctly *without* fluids? If you can't then fluids are something
you need, and if the implementation doesn't have them that's basically
tough.

I think making it a fluid would be the right thing to do.

David Rush

unread,
Mar 20, 2001, 3:46:14 AM3/20/01
to
Tim Bradshaw <t...@cley.com> writes:
> how could you do this correctly *without* fluids?

By using Autoconf. Anything else is pretty insecure. OTOH, insecure
behavior might actually be the goal (and I am *not* being
sarcastic). The core issue that Olin raised is common in writing
suid/administrative scripts; you want to guarantee that a known
version of a "standard" program gets run. The *right* answer is to
hard-code the path into the script. Autoconf is a fairly effective
mechanism for this.

Another solution would be to extend the process form to support some
kind of in-path construct, where the programmer specifies a list of
directories which should be searched to find the executable. This is
not a scoped solution (in the process form namespace, but Scheme is a
Lisp1 anyway, isn't it?) like using fluids would be, but if you're not
willing to use explicit paths, it has the advantage of being both
portable and correct.

> I think making it a fluid would be the right thing to do.

Well, I disaagree. But not very strongly. ISTM (am I right Olin?) that
Scsh doesn't rely on a lot of extensions to Scheme semantics (and I am
splitting hairs here, I find a significant qualitative difference
between read-string!/partial and fluid-let).

G*d I've made a mess in presenting this. I could try to be more
extensive if anyone thinks there's enough merit to it...

david rush
--
The beginning of wisdom for a [software engineer] is to recognize the
difference between getting a program to work, and getting it right.
-- M A Jackson, 1975

Tim Bradshaw

unread,
Mar 20, 2001, 7:31:38 PM3/20/01
to
* David Rush wrote:

> By using Autoconf. Anything else is pretty insecure. OTOH, insecure
> behavior might actually be the goal (and I am *not* being
> sarcastic). The core issue that Olin raised is common in writing
> suid/administrative scripts; you want to guarantee that a known
> version of a "standard" program gets run. The *right* answer is to
> hard-code the path into the script. Autoconf is a fairly effective
> mechanism for this.

Like Olin said: Imagine a scsh server sitting on a host. People


authenticate to the server and get a connection to a repl thread. You
don't want *my* EXEC-PATH-LIST frobbing to interfere with *your*
thread, right?

Autoconf is a complete non-solution to this problem.

--tim


David Rush

unread,
Mar 21, 2001, 6:44:20 AM3/21/01
to
Tim Bradshaw <t...@cley.com> writes:
> * David Rush wrote:
> > By using Autoconf. Anything else is pretty insecure. OTOH, insecure
> > behavior might actually be the goal (and I am *not* being
> > sarcastic).

> Like Olin said: Imagine a scsh server sitting on a host. People


> authenticate to the server and get a connection to a repl thread. You
> don't want *my* EXEC-PATH-LIST frobbing to interfere with *your*
> thread, right?

Then don't allow EXEC-PATH-LIST frobbing.

> Autoconf is a complete non-solution to this problem.

As *I* said insecure behavior might be a goal, but it's still
insecure. Requiring complete filenames (which is all that I really
advocated in the autoconf solution) is in fact the correct and secure
thing to do. Scsh is so file-system friendly that your hypothetical
Scsh server machine could easily have a path-searching routine built
into it to resolve executables against a user-specified path. Heck,
users who cared could even build such a resolver themselves.

The other solution I proposed, having an in-path construct added to
the process language, is essentially just providing the resolver in a
nice package. In case you haven't gotten it yet, EXEC-PATH-LIST *is*
convenient, but it is also a generally *bad* mechanism. The only
excuse for it is that search paths have widespread use in other
shells, but even then their dangers are well-understood and it is not
used in serious scripting work. Having an explicit resolver in
addition to EXEC-PATH-LIST, is a reasonable compromise, which doesn't
involve fluids.

david rush
--
To get anywhere with programming we must be free to discuss and
improve subjective phenomena. and leave the objective metrics to
resultants such as bug reports.
-- The Programmer's Stone (Alan Carter & Colston Sanger)

Biep @ http://www.biep.org

unread,
Apr 6, 2001, 7:29:28 AM4/6/01
to
"Friedrich Dominicus" <fr...@q-software-solutions.com> wondered in message
news:87vgpfa...@frown.here...

> Let me ask the other way. What alternatives are there?
> How can one share a Variable? If we know alternatives one might know
better.

In general, sharing a variable is done by enclosing the share points within
a common closure.
So. if a variable should be script-specific, it should be (as if it were)
defined in a LET(REC) around the script.
If it should be common to several scripts, it should be (as if it were)
defined in a LET(REC) around all those scripts.
If it should be global, it should be (as if it were) defined in a LET(REC)
wrapped around ALL source code.

The standard example:

(define up 'dummy)
(define down 'dummy)

(let ((counter 0))
(set! up (lambda () (set! counter (+ counter 1)) counter))
(set! down (lambda () (set! counter (- counter 1)) counter)) )

Now up and down share the counter while no-one else can get at it.

--
Biep
Reply via http://www.biep.org


0 new messages