Vitalnix : User Management Suite
  manual v1.90.8.21

Name

backend_api - The back-end module API, how it works, how to write a module

Description

Since there is a great variety of user databases, the Unified Account Database provides a generic API to any application. Some user databases are for example the Shadow Password System (/etc/passwd and friends). Another could be the Samba userdb in /var/lib/samba, or OpenLDAP (libldap).

Depending on how the user database is organized, you might want, or even need to read it into memory. Be sure to keep a lock on the database and/or files when writing, if necessary to not run into race conditions.

One should choose wise how to access local files. (If that applies to the module, i.e. accdb_shadow.) Using libc's putpwent() or similar is a pain due to re-entrancy problems, race conditions and not being widely available. Doing the I/O itself (and even caching if the module author wants to) can result in a faster execution, and allows for way more control. "Pipe modules", which just transfer data, as it would be with a LDAP or MySQL database, do not need to do any direct I/O, since the underlying (LDAP/MySQL) will handle that.

Applications that wish to use userdb modules might want to use accdb_load() from libaccdb for simplicity. However, they are not forced to do so, but then they would have to lookup the module symbols themselves.

Synopsis

First, include <accdb.h> to get at the accdb_[ug]entry structs. The module needs to provide following functions to the outside:

#include <accdb.h>

int q_open(void **state);
int q_usertrav(void *state, struct accdb_user *result);
int q_userinfo(void *state, struct accdb_user *req, struct accdb_user *dest, size_t s);
int q_useradd(void *state, struct accdb_user *user);
int q_usermod(void *state, struct accdb_user *user, struct accdb_group *mask);
int q_userdel(void *state, struct accdb_user *user);
int q_grouptrav(void *state, struct accdb_group *result);
int q_groupinfo(void *state, struct accdb_group *req, struct accdb_user *dest, size_t s);
int q_groupadd(void *state, struct accdb_group *user);
int q_groupmod(void *state, struct accdb_group *user, struct accdb_group *mask);
int q_groupdel(void *state, struct accdb_group *user);
void q_close(void *state);
int p_sysctl(unsigned int req, ...);

Actually none of these functions are mandatory, libaccdb will accept NULL pointers from dlsym() too, however, then you will run into problems while dereferencing a NULL pointer. If you do not provide q_open, libaccdb will refuse to load the module. See doc/12_libaccdb.html for more details.

If you include <accdb_int.h>, you also get the prototypes for free, besides some macros used for naming the module (see below at "Module Info").

[1] They are all prefixed so they do not clash with system function names.

Getting opened

q_open() is passed a void ** pointer. You can allocate *state to point to a user-defined struct. That is, a pointer to a private data area you might need. That struct may hold anything you need. It could, for example, contain FILE * pointers, or ints to file descriptors (fds, sockets) connected to the user database. Using static variables is allround bad (for re-entrancy too), do not stick to it.

The module may or may not allow opening the user database twice (i.e. if the module is opened by different applications), you must handle that then. One, locking files, and two, checking for the lock is the usual thing for a module to be opened only once at a time.

Upon success, return >0, otherwise return 0 (and possibly set errno), or even return -errno and set errno to signalize a hard error (i.e. ENOMEM due to malloc()).

Traversing the user and group lists

The accdb_[ug]entry structs are as follows:

struct accdb_user {
  // passwd part
  char *lname;
  long uid, gid;
  char *group, *gecos, *home, *shell;

  // shadow part
  char *pass, *pass_cryp;
  long last_change, keep_min, keep_max, warn_age, expire, inactive;

  // try to avoid using it
  void *_private;
};

struct accdb_group {
  long gid;
  char *name;

  // try to avoid using it
  void *_private;
};

Analogous to getpwent(), just a bit more organized, is the q_usertrav() function. It takes the usual state pointer and a struct accdb_user * pointer. When calling q_usertrav(), it takes the next user found in the database and fills in the struct.

