#include #include #include #include #include #include static int da_json_file(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "deviceatlas json file : expects a json path.\n"); return -1; } global.deviceatlas.jsonpath = strdup(args[1]); return 0; } static int da_log_level(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { int loglevel; if (*(args[1]) == 0) { memprintf(err, "deviceatlas log level : expects an integer argument.\n"); return -1; } loglevel = atol(args[1]); if (loglevel < 0 || loglevel > 3) { memprintf(err, "deviceatlas log level : expects a log level between 0 and 3, %s given.\n", args[1]); } else { global.deviceatlas.loglevel = (da_severity_t)loglevel; } return 0; } static int da_property_separator(char **args, int section_type, struct proxy *curpx, struct proxy *defpx, const char *file, int line, char **err) { if (*(args[1]) == 0) { memprintf(err, "deviceatlas property separator : expects a character argument.\n"); return -1; } global.deviceatlas.separator = *args[1]; return 0; } static size_t da_haproxy_read(void *ctx, size_t len, char *buf) { return fread(buf, 1, len, ctx); } static da_status_t da_haproxy_seek(void *ctx, off_t off) { return fseek(ctx, off, SEEK_SET) != -1 ? DA_OK : DA_SYS; } static void da_haproxy_log(da_severity_t severity, da_status_t status, const char *fmt, va_list args) { if (global.deviceatlas.loglevel && severity <= global.deviceatlas.loglevel) { char logbuf[256]; vsnprintf(logbuf, sizeof(logbuf), fmt, args); Warning("deviceatlas : %s.\n", logbuf); } } int init_deviceatlas(void) { da_status_t status = DA_SYS; if (global.deviceatlas.jsonpath != 0) { FILE *jsonp; da_property_decl_t extraprops[] = {{0, 0}}; size_t atlasimglen; da_status_t status; jsonp = fopen(global.deviceatlas.jsonpath, "r"); if (jsonp == 0) { Alert("deviceatlas : '%s' json file has invalid path or is not readable.\n", global.deviceatlas.jsonpath); goto out; } da_init(); da_seterrorfunc(da_haproxy_log); status = da_atlas_compile(jsonp, da_haproxy_read, da_haproxy_seek, &global.deviceatlas.atlasimgptr, &atlasimglen); fclose(jsonp); if (status != DA_OK) { Alert("deviceatlas : '%s' json file is invalid.\n", global.deviceatlas.jsonpath); goto out; } status = da_atlas_open(&global.deviceatlas.atlas, extraprops, global.deviceatlas.atlasimgptr, atlasimglen); if (status != DA_OK) { Alert("deviceatlas : data could not be compiled.\n"); goto out; } global.deviceatlas.useragentid = da_atlas_header_evidence_id(&global.deviceatlas.atlas, "user-agent"); fprintf(stdout, "Deviceatlas module loaded.\n"); } out: return status == DA_OK; } void deinit_deviceatlas(void) { if (global.deviceatlas.jsonpath != 0) { free(global.deviceatlas.jsonpath); } if (global.deviceatlas.useragentid > 0) { da_atlas_close(&global.deviceatlas.atlas); free(global.deviceatlas.atlasimgptr); } da_fini(); } static int da_haproxy(const struct arg *args, struct sample *smp, void *private) { struct chunk *tmp; da_deviceinfo_t devinfo; da_propid_t prop, *pprop; da_type_t proptype; da_status_t status; const char *useragent, *propname; char useragentbuf[1024]; int i; if (global.deviceatlas.useragentid == 0) { return 1; } tmp = get_trash_chunk(); chunk_reset(tmp); i = smp->data.u.str.len > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.len; memcpy(useragentbuf, smp->data.u.str.str, i - 1); useragentbuf[i - 1] = 0; useragent = (const char *)useragentbuf; propname = (const char *)args[0].data.str.str; i = 0; status = da_search(&global.deviceatlas.atlas, &devinfo, global.deviceatlas.useragentid, useragent, 0); if (status != DA_OK) { return 0; } for (; propname != 0; i ++, propname = (const char *)args[i].data.str.str) { status = da_atlas_getpropid(&global.deviceatlas.atlas, propname, &prop); if (status != DA_OK) { chunk_appendf(tmp, "%c", global.deviceatlas.separator); continue; } pprop = ∝ da_atlas_getproptype(&global.deviceatlas.atlas, *pprop, &proptype); switch (proptype) { case DA_TYPE_BOOLEAN: { bool val; status = da_getpropboolean(&devinfo, *pprop, &val); if (status == DA_OK) { chunk_appendf(tmp, "%d", val); } break; } case DA_TYPE_INTEGER: case DA_TYPE_NUMBER: { long val; status = da_getpropinteger(&devinfo, *pprop, &val); if (status == DA_OK) { chunk_appendf(tmp, "%ld", val); } break; } case DA_TYPE_STRING: { const char *val; status = da_getpropstring(&devinfo, *pprop, &val); if (status == DA_OK) { chunk_appendf(tmp, "%s", val); } break; } default: break; } chunk_appendf(tmp, "%c", global.deviceatlas.separator); } da_close(&devinfo); if (tmp->len) { --tmp->len; tmp->str[tmp->len] = 0; } smp->data.u.str.str = tmp->str; smp->data.u.str.len = strlen(tmp->str); return 1; } static struct cfg_kw_list dacfg_kws = {{ }, { { CFG_GLOBAL, "deviceatlas-json-file", da_json_file }, { CFG_GLOBAL, "deviceatlas-log-level", da_log_level }, { CFG_GLOBAL, "deviceatlas-property-separator", da_property_separator }, { 0, NULL, NULL }, }}; /* Note: must not be declared as its list will be overwritten */ static struct sample_conv_kw_list conv_kws = {ILH, { { "da-csv", da_haproxy, ARG5(1,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR }, { NULL, NULL, 0, 0, 0 }, }}; __attribute__((constructor)) static void __da_init(void) { /* register sample fetch and format conversion keywords */ sample_register_convs(&conv_kws); cfg_register_keywords(&dacfg_kws); }