[Date Prev][Date Next] [Chronological] [Thread] [Top]

Re: LDAP C API: ber_* error handling



Kurt D. Zeilenga writes:
> I feel the choices are:
> 	1) rework error handling such there is a standard
> 	(preferably thread safe) mechanism for obtaining detail
> 	error information from each and every API call, or
> 
> 	2) say it's "implementation specific."

I think the draft should be progressed fairly soon.  Maybe these
suggestions can reduce the error handling issues enough that it can be
done quickly (if people agree with them:-)

* Handling system errors:

Keep that fairly primitive for now, no more than what ISO C errno,
perror and strerror can do (plus a mutex to protect strerror calls in
thread-safe LDAP implementations).

So we can know whether or not to call perror:
Require each LDAP operation sets a flag (in the LDAP* or BerElement*
argument) which says whether or not the resulting errno is known to
describe an error from that operation.  When nonzero, the flag could be
a copy of errno itself.  That's nicer since it protects from later
changes in errno, but would also make it harder to port an LDAP client
to use other implementation-specific error handling.

* Calls with nowhere to put LDAP error codes:

I think there are only 5 or 6 of them:
  ldap_init, ldap_open, ldap_explode_<dn/rdn>, ldap_dn2ufn.
  (Shouldn't ldap_msgfree() return void instead of int?)
It shouldn't be too much work to invent new variants of these.
The other calls have an LDAP* or BerElement* to put errors in, or they
can only have system errors (mostly malloc errors) so we can require
that they'll set errno appropriately if they fail.  Though we might add
ldap wrappers to get their errno/strerror/perror anyway, for
not-quite-ISO-C implementations and thread-safe error handling.

If you don't like that, or it would still take too long time, maybe
it would be easier to let all those calls deposit their errors so that
    ldap_get_option(NULL, LDAP_OPT_ERROR_*, *)
and
    ldap_get_option(NULL, LDAP_OPT_SYSERROR_*, *)
can get them.  Personally I'm not fond of ldap_get_option just to get
errors, but I prefer that to waiting another year while each
implementation goes its own way.


* Thread-safe error codes:

This does not need to mangle the API too badly, I think it can even be
done as an API extension if we can't do it quickly now:

- ldap_set_option( NULL, LDAP_OPT_THREAD_SPECIFIC_ERRORS, LDAP_OPT_ON )

  could cause the library to keep error status and matchedDN from the
  latest LDAP* call (independent of session handle) in a thread-specific
  area, see POSIX pthread_key_create().

  Or that could be done only for the calls that have no LDAP (or
  BerElement) to put their errors in, combined with:

- LDAP *ldap_dup( LDAP *ld, int mode );

    return a new session handle to the same session as the the <ld>
    argument, with the same initial options modified by <mode> except
    [[list of options that should not be copied]].  Returns the new
    handle.  Returns NULL and sets the error status in <ld> at failure.

    To keep errors thread-safe, each thread could use its own handle to
    the same session.  Since any error from ldap_dup() will be stored in
    the original LDAP*, threaded programs should call ldap_dup(ld,mode)
    from the thread which owns <ld>.

  <mode> could be a binary OR of something like these options:

  * LDAP_MODE_THREAD_SAFE: Each handle will be used in a separate
    thread, the library must ensure this is safe.

  * LDAP_MODE_<WRONLY/RDWR/RDONLY/PRIVATE>: Whether the handle will be
    used to write requests to the network and/or read results from the
    network.  Don't know if it should be enforced, but it might be a
    useful hint to the library.

    LDAP_MODE_PRIVATE, if we need it (I hope not), would be enforced:
    it means results would be dispatched to the handle which requested
    them so that even ldap_result(,LDAP_RES_ANY,,,) on a the will only
    see its "own" requests, and other handles won't see those requests.
    But I hope it would be enough to just advise people not to use
    LDAP_RES_ANY in combination with ldap_dup().
    
  The call should fail if <mode> is not supported.

- int ldap_close( LDAP *ld );

    close session handle <ld>.  ldap_unbind(ld) is done if there were no
    other handles to the LDAP session.

- int ldap_forward_message( LDAP *from, LDAPMessage *msg, LDAP *to );

    <msg> which has been fetched from session handle <from> is inserted
    in the message queue to handle <to>, so ldap_result(to, ...) will
    receive it.  Both handles must refer to the same session.
    I'm not sure this call is useful, someone who wants to implement
    something like LDAP_MODE_PRIVATE by hand would want it.

- int ldap_get_option( ld, LDAP_OPT_REFCOUNT, (void *) &count )

    Put the number of handles to <ld>'s session in the ldap_int count.

  It might be simpler for the library if handles are classified as
  "primary" (returned by ldap_init & co) and "secondary" (returned by
  ldap_dup).  Maybe some operations or options should only be possible
  on primary handles, and we'd need
     ldap_get_option(ld, LDAP_OPT_MASTER_HANDLE, &master)
  to return <ld>'s master handle.

- void ldap_begin( void );

  Initialize the LDAP library, before any threaded calls.
  Non-threaded programs do not need this; ldap_init will call it.

- void ldap_end( void );

  Clean up after the LDAP library.  (Some platforms might prefer us to
  call this even when unthreaded, to clean up after a socket library or
  something.)

-- 
Hallvard