S6-FTRIGR(7)

NAME
s6-ftrigr - API for listeners, i.e. programs that want to subscribe to s6-fifodir(7) s and be instantly notified when the proper sequence of events happens

DESCRIPTION
Check the s6/ftrigr.h header for the exact function prototypes.

Make sure your application is not disturbed by children it doesn't know it has. This means paying some attention to the SIGCHLD handler, if any, and to the way you perform waitpid(2) s. The best practice is to use a self-pipe[1] to handle SIGCHLD (as well as other signals the application needs to trap), and to always use wait_nohang() to reap children, simply ignoring pids you don't know.

If your application has trouble handling unknown children, consider using an s6-ftrigrd(8) service. (And fix your application!)

  A programming example
The src/pipe-tools/s6-ftrig-listen1.c and src/supervision/s6-svwait.c files in the s6 package, for instance, illustrate how to use the s6-ftrigr library.

  Synchronous functions with a specified maximum execution time
Synchronous functions take a ‘tain_t const * (deadline)’ parameter and a ‘tain_t * (stamp)’ parameter. Those are pointers to tain_t structures containing absolute times; the former represents a deadline (in most cases, this time will be in the future) and the latter must be an accurate enough timestamp. These structures can be filled using the ‘tain_’ primitives declared in skalibs/tai.h [2].

