Discussion:
[Larceny-users] RFC: FFI usage for getting process's environment variables
Derick Eddington
2009-04-07 12:38:13 UTC
Permalink
I've implemented SRFI 98: An Interface to Access Environment Variables.
I've used the FFI in order to access the C environ variable and to do my
own transcoding of strings. This is my first time using Larceny's FFI,
so I would like to know if what I've done is correct or if it could be
done better, and I have some other questions (noted in the source code).
Also, attached is a little test program; it works for me.

Also, the document at doc/LarcenyNotes/note7-ffi.html says of ffi/dlsym
"handle can be #f, which means that the symbol will be resolved in the
symbol table of the running program", but that's no longer true; is this
intentional?


(library (srfi :98 os-environment-variables)
(export
get-environment-variable get-environment-variables)
(import
(rnrs base)
(rnrs control)
(rnrs bytevectors)
(rnrs io ports)
(primitives
foreign-procedure #;foreign-variable foreign-null-pointer? sizeof:pointer
%peek-pointer %peek8u void*->address ffi/dlopen ffi/dlsym))

;; TODO: Will the convenient string converters use the native transcoder in
;; the future? So that scheme-str->c-str-bv and c-str-ptr->scheme-str
;; won't be needed.

(define (scheme-str->c-str-bv x)
(let* ((bv (string->bytevector x (native-transcoder)))
(len (bytevector-length bv))
(bv/z (make-bytevector (+ 1 len))))
(bytevector-copy! bv 0 bv/z 0 len)
(bytevector-u8-set! bv/z len 0)
bv/z))

(define (c-str-ptr->scheme-str x)
(let loop ((x x) (a '()))
(let ((b (%peek8u x)))
(if (zero? b)
(bytevector->string (u8-list->bytevector (reverse a))
(native-transcoder))
(loop (+ 1 x) (cons b a))))))

(define getenv
(foreign-procedure "getenv" '(boxed) 'void*))

(define (get-environment-variable name)
(unless (string? name)
(assertion-violation 'get-environment-variable "not a string" name))
(let ((p (getenv (scheme-str->c-str-bv name))))
(and p
(c-str-ptr->scheme-str (void*->address p)))))

;; TODO: Will foreign-variable support a pointer type in the future?
;; Would this be the correct way to use it?
#;(define environ
(foreign-variable "environ" 'void*))

;; TODO: Is (ffi/dlopen "") okay? It works for me.
(define environ
(%peek-pointer (ffi/dlsym (ffi/dlopen "") "environ")))

(define (get-environment-variables)
(define (entry->pair x)
(let* ((s (c-str-ptr->scheme-str x))
(len (string-length s)))
(let loop ((i 0))
(if (< i len)
(if (char=? #\= (string-ref s i))
(cons (substring s 0 i)
(substring s (+ 1 i) len))
(loop (+ 1 i)))
(cons s #F)))))
(let loop ((e environ) (a '()))
(let ((entry (%peek-pointer e)))
(if (foreign-null-pointer? entry)
a
(loop (+ sizeof:pointer e)
(cons (entry->pair entry) a))))))
)
--
: Derick
----------------------------------------------------------------
Felix Klock
2009-04-09 21:47:55 UTC
Permalink
Derick (cc'ing larceny-users)-

Thanks for sharing the code; always easier to work with concrete
examples.
Post by Derick Eddington
I've implemented SRFI 98: An Interface to Access Environment
Variables.
I've used the FFI in order to access the C environ variable and to do my
own transcoding of strings. This is my first time using Larceny's FFI,
so I would like to know if what I've done is correct or if it could be
done better, and I have some other questions (noted in the source code).
Also, attached is a little test program; it works for me.
See my note below about your use of ffi/dlopen. I do not know about
other problems, but I agree that the transcoding is an issue that our
current version of getenv does not handle, mmm, very well.
Post by Derick Eddington
Also, the document at doc/LarcenyNotes/note7-ffi.html says of ffi/
dlsym
"handle can be #f, which means that the symbol will be resolved in the
symbol table of the running program", but that's no longer true; is this
intentional?
Hmm. At one point Jesse and I discussed whether supporting this
semantics made sense [1]. We never deliberately removed support for
the semantics described above; rather, this feature seems like it has
never been supported [2].

I suspect we do need to add support for this using an interface that
is consistent between our target platforms for native Larceny. The
problem is determining what is the least common denominator we can
expect to support. I'll see what my copy of APUE [3] says about this
once I am home tonight.
Post by Derick Eddington
;; TODO: Will the convenient string converters use the native
transcoder in
;; the future? So that scheme-str->c-str-bv and c-str-ptr-
Post by Derick Eddington
scheme-str
;; won't be needed.
This seems like a reasonable thing to add, in some fashion.
Post by Derick Eddington
;; TODO: Will foreign-variable support a pointer type in the future?
;; Would this be the correct way to use it?
It seems like it would be a natural consequence of generalizing
foreign-variable to handle other ffi-attribute descriptors. On the
other hand, I'm wary of trying to continue supporting foreign-variable
as it currently stands.

If one provides the handle for the dynamic library explicitly, then
there should be no worry about name collisions between the loaded
libraries. I do not know why Lars decided to hide this issue in the
lowest layer of the Ffi; I suppose its convenient for quick hacks, but
I would not want to rely on the absence of conflicts in a library as
important as SRFI 98. (Am I being silly?)

I usually expect C library designers to try to assign unique names to
the procedures exported by the dynamically loaded library, which is
why I have not seriously pushed to require a library handle in the FFI
routines, but I honestly do not know what to expect about the names of
global variables exported by most foreign libraries.

Anyway, I suspect your real point is that we should support using the
std-ffi.sch marshallers on global variables in *some* fashion, even if
not via the foreign-variable procedure, and I agree with that.
Post by Derick Eddington
;; TODO: Is (ffi/dlopen "") okay? It works for me.
(ffi/dlopen "") does not work for me on my Mac OS X 10.5.6 machine.
(See also [4] for some old but potentially relevant notes on the
subject.)
----

I'm going to be spend time over the next few days going over the Ffi
for the 0.97 release (both the code and the documentation); I think
trying to get the above problems solved would be a good goal for me to
shoot for. I'll give you more feedback on my progress later, but I
thought I should respond to your comments above immediately.

-Felix

[1] https://trac.ccs.neu.edu/trac/larceny/changeset/3694
[2] https://trac.ccs.neu.edu/trac/larceny/wiki/Ffi/Dlsym
[3] "Advanced Programming in the UNIX(R) Environment", Stevens and
Rago, Addison-Wesley 2005
[4] https://trac.ccs.neu.edu/trac/larceny/wiki/OsdepDynload
Felix Klock
2009-04-09 22:26:10 UTC
Permalink
Post by Felix Klock
Post by Derick Eddington
I've implemented SRFI 98: An Interface to Access Environment
Variables.
I've used the FFI in order to access the C environ variable and to do my
own transcoding of strings. This is my first time using Larceny's FFI,
so I would like to know if what I've done is correct or if it could be
done better, and I have some other questions (noted in the source code).
Also, attached is a little test program; it works for me.
See my note below about your use of ffi/dlopen. I do not know about
other problems, but I agree that the transcoding is an issue that
our current version of getenv does not handle, mmm, very well.
This should actually be really easy to fix, actually. Filed as Ticket
#635.

-Felix
Derick Eddington
2009-04-11 05:35:14 UTC
Permalink
Thank you for the information, Felix. Please do let me/us know. The
high-level user-friendly abstraction Larceny's FFI is striving for is
very appealing.
--
: Derick
----------------------------------------------------------------
Post by Felix Klock
I'll give you more feedback on my progress later, but I
thought I should respond to your comments above immediately.
Loading...