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