blob: 03b4b3e89f4248cd44bbd7146d70287ec615e57f [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
55static struct cfg_kw_list dacfg_kws = {{ }, {
56 { CFG_GLOBAL, "deviceatlas-json-file", da_json_file },
57 { CFG_GLOBAL, "deviceatlas-log-level", da_log_level },
58 { CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator },
59 { 0, NULL, NULL },
60}};
61
62static size_t da_haproxy_read(void *ctx, size_t len, char *buf)
63{
64 return fread(buf, 1, len, ctx);
65}
66
67static da_status_t da_haproxy_seek(void *ctx, off_t off)
68{
69 return fseek(ctx, off, SEEK_SET) != -1 ? DA_OK : DA_SYS;
70}
71
72static void da_haproxy_log(da_severity_t severity, da_status_t status,
73 const char *fmt, va_list args)
74{
Willy Tarreau6bd42e72015-06-01 15:25:46 +020075 if (global.deviceatlas.loglevel && severity <= global.deviceatlas.loglevel) {
David Carlier8167f302015-06-01 13:50:06 +020076 char logbuf[256];
77 vsnprintf(logbuf, sizeof(logbuf), fmt, args);
78 Warning("deviceatlas : %s.\n", logbuf);
79 }
80}
81
82void da_register_cfgkeywords(void)
83{
84 cfg_register_keywords(&dacfg_kws);
85}
86
87int init_deviceatlas(void)
88{
89 da_status_t status = DA_SYS;
90 if (global.deviceatlas.jsonpath != 0) {
91 FILE *jsonp;
92 da_property_decl_t extraprops[] = {{0, 0}};
93 size_t atlasimglen;
94 da_status_t status;
95
96 jsonp = fopen(global.deviceatlas.jsonpath, "r");
97 if (jsonp == 0) {
98 Alert("deviceatlas : '%s' json file has invalid path or is not readable.\n",
99 global.deviceatlas.jsonpath);
100 goto out;
101 }
102
103 da_init();
104 da_seterrorfunc(da_haproxy_log);
105 status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek,
106 &global.deviceatlas.atlasimgptr, &atlasimglen);
107 fclose(jsonp);
108 if (status != DA_OK) {
109 Alert("deviceatlas : '%s' json file is invalid.\n",
110 global.deviceatlas.jsonpath);
111 goto out;
112 }
113
114 status = da_atlas_open(&global.deviceatlas.atlas, extraprops,
115 global.deviceatlas.atlasimgptr, atlasimglen);
116
117 if (status != DA_OK) {
118 Alert("deviceatlas : data could not be compiled.\n");
119 goto out;
120 }
121
122 global.deviceatlas.useragentid = da_atlas_header_evidence_id(&global.deviceatlas.atlas,
123 "user-agent");
124
125 fprintf(stdout, "Deviceatlas module loaded.\n");
126 }
127
128out:
129 return status == DA_OK;
130}
131
132void deinit_deviceatlas(void)
133{
134 if (global.deviceatlas.jsonpath != 0) {
135 free(global.deviceatlas.jsonpath);
136 }
137
138 if (global.deviceatlas.useragentid > 0) {
139 da_atlas_close(&global.deviceatlas.atlas);
140 free(global.deviceatlas.atlasimgptr);
141 }
142
143 da_fini();
144}
145
Willy Tarreauf63386a2015-06-01 15:39:50 +0200146static int da_haproxy(const struct arg *args, struct sample *smp, void *private)
David Carlier8167f302015-06-01 13:50:06 +0200147{
148 struct chunk *tmp;
149 da_deviceinfo_t devinfo;
150 da_propid_t prop, *pprop;
151 da_type_t proptype;
152 da_status_t status;
153 const char *useragent, *propname;
154 char useragentbuf[1024];
155 int i;
156
157 if (global.deviceatlas.useragentid == 0) {
158 return 1;
159 }
160
161 tmp = get_trash_chunk();
162 chunk_reset(tmp);
163
164 i = smp->data.str.len > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.str.len;
165 memcpy(useragentbuf, smp->data.str.str, i - 1);
166 useragentbuf[i - 1] = 0;
167
168 useragent = (const char *)useragentbuf;
169 propname = (const char *)args[0].data.str.str;
170 i = 0;
171
172 status = da_search(&global.deviceatlas.atlas, &devinfo,
173 global.deviceatlas.useragentid, useragent, 0);
174 if (status != DA_OK) {
175 return 0;
176 }
177
178 for (; propname != 0; i ++, propname = (const char *)args[i].data.str.str) {
179 status = da_atlas_getpropid(&global.deviceatlas.atlas,
180 propname, &prop);
181 if (status != DA_OK) {
182 chunk_appendf(tmp, "%c", global.deviceatlas.separator);
183 continue;
184 }
185 pprop = &prop;
186 da_atlas_getproptype(&global.deviceatlas.atlas, *pprop, &proptype);
187
188 switch (proptype) {
189 case DA_TYPE_BOOLEAN: {
190 bool val;
191 status = da_getpropboolean(&devinfo, *pprop, &val);
192 if (status == DA_OK) {
193 chunk_appendf(tmp, "%d", val);
194 }
195 break;
196 }
197 case DA_TYPE_INTEGER:
198 case DA_TYPE_NUMBER: {
199 long val;
200 status = da_getpropinteger(&devinfo, *pprop, &val);
201 if (status == DA_OK) {
202 chunk_appendf(tmp, "%ld", val);
203 }
204 break;
205 }
206 case DA_TYPE_STRING: {
207 const char *val;
208 status = da_getpropstring(&devinfo, *pprop, &val);
209 if (status == DA_OK) {
210 chunk_appendf(tmp, "%s", val);
211 }
212 break;
213 }
214 default:
215 break;
216 }
217
218 chunk_appendf(tmp, "%c", global.deviceatlas.separator);
219 }
220
221 da_close(&devinfo);
222
223 if (tmp->len) {
224 --tmp->len;
225 tmp->str[tmp->len] = 0;
226 }
227
228 smp->data.str.str = tmp->str;
229 smp->data.str.len = strlen(tmp->str);
230
231 return 1;
232}
233
Willy Tarreauf63386a2015-06-01 15:39:50 +0200234/* Note: must not be declared <const> as its list will be overwritten */
235static struct sample_conv_kw_list conv_kws = {ILH, {
236 { "da-csv", da_haproxy, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR },
237 { NULL, NULL, 0, 0, 0 },
238}};
239
240__attribute__((constructor))
241static void __da_init(void)
242{
243 /* register sample fetch and format conversion keywords */
244 sample_register_convs(&conv_kws);
245}
David Carlier8167f302015-06-01 13:50:06 +0200246#endif