The data put into the struct also needs to be in local ending. In most cases this will be ISO-8859-1 "Latin-1". Migration to UTF-8 is planned in libaccdb and its tools, but it could impact your old programs. (v1.90.6.34). If you want to support UTF8, please put corresponding code pieces into a #define UTF8 rule.

All the char * fields in the structs shall point to allocated memory (or memory available throughout the program), so do not use local variables or alloca(). strdup() or similar might help you. Those strings may not be changed by external applications in any way, they are meant to be read-only. That way, these strings can even be reused anywhere.

For each successful returned user via q_usertrav(), the function shall return >0 (usually 1 suffices). The order of users is of no importance; if necessary, the application itself will need to sort it. If the end of the list is reached, return 0, or, if an error occurred, set errno and return -errno. When the end of the list is reached, q_usertrav() can be called with the result parameter set to NULL to reset the list traverser.

Similar applies to the group traversing function q_grouptrav().

The traversion pointer is not static but stored in the state. As of libHX-20031029, continuing traversal after modifying a libHX-Btree does not work, thus you will have to rewind first.
UPDATE: libHX-20031126 traversal after modifying a Btree works.
(XXX: remove notice in future)

Specifically gain info about a user or group

q_userinfo() walks down the list of users searching for matching users. The contents of req are compared with every user in the list. If a value in req is -1 or NULL (depending on the member type and meaning), it is ignored and not included in the comparison, so you could do i.e. the following searches:

any user with UID 37007 (req->lname == NULL && req->uid == 37007),
user "jengelh", any UID (strcmp(req->lname, "jengelh") == 0 && req->uid == -1)
user "jengelh" with UID 37007 (strcmp(req->lname, "jengelh") == 0 && req->uid == 37007)

q_userinfo() has three different operation cases. The first is if dest is not NULL, in which case at most s users are placed into dest, and the number of users stored (can never be >s) is returned. Case two is that dest is NULL and s is 0, where 1 is returned if any match is found. Case three, dest is NULL and s is 1, no users are copied, but the number of matches will be returned.

Return >0 for success (number of users found), 0 for user not found, or -errno (and set errno) for an error.

Again, the complement q_groupinfo() behaves just as like.

q_userinfo() and q_groupinfo() do not disturb the traversion with q_usertrav() / q_grouptrav(). They use their own (local) traversion pointer.

Adding a user

q_useradd creates a new user. EINVAL -- u->lname was NULL ENOENT -- group not found,

1.90.6.52: XXX

Module info

Beautify the module by using the macros MODULE_NAME(string), MODLUE_DESC(string) and/or MODULE_INFO(string). This is not mandatory, and applications must handle this situation if ((struct accdb_module *)m)->desc is NULL.

Module control interface

int p_sysctl(unsigned int req, ...)

The back-end can be controlled via the p_sysctl() call. There are some requests defined in accdb.h; General Sysctl()s get a number between 0x1 and 0xFF, Extension Sysctl()s between 0x100 and 0xBFFF and back-end specific ones from 0xC000 (mostly debugging purposes only).

p_sysctl(ACCDB_ADDFLAGS:0x1, state, flags): will add the provided flags to those present in the back-end's state.

p_sysctl(ACCDB_DELFLAGS:0x2, state, flags): will remove the provided flags from the back-end's state.

p_sysctl(ACCDB_FLUSHDB:0x3, state): causes any changes to be committed to the underlying medium.

p_sysctl(ACCDB_NEXTUID_SYS:0x100, state): return the next free auto-UID below UID_MIN.

p_sysctl(ACCDB_NEXTUID:0x101, state): return the next free auto-UID within UID_MIN and UID_MAX.

p_sysctl(ACCDB_NEXTGID_SYS:0x102, state): return the next free auto-GID below GID_MIN.

p_sysctl(ACCDB_NEXTGID:0x103, state): return the next free auto-GID within GID_MIN and GID_MAX.


November 29 2003