blob: 435cc3fd3e0a0d9a16378cd4a19df0234601862d [file] [log] [blame]
David Carlier8167f302015-06-01 13:50:06 +02001#ifdef USE_DEVICEATLAS
2
3#include <stdio.h>
4
5#include <common/cfgparse.h>
Willy Tarreauf63386a2015-06-01 15:39:50 +02006#include <proto/arg.h>
David Carlier8167f302015-06-01 13:50:06 +02007#include <proto/log.h>
Willy Tarreauf63386a2015-06-01 15:39:50 +02008#include <proto/sample.h>
David Carlier8167f302015-06-01 13:50:06 +02009#include <import/da.h>
10
11static int da_json_file(char **args, int section_type, struct proxy *curpx,
12 struct proxy *defpx, const char *file, int line,
13 char **err)
14{
15 if (*(args[1]) == 0) {
16 memprintf(err, "deviceatlas json file : expects a json path.\n");
17 return -1;
18 }
19 global.deviceatlas.jsonpath = strdup(args[1]);
20 return 0;
21}
22
23static int da_log_level(char **args, int section_type, struct proxy *curpx,
24 struct proxy *defpx, const char *file, int line,
25 char **err)
26{
27 int loglevel;
28 if (*(args[1]) == 0) {
29 memprintf(err, "deviceatlas log level : expects an integer argument.\n");
30 return -1;
31 }
32
33 loglevel = atol(args[1]);
34 if (loglevel < 0 || loglevel > 3) {
35 memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]);
36 } else {
37 global.deviceatlas.loglevel = (da_severity_t)loglevel;
38 }
39
40 return 0;
41}
42
43static int da_property_separator(char **args, int section_type, struct proxy *curpx,
44 struct proxy *defpx, const char *file, int line,
45 char **err)
46{
47 if (*(args[1]) == 0) {
48 memprintf(err, "deviceatlas property separator : expects a character argument.\n");
49 return -1;
50 }
51 global.deviceatlas.separator = *args[1];
52 return 0;
53}
54
David Carlier8167f302015-06-01 13:50:06 +020055static size_t da_haproxy_read(void *ctx, size_t len, char *buf)
56{
57 return fread(buf, 1, len, ctx);
58}
59
60static da_status_t da_haproxy_seek(void *ctx, off_t off)
61{
62 return fseek(ctx, off, SEEK_SET) != -1 ? DA_OK : DA_SYS;
63}
64
65static void da_haproxy_log(da_severity_t severity, da_status_t status,
66 const char *fmt, va_list args)
67{
Willy Tarreau6bd42e72015-06-01 15:25:46 +020068 if (global.deviceatlas.loglevel && severity <= global.deviceatlas.loglevel) {
David Carlier8167f302015-06-01 13:50:06 +020069 char logbuf[256];
70 vsnprintf(logbuf, sizeof(logbuf), fmt, args);
71 Warning("deviceatlas : %s.\n", logbuf);
72 }
73}
74
David Carlier8167f302015-06-01 13:50:06 +020075int init_deviceatlas(void)
76{
77 da_status_t status = DA_SYS;
78 if (global.deviceatlas.jsonpath != 0) {
79 FILE *jsonp;
80 da_property_decl_t extraprops[] = {{0, 0}};
81 size_t atlasimglen;
82 da_status_t status;
83
84 jsonp = fopen(global.deviceatlas.jsonpath, "r");
85 if (jsonp == 0) {
86 Alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
87 global.deviceatlas.jsonpath);
88 goto out;
89 }
90
91 da_init();
92 da_seterrorfunc(da_haproxy_log);
93 status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
94 &global.deviceatlas.atlasimgptr, &atlasimglen);
95 fclose(jsonp);
96 if (status != DA_OK) {
97 Alert("deviceatlas : '%s' json file is invalid.\n",
98 global.deviceatlas.jsonpath);
99 goto out;
100 }
101
102 status = da_atlas_open(&global.deviceatlas.atlas, extraprops,
103 global.deviceatlas.atlasimgptr, atlasimglen);
104
105 if (status != DA_OK) {
106 Alert("deviceatlas : data could not be compiled.\n");
107 goto out;
108 }
109
110 global.deviceatlas.useragentid = da_atlas_header_evidence_id(&global.deviceatlas.atlas,
111 "user-agent");
112
113 fprintf(stdout, "Deviceatlas module loaded.\n");
114 }
115
116out:
117 return status == DA_OK;
118}
119
120void deinit_deviceatlas(void)
121{
122 if (global.deviceatlas.jsonpath != 0) {
123 free(global.deviceatlas.jsonpath);
124 }
125
126 if (global.deviceatlas.useragentid > 0) {
127 da_atlas_close(&global.deviceatlas.atlas);
128 free(global.deviceatlas.atlasimgptr);
129 }
130
131 da_fini();
132}
133
Willy Tarreauf63386a2015-06-01 15:39:50 +0200134static int da_haproxy(const struct arg *args, struct sample *smp, void *private)
David Carlier8167f302015-06-01 13:50:06 +0200135{
136 struct chunk *tmp;
137 da_deviceinfo_t devinfo;
138 da_propid_t prop, *pprop;
139 da_type_t proptype;
140 da_status_t status;
141 const char *useragent, *propname;
142 char useragentbuf[1024];
143 int i;
144
145 if (global.deviceatlas.useragentid == 0) {
146 return 1;
147 }
148
149 tmp = get_trash_chunk();
150 chunk_reset(tmp);
151
152 i = smp->data.str.len > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.str.len;
153 memcpy(useragentbuf, smp->data.str.str, i - 1);
154 useragentbuf[i - 1] = 0;
155
156 useragent = (const char *)useragentbuf;
157 propname = (const char *)args[0].data.str.str;
158 i = 0;
159
160 status = da_search(&global.deviceatlas.atlas, &devinfo,
161 global.deviceatlas.useragentid, useragent, 0);
162 if (status != DA_OK) {
163 return 0;
164 }
165
166 for (; propname != 0; i ++, propname = (const char *)args[i].data.str.str) {
167 status = da_atlas_getpropid(&global.deviceatlas.atlas,
168 propname, &prop);
169 if (status != DA_OK) {
170 chunk_appendf(tmp, "%c", global.deviceatlas.separator);
171 continue;
172 }
173 pprop = &prop;
174 da_atlas_getproptype(&global.deviceatlas.atlas, *pprop, &proptype);
175
176 switch (proptype) {
177 case DA_TYPE_BOOLEAN: {
178 bool val;
179 status = da_getpropboolean(&devinfo, *pprop, &val);
180 if (status == DA_OK) {
181 chunk_appendf(tmp, "%d", val);
182 }
183 break;
184 }
185 case DA_TYPE_INTEGER:
186 case DA_TYPE_NUMBER: {
187 long val;
188 status = da_getpropinteger(&devinfo, *pprop, &val);
189 if (status == DA_OK) {
190 chunk_appendf(tmp, "%ld", val);
191 }
192 break;
193 }
194 case DA_TYPE_STRING: {
195 const char *val;
196 status = da_getpropstring(&devinfo, *pprop, &val);
197 if (status == DA_OK) {
198 chunk_appendf(tmp, "%s", val);
199 }
200 break;
201 }
202 default:
203 break;
204 }
205
206 chunk_appendf(tmp, "%c", global.deviceatlas.separator);
207 }
208
209 da_close(&devinfo);
210
211 if (tmp->len) {
212 --tmp->len;
213 tmp->str[tmp->len] = 0;
214 }
215
216 smp->data.str.str = tmp->str;
217 smp->data.str.len = strlen(tmp->str);
218
219 return 1;
220}
221
Willy Tarreau0d74f772015-06-01 15:42:29 +0200222static struct cfg_kw_list dacfg_kws = {{ }, {
223 { CFG_GLOBAL, "deviceatlas-json-file", da_json_file },
224 { CFG_GLOBAL, "deviceatlas-log-level", da_log_level },
225 { CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator },
226 { 0, NULL, NULL },
227}};
228
Willy Tarreauf63386a2015-06-01 15:39:50 +0200229/* Note: must not be declared <const> as its list will be overwritten */
230static struct sample_conv_kw_list conv_kws = {ILH, {
231 { "da-csv", da_haproxy, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
232 { NULL, NULL, 0, 0, 0 },
233}};
234
235__attribute__((constructor))
236static void __da_init(void)
237{
238 /* register sample fetch and format conversion keywords */
239 sample_register_convs(&conv_kws);
Willy Tarreau0d74f772015-06-01 15:42:29 +0200240 cfg_register_keywords(&dacfg_kws);
Willy Tarreauf63386a2015-06-01 15:39:50 +0200241}
David Carlier8167f302015-06-01 13:50:06 +0200242#endif