bcnm
Software
skarnet.org

The wpactrl library interface

wpactrl is a library designed to interface a client with the wpa_supplicant program, in a smaller, cleaner, more user-friendly and more packager-friendly way than the wpa_ctrl interface that comes with the wpa_supplicant distribution.

Compiling

Linking

Programming

The bcnm/wpactrl.h header is the reference for the exact function prototypes.

General usage

libwpactrl stores its information in a wpactrl_t structure. Such a structure must be allocated (can be declared in the stack) and initialized to WPACTRL_ZERO before use. The address of that wpactrl_t structure is then used as a handle and given as an argument to all the wpactrl_* function calls.

Connections

A wpactrl_t instance represents two connections to a wpa_supplicant program: an attached one and a detached one. For proper operation, it is important to regularly read the data from the attached connection. This is achieved by calling the wpactrl_update() function whenever data is available on the attached connection; this is notified by the connection's fd becoming readable. The attached connection's fd can be obtained via the wpactrl_fd() function. So, proper usage of a wpactrl_t instance involves an asynchronous event loop.

Synchronous functions

The bulk of libwpactrl functions takes an extra stamp argument at the end, of type tain_t. This means they are synchronous function calls, and the extra argument is there to ensure those calls do not block forever.

stamp must be first initialized to an accurate enough approximation of the current time, for instance via skalibs' tain_now() function; it will then be automatically updated by the libwpactrl function calls to always contain (an accurate enough approximation of) the current time.

skalibs can keep track of the timestamp for you, in the global STAMP variable. All libwpactrl functions taking a stamp argument also have a version with a name ending in _g, that does not take it and instead assumes the STAMP variable always contains (an accurate enough approximation of) the current time.

Those synchronous function calls normally return almost instantly: there should be no blocking code path between the function call and its return. Nevertheless, since they involve communication with a complex wpa_supplicant process, it's impossible to guarantee that they will never block, so the use of the stamp argument, plus a timeout given at wpactrl_start time, ensures there is a cap on the amount of time they block.

Starting and stopping a session

int wpactrl_start (wpactrl_t *a, char const *path, unsigned int timeout, tain_t *stamp)
Starts a session with a wpa_supplicant instance listening on a Unix socket at path. a is a handle that must be initialized to WPACTRL_ZERO before the call to wpactrl_start, and that must then be passed to every wpactrl_* call in the session. The function returns 1 if it succeeds, or 0 (and sets errno) if it fails. The timeout argument is interpreted as milliseconds: it sets the number of milliseconds for which every subsequent synchronous call to wpa_supplicant in the current session will be willing to wait. If a call to wpa_supplicant takes longer than timeout milliseconds, the call will immediately be aborted.

int wpactrl_end (wpactrl_t *a)
Ends the session, freeing all used resources. It is important to use this function even if your process exits right away, because wpactrl_end() will also delete entries in the filesystem.

Low-level command sending

ssize_t wpactrl_query (wpactrl_t *a, char const *q, char *ans, size_t anslen, tain_t *stamp)
Sends the query q to the connected instance of wpa_supplicant, and reads its answer into the buffer pointed to by ans. Returns -1 in case of failure, or the number of bytes of the answer in case of success. Returns -1 with errno set to EMSGSIZE if the answer is bigger than anslen bytes.

int wpactrl_querysa (wpactrl_t *a, char const *q, stralloc *sa, tain_t *stamp)
Sends the query q to the connected instance of wpa_supplicant, and reads its answer into the stralloc pointed to by sa. Returns 1 if it succeeds and 0 if it fails.

wparesponse_t wpactrl_command (wpactrl_t *a, char const *q, tain_t *stamp)
Sends the command q to the connected instance of wpa_supplicant, and returns its answer under the form of a wparesponse_t, which is an enumeration defined in the bcnm/wpactrl.h header. This function is meant to be used with commands returning a well-known value, such as RECONFIGURE (returning OK or FAIL) or PING (returning PONG). The wparesponse_t enumeration type lists all the possible values for the function's return code.

Reading from the attached (asynchronous) connection

int wpactrl_update (wpactrl_t *a)
Reads unsolicited messages from wpa_supplicant. If the messages are whitelisted, it keeps them, otherwise it discards them. The function returns the number of messages that have been read, or -1 in case of failure. A positive number does not mean that all pending messages have been read: there is a cap on the number of messages that can be consecutively read, to prevent a spamming wpa_supplicant from monopolizing your program.

