blob: 98dbe6c877eb27efe5a78a73f2663f0c5710f034 [file] [log] [blame]
David Carlier8167f302015-06-01 13:50:06 +02001#include <stdio.h>
2
Willy Tarreau4c7e4b72020-05-27 12:58:42 +02003#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +02004#include <haproxy/arg.h>
Willy Tarreau6be78492020-06-05 00:00:29 +02005#include <haproxy/cfgparse.h>
Willy Tarreau8d366972020-05-27 16:10:29 +02006#include <haproxy/errors.h>
Willy Tarreaub2551052020-06-09 09:07:15 +02007#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +02008#include <haproxy/http.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +02009#include <haproxy/http_ana.h>
Willy Tarreau126ba3a2020-06-04 18:26:43 +020010#include <haproxy/http_fetch.h>
Willy Tarreau87735332020-06-04 09:08:41 +020011#include <haproxy/http_htx.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020012#include <haproxy/htx.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020013#include <haproxy/sample.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020014#include <haproxy/tools.h>
Willy Tarreaubee9dde2016-12-21 21:25:06 +010015#include <dac.h>
16
17static struct {
18 void *atlasimgptr;
19 char *jsonpath;
20 char *cookiename;
21 size_t cookienamelen;
22 da_atlas_t atlas;
23 da_evidence_id_t useragentid;
24 da_severity_t loglevel;
25 char separator;
26 unsigned char daset:1;
27} global_deviceatlas = {
28 .loglevel = 0,
29 .jsonpath = 0,
30 .cookiename = 0,
31 .cookienamelen = 0,
32 .useragentid = 0,
33 .daset = 0,
34 .separator = '|',
35};
David Carlier8167f302015-06-01 13:50:06 +020036
37static int da_json_file(char **args, int section_type, struct proxy *curpx,
38 struct proxy *defpx, const char *file, int line,
39 char **err)
40{
41 if (*(args[1]) == 0) {
42 memprintf(err, "deviceatlas json file : expects a json path.\n");
43 return -1;
44 }
Willy Tarreaubee9dde2016-12-21 21:25:06 +010045 global_deviceatlas.jsonpath = strdup(args[1]);
David Carlier8167f302015-06-01 13:50:06 +020046 return 0;
47}
48
49static int da_log_level(char **args, int section_type, struct proxy *curpx,
50 struct proxy *defpx, const char *file, int line,
51 char **err)
52{
53 int loglevel;
54 if (*(args[1]) == 0) {
55 memprintf(err, "deviceatlas log level : expects an integer argument.\n");
56 return -1;
57 }
58
59 loglevel = atol(args[1]);
60 if (loglevel < 0 || loglevel > 3) {
61 memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
62 } else {
Willy Tarreaubee9dde2016-12-21 21:25:06 +010063 global_deviceatlas.loglevel = (da_severity_t)loglevel;
David Carlier8167f302015-06-01 13:50:06 +020064 }
65
66 return 0;
67}
68
69static int da_property_separator(char **args, int section_type, struct proxy *curpx,
70 struct proxy *defpx, const char *file, int line,
71 char **err)
72{
73 if (*(args[1]) == 0) {
74 memprintf(err, "deviceatlas property separator : expects a character argument.\n");
75 return -1;
76 }
Willy Tarreaubee9dde2016-12-21 21:25:06 +010077 global_deviceatlas.separator = *args[1];
David Carlier8167f302015-06-01 13:50:06 +020078 return 0;
79}
80
David Carlier608c65a2015-09-25 14:16:30 +010081static int da_properties_cookie(char **args, int section_type, struct proxy *curpx,
82 struct proxy *defpx, const char *file, int line,
83 char **err)
84{
85 if (*(args[1]) == 0) {
86 memprintf(err, "deviceatlas cookie name : expects a string argument.\n");
87 return -1;
88 } else {
Willy Tarreaubee9dde2016-12-21 21:25:06 +010089 global_deviceatlas.cookiename = strdup(args[1]);
David Carlier608c65a2015-09-25 14:16:30 +010090 }
Willy Tarreaubee9dde2016-12-21 21:25:06 +010091 global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
David Carlier608c65a2015-09-25 14:16:30 +010092 return 0;
93}
94
David Carlier8167f302015-06-01 13:50:06 +020095static size_t da_haproxy_read(void *ctx, size_t len, char *buf)
96{
97 return fread(buf, 1, len, ctx);
98}
99
100static da_status_t da_haproxy_seek(void *ctx, off_t off)
101{
102 return fseek(ctx, off, SEEK_SET) != -1 ? DA_OK : DA_SYS;
103}
104
105static void da_haproxy_log(da_severity_t severity, da_status_t status,
106 const char *fmt, va_list args)
107{
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100108 if (global_deviceatlas.loglevel && severity <= global_deviceatlas.loglevel) {
David Carlier8167f302015-06-01 13:50:06 +0200109 char logbuf[256];
110 vsnprintf(logbuf, sizeof(logbuf), fmt, args);
Christopher Faulet767a84b2017-11-24 16:50:31 +0100111 ha_warning("deviceatlas : %s.\n", logbuf);
David Carlier8167f302015-06-01 13:50:06 +0200112 }
113}
114
David Carlier608c65a2015-09-25 14:16:30 +0100115#define DA_COOKIENAME_DEFAULT "DAPROPS"
116
Willy Tarreau876054d2016-12-21 20:39:16 +0100117/*
118 * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*.
119 */
120static int init_deviceatlas(void)
David Carlier8167f302015-06-01 13:50:06 +0200121{
Willy Tarreau876054d2016-12-21 20:39:16 +0100122 int err_code = 0;
123
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100124 if (global_deviceatlas.jsonpath != 0) {
David Carlier8167f302015-06-01 13:50:06 +0200125 FILE *jsonp;
126 da_property_decl_t extraprops[] = {{0, 0}};
127 size_t atlasimglen;
128 da_status_t status;
129
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100130 jsonp = fopen(global_deviceatlas.jsonpath, "r");
David Carlier8167f302015-06-01 13:50:06 +0200131 if (jsonp == 0) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100132 ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
133 global_deviceatlas.jsonpath);
Willy Tarreau876054d2016-12-21 20:39:16 +0100134 err_code |= ERR_ALERT | ERR_FATAL;
David Carlier8167f302015-06-01 13:50:06 +0200135 goto out;
136 }
137
138 da_init();
139 da_seterrorfunc(da_haproxy_log);
140 status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100141 &global_deviceatlas.atlasimgptr, &atlasimglen);
David Carlier8167f302015-06-01 13:50:06 +0200142 fclose(jsonp);
143 if (status != DA_OK) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100144 ha_alert("deviceatlas : '%s' json file is invalid.\n",
145 global_deviceatlas.jsonpath);
Willy Tarreau876054d2016-12-21 20:39:16 +0100146 err_code |= ERR_ALERT | ERR_FATAL;
David Carlier8167f302015-06-01 13:50:06 +0200147 goto out;
148 }
149
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100150 status = da_atlas_open(&global_deviceatlas.atlas, extraprops,
151 global_deviceatlas.atlasimgptr, atlasimglen);
David Carlier8167f302015-06-01 13:50:06 +0200152
153 if (status != DA_OK) {
Christopher Faulet767a84b2017-11-24 16:50:31 +0100154 ha_alert("deviceatlas : data could not be compiled.\n");
Willy Tarreau876054d2016-12-21 20:39:16 +0100155 err_code |= ERR_ALERT | ERR_FATAL;
David Carlier8167f302015-06-01 13:50:06 +0200156 goto out;
157 }
158
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100159 if (global_deviceatlas.cookiename == 0) {
160 global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT);
161 global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename);
David Carlier608c65a2015-09-25 14:16:30 +0100162 }
163
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100164 global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas,
David Carlier8167f302015-06-01 13:50:06 +0200165 "user-agent");
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100166 global_deviceatlas.daset = 1;
David Carlier8167f302015-06-01 13:50:06 +0200167
168 fprintf(stdout, "Deviceatlas module loaded.\n");
169 }
170
171out:
Willy Tarreau876054d2016-12-21 20:39:16 +0100172 return err_code;
David Carlier8167f302015-06-01 13:50:06 +0200173}
174
Willy Tarreaub149eed2016-12-21 21:03:49 +0100175static void deinit_deviceatlas(void)
David Carlier8167f302015-06-01 13:50:06 +0200176{
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100177 if (global_deviceatlas.jsonpath != 0) {
178 free(global_deviceatlas.jsonpath);
David Carlier8167f302015-06-01 13:50:06 +0200179 }
180
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100181 if (global_deviceatlas.daset == 1) {
182 free(global_deviceatlas.cookiename);
183 da_atlas_close(&global_deviceatlas.atlas);
184 free(global_deviceatlas.atlasimgptr);
David Carlier8167f302015-06-01 13:50:06 +0200185 }
186
187 da_fini();
188}
189
David Carlier608c65a2015-09-25 14:16:30 +0100190static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo)
David Carlier8167f302015-06-01 13:50:06 +0200191{
Willy Tarreau83061a82018-07-13 11:56:34 +0200192 struct buffer *tmp;
David Carlier8167f302015-06-01 13:50:06 +0200193 da_propid_t prop, *pprop;
David Carlier8167f302015-06-01 13:50:06 +0200194 da_status_t status;
David Carlier608c65a2015-09-25 14:16:30 +0100195 da_type_t proptype;
196 const char *propname;
David Carlier8167f302015-06-01 13:50:06 +0200197 int i;
198
David Carlier8167f302015-06-01 13:50:06 +0200199 tmp = get_trash_chunk();
200 chunk_reset(tmp);
201
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200202 propname = (const char *) args[0].data.str.area;
David Carlier8167f302015-06-01 13:50:06 +0200203 i = 0;
204
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200205 for (; propname != 0; i ++,
206 propname = (const char *) args[i].data.str.area) {
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100207 status = da_atlas_getpropid(&global_deviceatlas.atlas,
David Carlier8167f302015-06-01 13:50:06 +0200208 propname, &prop);
209 if (status != DA_OK) {
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100210 chunk_appendf(tmp, "%c", global_deviceatlas.separator);
David Carlier8167f302015-06-01 13:50:06 +0200211 continue;
212 }
213 pprop = &prop;
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100214 da_atlas_getproptype(&global_deviceatlas.atlas, *pprop, &proptype);
David Carlier8167f302015-06-01 13:50:06 +0200215
216 switch (proptype) {
217 case DA_TYPE_BOOLEAN: {
218 bool val;
David Carlier608c65a2015-09-25 14:16:30 +0100219 status = da_getpropboolean(devinfo, *pprop, &val);
David Carlier8167f302015-06-01 13:50:06 +0200220 if (status == DA_OK) {
221 chunk_appendf(tmp, "%d", val);
222 }
223 break;
224 }
225 case DA_TYPE_INTEGER:
226 case DA_TYPE_NUMBER: {
227 long val;
David Carlier608c65a2015-09-25 14:16:30 +0100228 status = da_getpropinteger(devinfo, *pprop, &val);
David Carlier8167f302015-06-01 13:50:06 +0200229 if (status == DA_OK) {
230 chunk_appendf(tmp, "%ld", val);
231 }
232 break;
233 }
234 case DA_TYPE_STRING: {
235 const char *val;
David Carlier608c65a2015-09-25 14:16:30 +0100236 status = da_getpropstring(devinfo, *pprop, &val);
David Carlier8167f302015-06-01 13:50:06 +0200237 if (status == DA_OK) {
238 chunk_appendf(tmp, "%s", val);
239 }
240 break;
David Carlier608c65a2015-09-25 14:16:30 +0100241 }
David Carlier8167f302015-06-01 13:50:06 +0200242 default:
243 break;
244 }
245
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100246 chunk_appendf(tmp, "%c", global_deviceatlas.separator);
David Carlier8167f302015-06-01 13:50:06 +0200247 }
248
David Carlier608c65a2015-09-25 14:16:30 +0100249 da_close(devinfo);
David Carlier8167f302015-06-01 13:50:06 +0200250
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200251 if (tmp->data) {
252 --tmp->data;
253 tmp->area[tmp->data] = 0;
David Carlier8167f302015-06-01 13:50:06 +0200254 }
255
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200256 smp->data.u.str.area = tmp->area;
257 smp->data.u.str.data = tmp->data;
David Carlier7df41852019-07-10 21:19:24 +0100258 smp->data.type = SMP_T_STR;
David Carlier8167f302015-06-01 13:50:06 +0200259
260 return 1;
261}
262
David Carlier608c65a2015-09-25 14:16:30 +0100263static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *private)
264{
265 da_deviceinfo_t devinfo;
266 da_status_t status;
267 const char *useragent;
268 char useragentbuf[1024] = { 0 };
269 int i;
270
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200271 if (global_deviceatlas.daset == 0 || smp->data.u.str.data == 0) {
David Carlier608c65a2015-09-25 14:16:30 +0100272 return 1;
273 }
274
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200275 i = smp->data.u.str.data > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.data;
276 memcpy(useragentbuf, smp->data.u.str.area, i - 1);
David Carlier608c65a2015-09-25 14:16:30 +0100277 useragentbuf[i - 1] = 0;
278
279 useragent = (const char *)useragentbuf;
280
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100281 status = da_search(&global_deviceatlas.atlas, &devinfo,
282 global_deviceatlas.useragentid, useragent, 0);
David Carlier608c65a2015-09-25 14:16:30 +0100283
284 return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
285}
286
287#define DA_MAX_HEADERS 24
288
289static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private)
290{
David Carlier608c65a2015-09-25 14:16:30 +0100291 da_evidence_t ev[DA_MAX_HEADERS];
292 da_deviceinfo_t devinfo;
293 da_status_t status;
David CARLIER4de0eba2019-04-24 20:41:53 +0100294 struct channel *chn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200295 struct htx *htx;
296 struct htx_blk *blk;
David Carlier608c65a2015-09-25 14:16:30 +0100297 char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }};
298 int i, nbh = 0;
299
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100300 if (global_deviceatlas.daset == 0) {
David CARLIER4de0eba2019-04-24 20:41:53 +0100301 return 0;
David Carlier608c65a2015-09-25 14:16:30 +0100302 }
303
David CARLIER4de0eba2019-04-24 20:41:53 +0100304 chn = (smp->strm ? &smp->strm->req : NULL);
Christopher Fauletd1185cc2020-05-05 11:49:57 +0200305 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200306 if (!htx)
307 return 0;
David Carlier608c65a2015-09-25 14:16:30 +0100308
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200309 i = 0;
310 for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) {
311 size_t vlen;
312 char *pval;
313 da_evidence_id_t evid;
314 enum htx_blk_type type;
315 struct ist n, v;
316 char hbuf[24] = { 0 };
317 char tval[1024] = { 0 };
David Carlier608c65a2015-09-25 14:16:30 +0100318
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200319 type = htx_get_blk_type(blk);
David Carlier608c65a2015-09-25 14:16:30 +0100320
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200321 if (type == HTX_BLK_HDR) {
322 n = htx_get_blk_name(htx, blk);
323 v = htx_get_blk_value(htx, blk);
324 } else if (type == HTX_BLK_EOH) {
325 break;
326 } else {
327 continue;
328 }
David Carlier608c65a2015-09-25 14:16:30 +0100329
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200330 /* The HTTP headers used by the DeviceAtlas API are not longer */
331 if (n.len >= sizeof(hbuf)) {
332 continue;
David Carlier608c65a2015-09-25 14:16:30 +0100333 }
334
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200335 memcpy(hbuf, n.ptr, n.len);
336 hbuf[n.len] = 0;
337 pval = v.ptr;
338 vlen = v.len;
339 evid = -1;
340 i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len;
341 memcpy(tval, v.ptr, i);
342 tval[i] = 0;
343 pval = tval;
David CARLIER4de0eba2019-04-24 20:41:53 +0100344
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200345 if (strcasecmp(hbuf, "Accept-Language") == 0) {
346 evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas);
347 } else if (strcasecmp(hbuf, "Cookie") == 0) {
348 char *p, *eval;
349 size_t pl;
David CARLIER4de0eba2019-04-24 20:41:53 +0100350
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200351 eval = pval + vlen;
352 /**
353 * The cookie value, if it exists, is located between the current header's
354 * value position and the next one
355 */
356 if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename,
357 global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) {
David CARLIER4de0eba2019-04-24 20:41:53 +0100358 continue;
359 }
360
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200361 vlen -= global_deviceatlas.cookienamelen - 1;
362 pval = p;
363 evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas);
364 } else {
365 evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas, hbuf);
366 }
David CARLIER4de0eba2019-04-24 20:41:53 +0100367
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200368 if (evid == -1) {
369 continue;
David CARLIER4de0eba2019-04-24 20:41:53 +0100370 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200371
372 i = vlen > sizeof(vbuf[nbh]) - 1 ? sizeof(vbuf[nbh]) - 1 : vlen;
373 memcpy(vbuf[nbh], pval, i);
374 vbuf[nbh][i] = 0;
375 ev[nbh].key = evid;
376 ev[nbh].value = vbuf[nbh];
377 ++ nbh;
David Carlier608c65a2015-09-25 14:16:30 +0100378 }
379
Willy Tarreaubee9dde2016-12-21 21:25:06 +0100380 status = da_searchv(&global_deviceatlas.atlas, &devinfo,
David Carlier608c65a2015-09-25 14:16:30 +0100381 ev, nbh);
382
383 return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo);
384}
385
Willy Tarreau0d74f772015-06-01 15:42:29 +0200386static struct cfg_kw_list dacfg_kws = {{ }, {
387 { CFG_GLOBAL, "deviceatlas-json-file", da_json_file },
David CARLIER4de0eba2019-04-24 20:41:53 +0100388 { CFG_GLOBAL, "deviceatlas-log-level", da_log_level },
389 { CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator },
390 { CFG_GLOBAL, "deviceatlas-properties-cookie", da_properties_cookie },
391 { 0, NULL, NULL },
Willy Tarreau0d74f772015-06-01 15:42:29 +0200392}};
393
Willy Tarreau0108d902018-11-25 19:14:37 +0100394INITCALL1(STG_REGISTER, cfg_register_keywords, &dacfg_kws);
395
Willy Tarreauf63386a2015-06-01 15:39:50 +0200396/* Note: must not be declared <const> as its list will be overwritten */
David Carlier608c65a2015-09-25 14:16:30 +0100397static struct sample_fetch_kw_list fetch_kws = {ILH, {
David Carlier840b0242016-03-16 10:09:55 +0000398 { "da-csv-fetch", da_haproxy_fetch, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
David CARLIER4de0eba2019-04-24 20:41:53 +0100399 { NULL, NULL, 0, 0, 0 },
David Carlier608c65a2015-09-25 14:16:30 +0100400}};
401
Willy Tarreau0108d902018-11-25 19:14:37 +0100402INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws);
403
David Carlier608c65a2015-09-25 14:16:30 +0100404/* Note: must not be declared <const> as its list will be overwritten */
Willy Tarreauf63386a2015-06-01 15:39:50 +0200405static struct sample_conv_kw_list conv_kws = {ILH, {
David Carlier840b0242016-03-16 10:09:55 +0000406 { "da-csv-conv", da_haproxy_conv, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
David CARLIER4de0eba2019-04-24 20:41:53 +0100407 { NULL, NULL, 0, 0, 0 },
Willy Tarreauf63386a2015-06-01 15:39:50 +0200408}};
409
David Carlier0470d702019-04-26 12:02:28 +0000410static void da_haproxy_register_build_options()
411{
412 char *ptr = NULL;
413
414#ifdef MOBI_DA_DUMMY_LIBRARY
415 memprintf(&ptr, "Built with DeviceAtlas support (dummy library only).");
416#else
417 memprintf(&ptr, "Built with DeviceAtlas support (library version %u.%u).", MOBI_DA_MAJOR, MOBI_DA_MINOR);
418#endif
419 hap_register_build_opts(ptr, 1);
420}
421
Willy Tarreau0108d902018-11-25 19:14:37 +0100422INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws);
423
Willy Tarreau172f5ce2018-11-26 11:21:50 +0100424REGISTER_POST_CHECK(init_deviceatlas);
425REGISTER_POST_DEINIT(deinit_deviceatlas);
David Carlier0470d702019-04-26 12:02:28 +0000426INITCALL0(STG_REGISTER, da_haproxy_register_build_options);