aboutsummaryrefslogtreecommitdiffstats
path: root/doc/libs6/ftrigr.html
blob: 2e2682e783e6dbc8e81045754ee9dcd475d7cfcc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="color-scheme" content="dark light" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-Language" content="en" />
    <title>s6: the ftrigr library interface</title>
    <meta name="Description" content="s6: the ftrigr library interface" />
    <meta name="Keywords" content="s6 ftrig notification subscriber listener libftrigr ftrigr library interface" />
    <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
  </head>
<body>

<p>
<a href="index.html">libs6</a><br />
<a href="../">s6</a><br />
<a href="//skarnet.org/software/">Software</a><br />
<a href="//skarnet.org/">skarnet.org</a>
</p>

<h1> The <tt>ftrigr</tt> library interface </h1>

<p>
 The <tt>ftrigr</tt> library provides an API for listeners, i.e.
programs that want to subscribe to fifodirs and be instantly
notified when the proper sequence of events happens.
</p>

<h2> Programming </h2>

<p>
 Check the <tt>s6/ftrigr.h</tt> header for the
exact function prototypes.
</p>

<p>
 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 <tt>waitpid()</tt>s. The best
practice is to use a
<a href="//skarnet.org/software/skalibs/libstddjb/selfpipe.html">self-pipe</a>
to handle SIGCHLD (as well as other signals the application needs to trap),
and to <em>always</em> use <tt>wait_nohang()</tt> to reap children,
simply ignoring pids you don't know.
</p>

<h3> A programming example </h3>

<p>
 The <tt>src/pipe-tools/s6-ftrig-listen1.c</tt> and
<tt>src/supervision/s6-svwait.c</tt> files in the s6 package,
for instance, illustrate how to use the ftrigr library.
</p>

<a name="synctimed">
<h3> Synchronous functions with a specified maximum execution time </h3>
</a>

<ul>
 <li> Synchronous functions take a <tt>tain const *</tt>
(<em>deadline</em>) parameter and a <tt>tain *</tt> (<em>stamp</em>)
parameter. Those are pointers to tain 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 <tt>tain_</tt> primitives declared in
<a href="//skarnet.org/software/skalibs/libstddjb/tai.html">skalibs/tai.h</a>. </li>
 <li> ("Accurate enough" means that <strong>no blocking system call must have
been made</strong> since the last time <em>stamp</em> was updated (by
<tt>tain_now(&amp;stamp)</tt>). It's a good policy to always update
<em>stamp</em> right after a (potentially) blocking system call like
<tt>select()</tt> returns. And unless the application is extremely CPU-intensive
(think calculus for physicists or astronomers) updating <em>stamp</em> more
frequently is unnecessary.) </li>
 <li> 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 structures, in which case the function will never timeout. </li>
 <li> 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. </li>
 <li> If any waiting occurred, the <em>stamp</em> 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 <em>deadline</em> and <em>stamp</em>
parameters: then the whole sequence is bound in execution time. </li>
 <li> This is a general safety mechanism implemented in
<a href="//skarnet.org/software/skalibs/libunixonacid/">libunixonacid</a>:
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 on. </li>
</ul>


<h3> Starting and ending a session </h3>

<pre>
ftrigr a = FTRIGR_ZERO ;
tain deadline, stamp ;

tain_now(&amp;stamp) ;
tain_addsec(&amp;deadline, &amp;stamp, 2)

ftrigr_startf(&amp;a, &amp;deadline, &amp;stamp) ;
</pre>

<p>
<tt>ftrigr_startf()</tt> starts a session with an ftrigrd process as a child
(which is the simplest usage). <br />
<tt>a</tt> is an ftrigr structure that can be declared in the stack and
must be initialized to FTRIGR_ZERO.
<tt>stamp</tt> must be an accurate enough timestamp. <br />
If the session initialization fails, the function returns 0 and errno is set;
else the function returns 1.
</p>

<p>
If the absolute time <tt>deadline</tt> 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 <tt>stamp</tt> and <tt>deadline</tt> should be
enough: if the function takes more than that to return, then there is a
problem with the underlying processes.
</p>

<p>
 You can have more than one session open in parallel, by declaring
several distinct <tt>ftrigr</tt> structures and calling
<tt>ftrigr_startf()</tt> more than once.
However, this is useless, since one single session can handle
virtually as many concurrent fifodirs as your application needs.
</p>

<pre>
ftrigr_end(&amp;a) ;
</pre>

<p>
<tt>ftrigr_end()</tt> frees all the resources used by the session. The
<tt>a</tt> structure is then reusable for another session.
</p>

<h3> Subscribing to a fifodir </h3>

<pre>
char const *path = "/var/lib/myservice/fifodir" ;
char const *re = "a.*b|c*d" ;
uint32_t options = 0 ;
uint32_t timeout = 60000 ;
uint32_t id ;