char *wpactrl_msg (wpactrl_t *a)
Returns a pointer to the first unsolicited message from wpa_supplicant that has been read by wpactrl_update() but has not been acknowledged yet. If there's no such message, returns NULL.

void wpactrl_ackmsg (wpactrl_t *a)
Acknowledges reading of one unsolicited message from wpa_supplicant. The next invocation of wpactrl_msg() will point to the next one.

int wpactrl_filter_add (wpactrl_t *a, char const *prefix)
Adds prefix to the whitelist. Unsolicited messages from wpa_supplicant will be stored and made available to the application if they start with <priority>prefix, priority being a single nonzero digit. If the filter is activated (which is the default), then only messages matching prefixes registered via wpactrl_filter_add() will be stored, and all other messages will be discarded. The function returns 1 if it succeeds and 0 if it fails.

void wpactrl_filter_remove (wpactrl_t *a, char const *prefix)
Removes prefix from the whitelist.

void wpactrl_filter_activate (wpactrl_t *a)
Activates the message filter. Unsolicited messages from wpa_supplicant will be discarded unless they are explicitly whitelisted by a call to wpactrl_filter_add(). This is the default.

void wpactrl_filter_deactivate (wpactrl_t *a)
Dectivates the message filter. All the unsolicited messages from wpa_supplicant will be stored and made available to the application.

int wpactrl_filter_match (wpactrl_t const *a, char const *s, size_t len)
Returns 1 if the string s of size len matches one of the registered filters, and 0 otherwise.

Helper functions for parsing answers from wpa_supplicant

size_t wpactrl_bssid_scan (char const *s, char *bssid)
Parses a BSSID of the form a:b:c:d:e:f in string s and writes it as an array of 6 bytes pointed to by bssid. The string "any" is specifically recognized and yields a bssid of 6 zero bytes. The function returns the number of characters eaten in s, or 0 if it fails to recognize a BSSID.

size_t wpactrl_flags_scan (char const *s, stralloc *sa)
Parses a wpa_supplicant "flags" field in the string s and appends them to the stralloc pointed to by sa. The flags are written without their surrounding square brackets, and every flag is terminated by a null byte.

unsigned int wpactrl_env_parse (char *s, size_t len)
Replaces newlines with null bytes in the string s of length len. Returns the number of replaced newlines.

int wpactrl_scan_parse (char const *s, size_t len, genalloc *ga, stralloc *storage)
Parses the string s of length len, expecting it to be wpa_supplicant's response to a SCAN_RESULTS command. The result is a series of wpactrl_scanres_t structures, appended to the genalloc pointed to by ga, and variable length data is appended to the stralloc pointed to by storage. The ssid_start and flags_start fields of a wpactrl_scanres_t are indices pointing into the storage→s string.

int wpactrl_networks_parse (char const *s, size_t len, genalloc *ga, stralloc *storage)
Parses the string s of length len, expecting it to be wpa_supplicant's response to a LIST_NETWORKS command. The result is a series of wpactrl_networks_t structures, appended to the genalloc pointed to by ga, and variable length data is appended to the stralloc pointed to by storage. The ssid_start and flags_start fields of a wpactrl_networks_t are indices pointing into the storage→s string.

void wpactrl_xchg_cbres_free (wpactrl_xchg_cbres_t *res)
Frees the heap memory used by the object pointed to by res.

User functions for common calls to wpa_supplicant

int wpactrl_addnetwork (wpactrl_t *a, uint32_t *id, tain_t *stamp)
Tells wpa_supplicant to create a new network. If it fails, returns 0. If it succeeds, stores the new network id in *id and returns 1.

wparesponse_t wpactrl_removenetwork (wpactrl_t *a, uint32_t id, tain_t *stamp)
Tells wpa_supplicant to remove the network with id id. Returns the response code of wpa_supplicant: WPA_OK on success, WPA_FAIL or something else on failure.

int wpactrl_findnetwork (wpactrl_t *a, char const *ssid, uint32_t *id, tain_t *stamp)
Finds the network id (as seen by wpa_supplicant) of the network with ssid ssid. Stores it into *id if found, and returns 1. Returns 0 if not found; returns -1 (and sets errno) if an error occurs.

wparesponse_t wpactrl_setnetworkoption (wpactrl_t *a, uint32_t id, char const *var, char const *val, tain_t *stamp)
Sets parameter var to value val for network id. Returns the response code of wpa_supplicant, most likely WPA_OK or WPA_FAIL.

wparesponse_t wpactrl_selectnetwork (wpactrl_t *a, uint32_t id, tain_t *stamp)
Selects network id to associate with. Returns the response code of wpa_supplicant, most likely WPA_OK or WPA_FAIL.

