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
300
301
302
303
304
305
306
307
308
309
310
311
312
|
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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>
<p>
If your application has trouble handling unknown
children, consider using an ftrigrd service. (And fix your application!)
</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_t const *</tt>
(<em>deadline</em>) parameter and a <tt>tain_t *</tt> (<em>stamp</em>)
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 <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(&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_t 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_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) ;
</pre>
<p>
<tt>ftrigr_start</tt> starts a session with an ftrigrd service listening on
<em>path</em>. <br />
<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_t structure that must be declared in the stack and
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_t</tt> structures and calling
<tt>ftrigr_startf</tt> (or <tt>ftrigr_start</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(&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 ;
uint16_t id = ftrigr_subscribe (&a, path, re, options, &deadline, &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.
<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>
<tt>ftrigr_subscribe()</tt> returns 0 and sets errno in case of failure, or
a nonzero 16-bit number identifying the subscription in case of success.
</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>
uint16_t list[1] ;
unsigned int n = 1 ;
char trigger ;
list[0] = id ;
// r = ftrigr_wait_and(&a, list, n, &deadline, &stamp) ;
r = ftrigr_wait_or(&a, list, n, &deadline, &stamp, &trigger) ;
</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. <br />
<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>trigger</tt> is set to the character that triggered that
event, i.e. the last character of a sequence that matched the regular
expression <tt>re</tt> used in the subscription.
</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_t const *a)
</pre>
<p>
Returns a file descriptor to select on for reading. Do not
<tt>read()</tt> it though.
</p>
<pre>
int ftrigr_updateb (ftrigr_t *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 if an error
occurs; in case of success, it returns the number of identifiers for
which something happened.
</p>
<p>
When <tt>ftrigr_updateb</tt> returns,
<tt>genalloc_s(uint16_t, &a->list)</tt> points to an array of
<tt>genalloc_len(uint16_t, &a->list)</tt> 16-bit unsigned
integers. Those integers are ids waiting to be passed to
<tt>ftrigr_check</tt> or <tt>ftrigr_checksa</tt>.
The number of ids already acknowledged is stored in
<tt>a->head</tt>, so the first unacknowledged id is
<tt>genalloc_s(uint16_t, &a->list)[a->head]</tt>.
</p>
<pre>
int ftrigr_check (ftrigr_t *a, uint16_t id, char *what)
</pre>
<p>
Checks whether an event happened to <em>id</em>. Use after a
call to <tt>ftrigr_updateb()</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, writes the character that triggered the
latest notification into <em>what</em> and returns the number of
times that an event happened to this identifier since the last
call to <tt>ftrigr_check()</tt>. </li>
</ul>
<pre>
int ftrigr_checksa (ftrigr_t *a, uint16_t id, stralloc *what)
</pre>
<p>
Checks whether an event happened to <em>id</em>. Use after a
call to <tt>ftrigr_update()</tt>, as an alternative to <tt>ftrigr_check()</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, appends one character to the end of the <em>what</em>
<a href="//skarnet.org/software/skalibs/libstddjb/stralloc.html">stralloc</a>
for every time a notification was triggered since the last call
to <tt>ftrigr_check()</tt>. Each character is the one that triggered
a notification. The function then returns 1. </li>
</ul>
<pre>
int ftrigr_ack (ftrigr_t *a, size_t n)
</pre>
<p>
Acknowledges reading <em>n</em> ids from the id list updated by
<tt>ftrigr_updateb</tt>.
</p>
<pre>
int ftrigr_update (ftrigr_t *a)
</pre>
<p>
Acknowledges all the pending ids (i.e. clears the stored id list)
then calls <tt>ftrigr_updateb()</tt>.
</p>
</body>
</html>
|