int r = ftrigr_subscribe(&amp;a, &amp;id, options, timeout, path, re, &amp;deadline, &amp;stamp) ;
</pre>

<p>
<tt>ftrigr_subscribe()</tt> instructs the
<a href="s6-ftrigrd.html">s6-ftrigrd daemon</a>, related to the open
session represented by the <tt>a</tt> structure, to subscribe to the
<tt>path</tt> fifodir, and to notify the application when it receives
a series of events that matches the <tt>re</tt> regexp. </p>

<p>
<tt>options</tt> can be 0 or FTRIGR_REPEAT. If it is 0, the daemon will
automatically unsubscribe from <tt>path</tt> once <tt>re</tt> has been
matched by a series of events. If it is FTRIGR_REPEAT, it will remain
subscribed until told otherwise.
</p>

<p>
 If <tt>timeout</tt> is nonzero, it represents a number of milliseconds;
after this delay, the daemon will automatically unsubscribe from
<tt>path</tt> and report an ETIMEDOUT error to the client. It is not
advised to use a nonzero timeout along with the FTRIGR_REPEAT option.
</p>

<p>
 If it fails, <tt>ftrigr_subscribe()</tt> returns 0 and sets errno. If
it succeeds, it returns 1 and stores a number identifying the subscription
into <tt>id</tt>.
</p>

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

<h3> Synchronously waiting for events </h3>

<pre>
uint32_t list[1] = { id } ;
unsigned int n = 1 ;
ftrigr_string fs ;

// 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;fs) ;
</pre>

<p>
 <tt>ftrigr_wait_and()</tt> waits for <em>all</em> the <tt>n</tt> fifodirs
whose ids are listed in <tt>list</tt> to receive an event. It returns -1
in case of error or timeout, or a non-negative integer in case of success.
</p>

<p>
 <tt>ftrigr_wait_or()</tt> waits for <em>one</em> of the <tt>n</tt> fifodirs
whose ids are listed in <tt>list</tt> to receive an event. It returns -1
in case of error or timeout; if it succeeds, the return value is the
position in <tt>list</tt>, starting at 0, of the identifier that received
an event; and <tt>fs</tt> is the list of events that were received
since the subscription and matched the <tt>re</tt> regular expression.
<tt>fs.s</tt> is a <tt>char *</tt> pointing to the (not null-terminated)
string of events, and <tt>fs.len</tt> is its length.
</p>

<h3> Asynchronously waiting for events </h3>

<p>
<em> (from now on, the functions are listed with their prototypes instead
of usage examples.) </em>
</p>

<pre>
int ftrigr_fd (ftrigr const *a)
</pre>

<p>
 Returns a file descriptor to select on for reading. Do not
<tt>read()</tt> it though.
</p>

<pre>
int ftrigr_update (ftrigr *a)
</pre>

<p>
 Call this function whenever the fd checks readability: it will
update <em>a</em>'s internal structures with information from the
<a href="s6-ftrigrd.html">s6-ftrigrd</a> daemon. It returns -1 (and sets
errno) if an error occurs, 0 if there were no events, and 1 if events
were received.
</p>

<pre>
int ftrigr_peek (ftrigr *a, uint32_t id, ftrigr_string *fs)
</pre>

<p>
 Checks whether an event happened to <em>id</em>. Use after a
call to <tt>ftrigr_update()</tt>.
</p>

<ul>
 <li> If an error occurred, returns -1 and sets errno. The error
number may have been transmitted from
<a href="s6-ftrigrd.html">s6-ftrigrd</a>. </li>
 <li> If no notification happened yet, returns 0. </li>
 <li> If something happened, returns 1, and <tt>fs</tt> contains
the string of events that were received since the subscription or
since the last call to <tt>ftrigr_ack()</tt> (see below).
<tt>fs-&gt;s</tt> is a pointer to the non-null-terminated string,
and <tt>fs-&gt;len</tt> is its length. </li>
</ul>

<pre>
void ftrigr_ack (ftrigr *a, uint32_t id)
</pre>

<p>
 Resets the stored string of events. The next invocation of
<tt>ftrigr_peek()</tt> will only show new events, if any.
</p>

<pre>
int ftrigr_release (ftrigr *a, uint32_t id)
</pre>

<p>
 Frees the resources used by subscription <tt>id</tt>. Use this
after getting an event from a subscription done without the
FTRIGR_REPEAT flag and reading its results, if you want to
keep the session open and perform more subscriptions.
</p>

<p>
 If subscription <tt>id</tt> was given the FTRIGR_REPEAT flag,
use <tt>ftrigr_unsubscribe()</tt> instead. If you're not going
to perform other subscriptions, <tt>ftrigr_end()</tt> will
free all the resources without you needing to call
<tt>ftrigr_release()</tt> first.
</p>

</body>
</html>