( “Accurate enough” means that no blocking system call must have been made since the last time stamp was updated ( by ‘tain_now (&stamp)’). It's a good policy to always update stamp right after a (potentially) blocking system call like select(2) returns. And unless the application is extremely CPU-intensive (think calculus for physicists or astronomers) updating stamp more frequently is unnecessary.)

If such a synchronous function still hasn't returned when the deadline occurs, then it will immediately return a failure code and set errno to ETIMEDOUT. It is possible to pass null pointers to the function instead of pointers to tain_t structures, in which case the function will never timeout.

If a timeout occurs, the library does not guarantee proper interprocess communication later on; the application should either die, or at least close the communication channel and open a new one.

If any waiting occurred, the stamp structure is automatically updated by the called function, so it always represents an accurate enough estimation of the current time. This allows the programmer to call several such functions in a sequence without modifying the deadline and stamp parameters: then the whole sequence is bound in execution time.

This is a general safety mechanism implemented in libunixonacid[3]: in interprocess communication, purely synchronous primitives are dangerous because they make the calling process rely on proper behaviour of the called process. Giving synchronous primitives the ability to timeout allows developers to write reliable programs even when interacting with software they have no control over.

  Starting and ending a session

    ftrigr_t a = FTRIGR_ZERO ;
    tain_t deadline, stamp ;
    tain_now(&stamp) ;
    tain_addsec(&deadline, &stamp, 2)
    // char const *path = FTRIGR_IPCPATH ;
    // ftrigr_start(&a, path, &deadline, &stamp) ;
    ftrigr_startf(&a, &deadline, &stamp) ;

ftrigr_start() starts a session with an s6-ftrigrd(8) service listening on path.

ftrigr_startf() starts a session with an s6-ftrigrd(8) process as a child (which is the simplest usage).

a is an ftrigr_t structure that must be declared in the stack and initialized to FTRIGR_ZERO. stamp must be an accurate enough timestamp.

If the session initialization fails, the function returns 0 and errno is set; else the function returns 1.

If the absolute time deadline is reached and the function has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. Only local interprocess communications are involved; unless your system is heavily overloaded, the function should return near-instantly. One or two seconds of delay between stamp and deadline should be enough: if the function takes more than that to return, then there is a problem with the underlying processes.

You can have more than one session open in parallel, by declaring several distinct ftrigr_t structures and calling ftrigr_startf() ( or ftrigr_start()) more than once. However, this is useless, since one single session can handle virtually as many concurrent s6-fifodir(7) s as your application needs.

    ftrigr_end(&a) ;

ftrigr_end() frees all the resources used by the session. The a structure is then reusable for another session.

  Subscribing to an s6-fifodir 7

    char const *path = "/var/lib/myservice/fifodir" ;
    char const *re = "a.*b|c*d" ;
    uint32_t options = 0 ;
    uint16_t id = ftrigr_subscribe (&a, path, re, options, &deadline, &stamp) ;

ftrigr_subscribe() instructs the s6-ftrigrd(8) daemon, related to the open session represented by the a structure, to subscribe to the path s6-fifodir(7), and to notify the application when it receives a series of events that matches the re regexp. options can be 0 or FTRIGR_REPEAT. If it is 0, the daemon will automatically unsubscribe from path once re has been matched by a series of events. If it is FTRIGR_REPEAT, it will remain subscribed until told otherwise.

ftrigr_subscribe() returns 0 and sets errno in case of failure, or a nonzero 16-bit number identifying the subscription in case of success.

ftrigr_subscribe() should return near-instantly, but if deadline is reached, it will return 0 ETIMEDOUT. If ftrigr_subscribe() returns successfully, then the s6-ftrigrd(8) daemon is guaranteed to be listening on path, and events can be sent without the risk of a race condition.

  Synchronously waiting for events

    uint16_t list[1] ;
    unsigned int n = 1 ;
    char trigger ;
    list[0] = id ;
    // r = ftrigr_wait_and(&amp;a, list, n, &amp;deadline, &amp;stamp) ;
    r = ftrigr_wait_or(&amp;a, list, n, &amp;deadline, &amp;stamp, &amp;trigger) ;

ftrigr_wait_and() waits for all the n s6-fifodir(7)s whose ids are listed in list to receive an event. It returns -1 in case of error or timeout, or a non-negative integer in case of success.

ftrigr_wait_or() waits for one of the n s6-fifodir(7)s whose ids are listed in list to receive an event. It returns -1 in case of error or timeout; if it succeeds, the return value is the position in list, starting at 0, of the identifier that received an event; and trigger is set to the character that triggered that event, i.e. the last character of a sequence that matched the regular expression re used in the subscription.

  Asynchronously waiting for events
(From now on, the functions are listed with their prototypes instead of usage examples.)

int ftrigr_fd(ftrigr_t const *a)

    Returns a file descriptor to select on for reading. Do not read(2) it though.

int ftrigr_updateb(ftrigr_t *a)

    Call this function whenever the fd checks readability: it will update a' s internal structures with information from the s6-ftrigrd(8) daemon. It returns -1 if an error occurs; in case of success, it returns the number of identifiers for which something happened.

    When ftrigr_updateb() returns, ‘genalloc_s(uint16_t, &a->list)’ points to an array of ‘genalloc_len(uint16_t, &a->list)’ 16-bit unsigned integers. Those integers are ids waiting to be passed to ftrigr_check() or ftrigr_checksa(). The number of ids already acknowledged is stored in ‘a->head’, so the first unacknowledged id is ‘genalloc_s(uint16_t, &a->list)[a->head]’.

int ftrigr_check(ftrigr_t *a, uint16_t id, char *what)

    Checks whether an event happened to id. Use after a call to ftrigr_updateb().

    •   If an error occurred, returns -1 and sets errno. The error number may have been transmitted from s6-ftrigrd(8).

    •   If no notification happened yet, returns 0.

    •   If something happened, writes the character that triggered the latest notification into what and returns the number of times that an event happened to this identifier since the last call to ftrigr_check().

int ftrigr_checksa(ftrigr_t *a, uint16_t id, stralloc *what)

    Checks whether an event happened to id. Use after a call to ftrigr_update(), as an alternative to ftrigr_check().

    •   If an error occurred, returns -1 and sets errno. The error number may have been transmitted from s6-ftrigrd(8).

    •   If no notification happened yet, returns 0.

    •   If something happened, appends one character to the end of the what stralloc[4] for every time a notification was triggered since the last call to ftrigr_check(). Each character is the one that triggered a notification. The function then returns 1.

int ftrigr_ack(ftrigr_t *a, size_t n)

    Acknowledges reading n ids from the id list updated by ftrigr_updateb().

int ftrigr_update(ftrigr_t *a)

    Acknowledges all the pending ids (i.e. clears the stored id list) then calls ftrigr_updateb().

SEE ALSO
s6-accessrules(7), s6-fdholder(7), s6-ftrigw(7), s6-libs6(7), s6-s6lock(7)

[1] https://skarnet.org/software/skalibs/libstddjb/selfpipe.html

[2] https://skarnet.org/software/skalibs/libstddjb/tai.html

[3] https://skarnet.org/software/skalibs/libunixonacid/

[4] https://skarnet.org/software/skalibs/libstddjb/stralloc.html

This man page is ported from the authoritative documentation at: https://skarnet.org/software/s6/libs6/ftrigr.html

AUTHORS
Laurent Bercot Alexis <flexibeast@gmail.com> (man page port)