int wpactrl_associate (wpactrl_t *, char const *ssid, char const *psk, tain_t *stamp)
Tells wpa_supplicant to associate with the wifi network having the ssid ssid, creating it if it's not already known by wpa_supplicant. If psk is NULL, the network will be assumed open and authentication will use a NONE protocol. If psk is not NULL, the network authentication will be assumed using WPA-PSK or WPA2-PSK, and psk will be sent as pre-shared key. The function returns 1 on success, or 0 if something went wrong.

int wpactrl_startscan (wpactrl_t *a, wpactrl_xchg_t *dt, wpactrl_xchg_cbres_t *res, tain_t const *limit, tain_t *stamp)
Asks wpa_supplicant to start a scan. Sets up the wpactrl_xchg_t structure pointed to by dt so it can be used in an asynchronous event loop to check for availability of the scan results (see below). limit is an absolute deadline after which the scan should be considered failed: if the current time goes over limit, then wpactrl_xchg_timeout() will report a timeout on item. But if wpactrl_xchg_event() reports that an event occurs on item, instead, the results will be available in the wpactrl_xchg_cbres_t structure pointed to by res: res→parsed will be a genalloc made of wpactrl_scanres_t objects, constructed by the wpactrl_scan_parse() function, and res→storage will be the associated storage. wpactrl_startscan() returns 0 (and sets errno) if an error occurs, and 1 if the scan is properly started.

Functions to use within an asynchronous event loop

int wpactrl_xchg_init (wpactrl_xchg_t *dt, wpactrl_xchgitem_t const *tab, unsigned int tablen, tain_t const *limit, void *aux)
Initializes the wpactrl_xchg_t structure pointed to by dt. Returns 0 on failure and 1 on success.

A wpactrl_xchg_t contains the state for an asynchronous call to wpa_supplicant (i.e. a command has been sent and we're now waiting on reception of an event on the attached interface). It is initialized with the tab, n and aux values.

aux is a user-provided pointer used to pass external data to the function callbacks defined in tab.

tab points to tablen caller-provided objects of type wpactrl_xchgitem_t. This type is a struct containing the following members:

The *cb function must return 0 (and set errno) if it fails, or a positive integer if it succeeds. The objects in tab will be used sequentially: first a message with dt→tab[0].filter will be waited for, then *dt→tab[0].cb will be run; if it succeeds, a message with dt→tab[1].filter will be waited for, and so on. The last function, *dt→tab[tablen-1].cb, should write the final result of the whole to a place accessible by the user; this is one of the uses for the aux pointer.

limit is a deadline: an absolute date after which the whole series of exchanges with wpa_supplicant will stop and be considered failed, i.e. iopause will report a timeout and wpactrl_xchg_timeout() called on dt will return 1.

int wpactrl_xchg_start (wpactrl_t *a, wpactrl_xchg_t *dt)
Starts the exchange defined in the object pointed to by dt, with the wpa_supplicant instance defined by the handle a. Returns 1 if it succeeds and 0 if it fails.

void wpactrl_xchg_computedeadline (wpactrl_xchg_t const *dt, tain_t *deadline)
Updates the deadline pointed to by deadline, destined to be used in the next iopause invocation, with the one contained in *dt. Namely: if the deadline defined by *dt is earlier than *deadline, replaces the latter with the former.

int wpactrl_xchg_timeout (wpactrl_t *a, wpactrl_xchg_t *dt, tain_t const *stamp)
To be called after an iopause invocation that returned 0. Tests whether the exchange defined by dt has timed out. Returns 1 (and cleans up the relevant filters in a if it is the case, and 0 otherwise. stamp must point to the current time.

int wpactrl_xchg_event (wpactrl_t *a, wpactrl_xchg_t *dt, tain_t *stamp)
To be called after an iopause invocation that returned a positive number, and after a wpactrl_update(a) invocation. Advances the exchange described in *dt, if applicable: if a message arrived that matches the current filter set up by *dt, executes the corresponding callback, then sets up the next filter. stamp must point to the current time.

The function returns a negative number if an error occurred and the exchange needs to be cancelled and freed; 0 if the exchange isn't over yet; and a positive number if the exchange completed successfully. Namely:

A working example

The provided bcnm-wpactrl-scan.c file is an example on how to program with skalibs and libwpactrl. It connects to a wpa_supplicant instance (it takes the path to the Unix socket to wpa_supplicant as an argument), requests a scan, waits for the scan results with a timeout of 10 seconds, and prints the results as is on its standard output.