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