diff options
Diffstat (limited to '')
-rw-r--r-- | addons/deviceatlas/Makefile | 48 | ||||
-rw-r--r-- | addons/deviceatlas/da.c | 501 | ||||
-rw-r--r-- | addons/deviceatlas/dadwsch.c | 195 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/Makefile | 12 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/Os/daunix.c | 9 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/dac.c | 222 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/dac.h | 600 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/dadwcom.c | 1 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/dasch.c | 1 | ||||
-rw-r--r-- | addons/deviceatlas/dummy/json.c | 1 |
10 files changed, 1590 insertions, 0 deletions
diff --git a/addons/deviceatlas/Makefile b/addons/deviceatlas/Makefile new file mode 100644 index 0000000..fbcffca --- /dev/null +++ b/addons/deviceatlas/Makefile @@ -0,0 +1,48 @@ +# DEVICEATLAS_SRC : DeviceAtlas API source root path + + +OS := $(shell uname -s) +OBJS := dadwsch.o +CFLAGS := -g -O2 +LDFLAGS := + +CURL_CONFIG := curl-config +CURLDIR := $(shell $(CURL_CONFIG) --prefix 2>/dev/null || echo /usr/local) +CURL_INC := $(CURLDIR)/include +CURL_LIB := $(CURLDIR)/lib +CURL_LDFLAGS := $(shell $(CURL_CONFIG) --libs 2>/dev/null || echo -L /usr/local/lib -lcurl) + +PCRE2_CONFIG := pcre2-config +PCRE2DIR := $(shell $(PCRE2_CONFIG) --prefix 2>/dev/null || echo /usr/local) +PCRE2_INC := $(PCRE2DIR)/include +PCRE2_LIB := $(PCRE2DIR)/lib +PCRE2_LDFLAGS := $(shell $(PCRE2_CONFIG) --libs8 2>/dev/null || echo /usr/local) + +ifeq ($(DEVICEATLAS_SRC),) +dadwsch: dadwsch.c + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +LDFLAGS += -lda +else +DEVICEATLAS_INC = $(DEVICEATLAS_SRC) +DEVICEATLAS_LIB = $(DEVICEATLAS_SRC) +CFLAGS += -DDA_REGEX_HDR=\"dac_pcre2.c\" -DDA_REGEX_TAG=2 +CFLAGS += -DMOBI_CURL -DMOBI_CURLSSET -DMOBI_GZ -DMOBI_ZIP +CFLAGS += -I$(DEVICEATLAS_INC) -I$(CURL_INC) -I$(PCRE2DIR) +LDFLAGS += $(CURL_LDFLAGS) $(PCRE2_LDFLAGS) -lz -lzip -lpthread + +dadwsch: dadwsch.c $(DEVICEATLAS_SRC)/dac.c $(DEVICEATLAS_SRC)/dasch.c $(DEVICEATLAS_SRC)/dadwarc.c $(DEVICEATLAS_SRC)/dadwcom.c $(DEVICEATLAS_SRC)/dadwcurl.c $(DEVICEATLAS_SRC)/json.c $(DEVICEATLAS_SRC)/Os/daunix.c + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) +endif + +ifeq ($(OS), Linux) +LDFLAGS += -lrt +endif +ifeq ($(OS), SunOS) +LDFLAGS += -lrt +endif + +clean: + rm -f *.o + rm -f $(DEVICEATLAS_LIB)*.o + rm -f dadwsch diff --git a/addons/deviceatlas/da.c b/addons/deviceatlas/da.c new file mode 100644 index 0000000..969dfaa --- /dev/null +++ b/addons/deviceatlas/da.c @@ -0,0 +1,501 @@ +#include <stdio.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <errno.h> + +#include <haproxy/api.h> +#include <haproxy/arg.h> +#include <haproxy/cfgparse.h> +#include <haproxy/errors.h> +#include <haproxy/global.h> +#include <haproxy/http.h> +#include <haproxy/http_ana.h> +#include <haproxy/http_fetch.h> +#include <haproxy/http_htx.h> +#include <haproxy/htx.h> +#include <haproxy/sample.h> +#include <haproxy/tools.h> +#include <dac.h> + +#define ATLASTOKSZ PATH_MAX +#define ATLASMAPNM "/hapdeviceatlas" + +static struct { + void *atlasimgptr; + void *atlasmap; + char *jsonpath; + char *cookiename; + size_t cookienamelen; + int atlasfd; + da_atlas_t atlas; + da_evidence_id_t useragentid; + da_severity_t loglevel; + char separator; + unsigned char daset:1; +} global_deviceatlas = { + .loglevel = 0, + .jsonpath = 0, + .cookiename = 0, + .cookienamelen = 0, + .atlasmap = NULL, + .atlasfd = -1, + .useragentid = 0, + .daset = 0, + .separator = '|', +}; + +__decl_thread(HA_SPINLOCK_T dadwsch_lock); + +static int da_json_file(char **args, int section_type, struct proxy *curpx, + const 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, + const 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, + const 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 int da_properties_cookie(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int line, + char **err) +{ + if (*(args[1]) == 0) { + memprintf(err, "deviceatlas cookie name : expects a string argument.\n"); + return -1; + } else { + global_deviceatlas.cookiename = strdup(args[1]); + } + global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename); + 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); + ha_warning("deviceatlas : %s.\n", logbuf); + } +} + +#define DA_COOKIENAME_DEFAULT "DAPROPS" + +/* + * module init / deinit functions. Returns 0 if OK, or a combination of ERR_*. + */ +static int init_deviceatlas(void) +{ + int err_code = ERR_NONE; + + 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) { + ha_alert("deviceatlas : '%s' json file has invalid path or is not readable.\n", + global_deviceatlas.jsonpath); + err_code |= ERR_ALERT | ERR_FATAL; + 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) { + ha_alert("deviceatlas : '%s' json file is invalid.\n", + global_deviceatlas.jsonpath); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + status = da_atlas_open(&global_deviceatlas.atlas, extraprops, + global_deviceatlas.atlasimgptr, atlasimglen); + + if (status != DA_OK) { + ha_alert("deviceatlas : data could not be compiled.\n"); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (global_deviceatlas.cookiename == 0) { + global_deviceatlas.cookiename = strdup(DA_COOKIENAME_DEFAULT); + global_deviceatlas.cookienamelen = strlen(global_deviceatlas.cookiename); + } + + global_deviceatlas.useragentid = da_atlas_header_evidence_id(&global_deviceatlas.atlas, + "user-agent"); + if ((global_deviceatlas.atlasfd = shm_open(ATLASMAPNM, O_RDWR, 0660)) != -1) { + global_deviceatlas.atlasmap = mmap(NULL, ATLASTOKSZ, PROT_READ | PROT_WRITE, MAP_SHARED, global_deviceatlas.atlasfd, 0); + if (global_deviceatlas.atlasmap == MAP_FAILED) { + close(global_deviceatlas.atlasfd); + global_deviceatlas.atlasfd = -1; + global_deviceatlas.atlasmap = NULL; + } else { + fprintf(stdout, "Deviceatlas : scheduling support enabled.\n"); + } + } + global_deviceatlas.daset = 1; + + fprintf(stdout, "Deviceatlas module loaded.\n"); + } + +out: + return err_code; +} + +static void deinit_deviceatlas(void) +{ + if (global_deviceatlas.jsonpath != 0) { + free(global_deviceatlas.jsonpath); + } + + if (global_deviceatlas.daset == 1) { + free(global_deviceatlas.cookiename); + da_atlas_close(&global_deviceatlas.atlas); + free(global_deviceatlas.atlasimgptr); + } + + if (global_deviceatlas.atlasfd != -1) { + munmap(global_deviceatlas.atlasmap, ATLASTOKSZ); + close(global_deviceatlas.atlasfd); + shm_unlink(ATLASMAPNM); + } + + da_fini(); +} + +static void da_haproxy_checkinst(void) +{ + if (global_deviceatlas.atlasmap != 0) { + char *base; + base = (char *)global_deviceatlas.atlasmap; + + if (base[0] != 0) { + void *cnew; + size_t atlassz; + char atlasp[ATLASTOKSZ] = {0}; + da_atlas_t inst; + da_property_decl_t extraprops[1] = {{NULL, 0}}; +#ifdef USE_THREAD + HA_SPIN_LOCK(OTHER_LOCK, &dadwsch_lock); +#endif + strlcpy2(atlasp, base, sizeof(atlasp)); + if (da_atlas_read_mapped(atlasp, NULL, &cnew, &atlassz) == DA_OK) { + if (da_atlas_open(&inst, extraprops, cnew, atlassz) == DA_OK) { + char jsonbuf[26]; + time_t jsond; + + da_atlas_close(&global_deviceatlas.atlas); + free(global_deviceatlas.atlasimgptr); + global_deviceatlas.atlasimgptr = cnew; + global_deviceatlas.atlas = inst; + memset(base, 0, ATLASTOKSZ); + jsond = da_getdatacreation(&global_deviceatlas.atlas); + ctime_r(&jsond, jsonbuf); + jsonbuf[24] = 0; + printf("deviceatlas: new instance, data file date `%s`.\n", jsonbuf); + } else { + ha_warning("deviceatlas: instance update failed.\n"); + memset(base, 0, ATLASTOKSZ); + free(cnew); + } + } +#ifdef USE_THREAD + HA_SPIN_UNLOCK(OTHER_LOCK, &dadwsch_lock); +#endif + } + } +} + +static int da_haproxy(const struct arg *args, struct sample *smp, da_deviceinfo_t *devinfo) +{ + struct buffer *tmp; + da_propid_t prop, *pprop; + da_status_t status; + da_type_t proptype; + const char *propname; + int i; + + tmp = get_trash_chunk(); + chunk_reset(tmp); + + propname = (const char *) args[0].data.str.area; + i = 0; + + for (; propname != 0; i ++, + propname = (const char *) args[i].data.str.area) { + 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->data) { + --tmp->data; + tmp->area[tmp->data] = 0; + } + + smp->data.u.str.area = tmp->area; + smp->data.u.str.data = tmp->data; + smp->data.type = SMP_T_STR; + + return 1; +} + +static int da_haproxy_conv(const struct arg *args, struct sample *smp, void *private) +{ + da_deviceinfo_t devinfo; + da_status_t status; + const char *useragent; + char useragentbuf[1024] = { 0 }; + int i; + + if (global_deviceatlas.daset == 0 || smp->data.u.str.data == 0) { + return 1; + } + + da_haproxy_checkinst(); + + i = smp->data.u.str.data > sizeof(useragentbuf) ? sizeof(useragentbuf) : smp->data.u.str.data; + memcpy(useragentbuf, smp->data.u.str.area, i - 1); + useragentbuf[i - 1] = 0; + + useragent = (const char *)useragentbuf; + + status = da_search(&global_deviceatlas.atlas, &devinfo, + global_deviceatlas.useragentid, useragent, 0); + + return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo); +} + +#define DA_MAX_HEADERS 24 + +static int da_haproxy_fetch(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + da_evidence_t ev[DA_MAX_HEADERS]; + da_deviceinfo_t devinfo; + da_status_t status; + struct channel *chn; + struct htx *htx; + struct htx_blk *blk; + char vbuf[DA_MAX_HEADERS][1024] = {{ 0 }}; + int i, nbh = 0; + + if (global_deviceatlas.daset == 0) { + return 0; + } + + da_haproxy_checkinst(); + + chn = (smp->strm ? &smp->strm->req : NULL); + htx = smp_prefetch_htx(smp, chn, NULL, 1); + if (!htx) + return 0; + + i = 0; + for (blk = htx_get_first_blk(htx); nbh < DA_MAX_HEADERS && blk; blk = htx_get_next_blk(htx, blk)) { + size_t vlen; + char *pval; + da_evidence_id_t evid; + enum htx_blk_type type; + struct ist n, v; + char hbuf[24] = { 0 }; + char tval[1024] = { 0 }; + + type = htx_get_blk_type(blk); + + if (type == HTX_BLK_HDR) { + n = htx_get_blk_name(htx, blk); + v = htx_get_blk_value(htx, blk); + } else if (type == HTX_BLK_EOH) { + break; + } else { + continue; + } + + /* The HTTP headers used by the DeviceAtlas API are not longer */ + if (n.len >= sizeof(hbuf)) { + continue; + } + + memcpy(hbuf, n.ptr, n.len); + hbuf[n.len] = 0; + pval = v.ptr; + vlen = v.len; + evid = -1; + i = v.len > sizeof(tval) - 1 ? sizeof(tval) - 1 : v.len; + memcpy(tval, v.ptr, i); + tval[i] = 0; + pval = tval; + + if (strcasecmp(hbuf, "Accept-Language") == 0) { + evid = da_atlas_accept_language_evidence_id(&global_deviceatlas.atlas); + } else if (strcasecmp(hbuf, "Cookie") == 0) { + char *p, *eval; + size_t pl; + + eval = pval + vlen; + /** + * The cookie value, if it exists, is located between the current header's + * value position and the next one + */ + if (http_extract_cookie_value(pval, eval, global_deviceatlas.cookiename, + global_deviceatlas.cookienamelen, 1, &p, &pl) == NULL) { + continue; + } + + vlen -= global_deviceatlas.cookienamelen - 1; + pval = p; + evid = da_atlas_clientprop_evidence_id(&global_deviceatlas.atlas); + } else { + evid = da_atlas_header_evidence_id(&global_deviceatlas.atlas, hbuf); + } + + if (evid == -1) { + continue; + } + + i = vlen > sizeof(vbuf[nbh]) - 1 ? sizeof(vbuf[nbh]) - 1 : vlen; + memcpy(vbuf[nbh], pval, i); + vbuf[nbh][i] = 0; + ev[nbh].key = evid; + ev[nbh].value = vbuf[nbh]; + ++ nbh; + } + + status = da_searchv(&global_deviceatlas.atlas, &devinfo, + ev, nbh); + + return status != DA_OK ? 0 : da_haproxy(args, smp, &devinfo); +} + +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 }, + { CFG_GLOBAL, "deviceatlas-properties-cookie", da_properties_cookie }, + { 0, NULL, NULL }, +}}; + +INITCALL1(STG_REGISTER, cfg_register_keywords, &dacfg_kws); + +/* Note: must not be declared <const> as its list will be overwritten */ +static struct sample_fetch_kw_list fetch_kws = {ILH, { + { "da-csv-fetch", da_haproxy_fetch, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV }, + { NULL, NULL, 0, 0, 0 }, +}}; + +INITCALL1(STG_REGISTER, sample_register_fetches, &fetch_kws); + +/* Note: must not be declared <const> as its list will be overwritten */ +static struct sample_conv_kw_list conv_kws = {ILH, { + { "da-csv-conv", da_haproxy_conv, ARG12(1,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR,STR), NULL, SMP_T_STR, SMP_T_STR }, + { NULL, NULL, 0, 0, 0 }, +}}; + +static void da_haproxy_register_build_options() +{ + char *ptr = NULL; + +#ifdef MOBI_DA_DUMMY_LIBRARY + memprintf(&ptr, "Built with DeviceAtlas support (dummy library only)."); +#else + memprintf(&ptr, "Built with DeviceAtlas support (library version %u.%u).", MOBI_DA_MAJOR, MOBI_DA_MINOR); +#endif + hap_register_build_opts(ptr, 1); +} + +INITCALL1(STG_REGISTER, sample_register_convs, &conv_kws); + +REGISTER_POST_CHECK(init_deviceatlas); +REGISTER_POST_DEINIT(deinit_deviceatlas); +INITCALL0(STG_REGISTER, da_haproxy_register_build_options); diff --git a/addons/deviceatlas/dadwsch.c b/addons/deviceatlas/dadwsch.c new file mode 100644 index 0000000..e35566a --- /dev/null +++ b/addons/deviceatlas/dadwsch.c @@ -0,0 +1,195 @@ +#define _GNU_SOURCE +#include <dac.h> +#include <dadwcurl.h> +#include <dadwarc.h> +#include <getopt.h> +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> + +#define ATLASTOKSZ PATH_MAX +#define ATLASMAPNM "/hapdeviceatlas" + +const char *__pgname; + +static struct { + da_dwatlas_t o; + int ofd; + void* atlasmap; +} global_deviceatlassch = { + .ofd = -1, + .atlasmap = NULL +}; + + +void usage(void) +{ + fprintf(stderr, "%s -u download URL [-d hour (in H:M:S format) current hour by default] [-p path for the downloaded file, /tmp by default]\n", __pgname); + exit(EXIT_FAILURE); +} + +static size_t jsonread(void *ctx, size_t count, char *buf) +{ + return fread(buf, 1, count, ctx); +} + +static da_status_t jsonseek(void *ctx, off_t pos) +{ + return fseek(ctx, pos, SEEK_SET) != -1 ? DA_OK : DA_SYS; +} + +static void dadwlog(dw_config_t cfg, const char* msg) +{ + time_t now = time(NULL); + char buf[26] = {0}; + ctime_r(&now, buf); + buf[24] = 0; + fprintf(stderr, "%s: %s\n", buf, msg); +} + +static dw_status_t dadwnot(void *a, dw_config_t *cfg) +{ + da_dwatlas_t *o = (da_dwatlas_t *)a; + if (!o) + return DW_ERR; + char *e; + char jsondbuf[26] = {0}, buf[26] = {0}, atlasp[ATLASTOKSZ] = {0}; + time_t now = time(NULL); + time_t jsond; + int fd = -1; + (void)a; + jsond = da_getdatacreation(&o->atlas); + dwgetfinalp(o->dcfg.info, atlasp, sizeof(atlasp)); + ctime_r(&jsond, jsondbuf); + ctime_r(&now, buf); + jsondbuf[24] = 0; + buf[24] = 0; + + printf("%s: data file generated on `%s`\n", buf, jsondbuf); + int val = 1; + unsigned char *ptr = (unsigned char *)global_deviceatlassch.atlasmap; + memset(ptr, 0, sizeof(atlasp)); + strcpy(ptr, atlasp); + return DW_OK; +} + +static da_status_t dadwinit(void) +{ + if ((global_deviceatlassch.ofd = shm_open(ATLASMAPNM, O_RDWR | O_CREAT, 0660)) == -1) { + fprintf(stderr, "%s\n", strerror(errno)); + return DA_SYS; + } + + if (ftruncate(global_deviceatlassch.ofd, ATLASTOKSZ) == -1) { + close(global_deviceatlassch.ofd); + return DA_SYS; + } + lseek(global_deviceatlassch.ofd, 0, SEEK_SET); + global_deviceatlassch.atlasmap = mmap(0, ATLASTOKSZ, PROT_READ | PROT_WRITE, MAP_SHARED, global_deviceatlassch.ofd, 0); + if (global_deviceatlassch.atlasmap == MAP_FAILED) { + fprintf(stderr, "%s\n", strerror(errno)); + return DA_SYS; + } else { + memset(global_deviceatlassch.atlasmap, 0, ATLASTOKSZ); + return DA_OK; + } +} + +static void dadwexit(int sig __attribute__((unused)), siginfo_t *s __attribute__((unused)), void *ctx __attribute__((unused))) +{ + ssize_t w; + + fprintf(stderr, "%s: exit\n", __pgname); + dw_daatlas_close(&global_deviceatlassch.o); + da_fini(); + munmap(global_deviceatlassch.atlasmap, ATLASTOKSZ); + close(global_deviceatlassch.ofd); + shm_unlink(ATLASMAPNM); + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + const char *opts = "u:p:d:h"; + bool dset = false; + size_t i; + int ch; + + da_property_decl_t extraprops[1] = { + { 0, 0 } + }; + + __pgname = argv[0]; + + dw_df_dainit_fn = curldwinit; + dw_df_dacleanup_fn = curldwcleanup; + + da_init(); + memset(&global_deviceatlassch.o.dcfg, 0, sizeof(global_deviceatlassch.o.dcfg)); + while ((ch = getopt(argc, argv, opts)) != -1) { + switch (ch) { + case 'u': + global_deviceatlassch.o.dcfg.info.url = strdup(optarg); + break; + case 'p': + global_deviceatlassch.o.dcfg.info.path = strdup(optarg); + break; + case 'd': + if (strptime(optarg, "%H:%M:%S", &global_deviceatlassch.o.dcfg.info.rtm) != NULL) + dset = true; + else + usage(); + break; + case 'h': + default: + usage(); + } + } + + if (!dset) { + time_t now = time(NULL); + struct tm *cnow = gmtime(&now); + memcpy(&global_deviceatlassch.o.dcfg.info.rtm, cnow, offsetof(struct tm, tm_mday)); + } + + if (!global_deviceatlassch.o.dcfg.info.url) + usage(); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sa.sa_sigaction = dadwexit; + + global_deviceatlassch.o.dcfg.info.datatm = 1; + global_deviceatlassch.o.dcfg.info.chksum = 1; + global_deviceatlassch.o.dcfg.info.reload = 1; + global_deviceatlassch.o.dcfg.info.tobin = 1; + global_deviceatlassch.o.dcfg.ep = extraprops; + global_deviceatlassch.o.dcfg.dwproc = curldwproc; + global_deviceatlassch.o.dcfg.dwextract = dadwextract; + global_deviceatlassch.o.dcfg.lptr = (void *)stderr; + global_deviceatlassch.o.dcfg.dwlog = &dadwlog; + global_deviceatlassch.o.dcfg.dwnotify_n = &dadwnot; + global_deviceatlassch.o.rfn = jsonread; + global_deviceatlassch.o.posfn = jsonseek; + + if (dadwinit() != DA_OK) { + fprintf(stderr, "%s init failed\n", __pgname); + exit(EXIT_FAILURE); + } + + if (da_atlas_open_schedule(&global_deviceatlassch.o) != DA_OK) { + fprintf(stderr, "%s scheduling failed\n", __pgname); + exit(EXIT_FAILURE); + } + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + while (true) sleep(1); + + return 0; +} diff --git a/addons/deviceatlas/dummy/Makefile b/addons/deviceatlas/dummy/Makefile new file mode 100644 index 0000000..8bba840 --- /dev/null +++ b/addons/deviceatlas/dummy/Makefile @@ -0,0 +1,12 @@ +# makefile for dummy DeviceAtlas library +# +# To enable the DeviceAtlas module support, the following are needed +# make TARGET=<target> DEVICEATLAS_SRC=addons/deviceatlas/dummy USE_PCRE=1 USE_DEVICEATLAS=1 + +build: libda.a + +libda.a: dac.o + ar rv $@ $< + +clean: + rm -rf *.a *.o diff --git a/addons/deviceatlas/dummy/Os/daunix.c b/addons/deviceatlas/dummy/Os/daunix.c new file mode 100644 index 0000000..ca696f9 --- /dev/null +++ b/addons/deviceatlas/dummy/Os/daunix.c @@ -0,0 +1,9 @@ +#include "dac.h" + +static char const __attribute__((unused)) rcsid[] = "$Id: dac.c, v dummy 1970/01/01 00:00:01 dcarlier Exp $"; + +da_status_t +da_atlas_read_mapped(const char *path, void *m, void **p, size_t *l) +{ + return DA_SYS; +} diff --git a/addons/deviceatlas/dummy/dac.c b/addons/deviceatlas/dummy/dac.c new file mode 100644 index 0000000..720dc6a --- /dev/null +++ b/addons/deviceatlas/dummy/dac.c @@ -0,0 +1,222 @@ +#include "dac.h" +#include <stdlib.h> +#include <string.h> +#include <time.h> + +static char const __attribute__((unused)) rcsid[] = "$Id: dac.c, v dummy 1970/01/01 00:00:01 dcarlier Exp $"; + +struct da_bitset { + unsigned long bits[8]; + size_t bit_count; +}; + +/* + * Constructor/Destructor for possible globals. + */ + +void +da_init() +{ +} + +void +da_fini() +{ +} + + +void +da_seterrorfunc(da_errorfunc_t callback) +{ +} + +const char * +da_typename(da_type_t fieldtype) +{ + return "none"; +} + +char * +da_getdataversion(da_atlas_t *atlas) +{ + return "dummy library version 1.0"; +} + +time_t +da_getdatacreation(da_atlas_t *atlas) +{ + return time(NULL); +} + +int +da_getdatarevision(da_atlas_t *atlas) +{ + return 1; +} + +da_status_t +da_atlas_compile(void *ctx, da_read_fn readfn, da_setpos_fn rewind, void **ptr, size_t *size) +{ + return DA_OK; +} + +da_status_t +da_atlas_open(da_atlas_t *atlas, da_property_decl_t *extraprops, const void *ptr, size_t len) +{ + void *ptr2 = malloc(len); + free(ptr2); + return ptr2 ? DA_OK : DA_NOMEM; +} + +void +da_atlas_close(da_atlas_t *atlas) +{ +} + +da_evidence_id_t +da_atlas_clientprop_evidence_id(const da_atlas_t *atlas) +{ + return (da_evidence_id_t)2; +} + +da_evidence_id_t +da_atlas_accept_language_evidence_id(const da_atlas_t *atlas) +{ + return (da_evidence_id_t)3; +} + +da_evidence_id_t +da_atlas_header_evidence_id(const da_atlas_t *atlas, const char *evidence_name) +{ + return (da_evidence_id_t)1; +} + +da_status_t +da_atlas_getproptype(const da_atlas_t *atlas, da_propid_t propid, da_type_t *type) +{ + *type = DA_TYPE_BOOLEAN; + return DA_OK; +} + +da_status_t +da_atlas_getpropname(const da_atlas_t *atlas, da_propid_t propid, const char **name) +{ + *name = "isRobot"; + return DA_OK; +} + +da_status_t +da_atlas_getpropid(const da_atlas_t *atlas, const char *propname, da_propid_t *property) +{ + *property = (da_propid_t)1; + return DA_OK; +} + +size_t +da_atlas_getpropcount(const da_atlas_t *atlas) +{ + return 1; +} + +void +da_atlas_setconfig(da_atlas_t *atlas, da_config_t *config) +{ +} + +da_status_t +da_searchv(const da_atlas_t *atlas, da_deviceinfo_t *result, da_evidence_t *evidence, size_t count) +{ + memset(result, 0, sizeof(*result)); + result->propcount = count; + return DA_OK; +} + +da_status_t +da_search(const da_atlas_t *atlas, da_deviceinfo_t *result, ...) +{ + da_evidence_t vec[4]; /* XXX: this will have to grow if more evidence is supported. */ + size_t i; + va_list args; + va_start(args, result); + for (i = 0; i < sizeof vec / sizeof vec[0];) { + vec[i].key = va_arg(args, da_evidence_id_t); + if (vec[i].key == 0) + break; + vec[i++].value = va_arg(args, char *); + } + va_end(args); + return da_searchv(atlas, result, vec, i); +} + +/* + * Search-result centric functions. + */ +size_t +da_getpropcount(const da_deviceinfo_t *info) +{ + return info->propcount; +} + +da_status_t +da_getfirstprop(const da_deviceinfo_t *info, da_propid_t **propid) +{ + if (info->propcount == 0) + return DA_NOMORE; + *propid = &info->proplist[0]; + return DA_OK; +} + +da_status_t +da_getnextprop(const da_deviceinfo_t *info, da_propid_t **propid) +{ + if (*propid - info->proplist >= info->propcount - 1) + return DA_NOMORE; + ++*propid; + return DA_OK; +} + +void +da_close(da_deviceinfo_t *sr) +{ +} + +da_status_t +da_getpropname(const da_deviceinfo_t *info, da_propid_t propid, const char **name) +{ + *name = "isRobot"; + return DA_OK; +} + +da_status_t +da_getproptype(const da_deviceinfo_t *info, da_propid_t propid, da_type_t *type) +{ + *type = DA_TYPE_BOOLEAN; + return DA_OK; +} + +da_status_t +da_getpropinteger(const da_deviceinfo_t *info, da_propid_t property, long *vp) +{ + *vp = -1; + return DA_OK; +} + +da_status_t +da_getpropstring(const da_deviceinfo_t *info, da_propid_t property, const char **vp) +{ + *vp = NULL; + return DA_OK; +} + +da_status_t +da_getpropboolean(const da_deviceinfo_t *info, da_propid_t property, bool *vp) +{ + *vp = true; + return DA_OK; +} + +const char * +da_get_property_name(const da_atlas_t *atlas, da_propid_t property) +{ + return "isRobot"; +} diff --git a/addons/deviceatlas/dummy/dac.h b/addons/deviceatlas/dummy/dac.h new file mode 100644 index 0000000..bf166ae --- /dev/null +++ b/addons/deviceatlas/dummy/dac.h @@ -0,0 +1,600 @@ +#ifndef MOBI_DA_DAC_H +#define MOBI_DA_DAC_H + +/** + * @file dac.h + * @author Afilias Technologies + * + * @brief API main header file + */ + +#include <sys/types.h> +#include <limits.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdarg.h> + +#ifndef __cplusplus +#ifndef true +#ifdef HAVE_NO_BUILTIN__BOOL +typedef int _Bool; +#endif +#define bool _Bool + +#define true 1 +#define false 0 +#endif +#endif + +#define MOBI_DA_MAJOR 2 +#define MOBI_DA_MINOR 1 +#define MOBI_DA_DUMMY_LIBRARY 1 + + +/** + * @brief All values returned by the API have one of these types. + * da_getprop*() return data in the appropriate C type for the given da_type. + */ +enum da_type { + DA_TYPE_NONE, + DA_TYPE_BOOLEAN, + DA_TYPE_INTEGER, + DA_TYPE_NUMBER, + DA_TYPE_STRING, + DA_TYPE_ARRAY, + DA_TYPE_OBJECT, + DA_TYPE_NULL +}; + +/** + * Any method that returns a da_status may potentially fail for one of these reasons. + * XXX: Error reporting needs to be improved. + */ +enum da_status { + DA_OK, /* Success. */ + DA_INVALID_JSON, /* The JSON format is invalid, or the content is unexpected in a given context. */ + DA_OVERFLOW, /* Overflow occurred. Note this is used to indicate an unfinished string parse in JSON */ + DA_FORMAT_ERROR, /* The data supplied is formatted incorrectly. */ + DA_NOMEM, /* There was not enough space to complete the operation */ + DA_SYS, /* A system error occurred - consult the OS for more details (eg, check errno) */ + DA_NOTIMPL, /* This method is not implemented */ + DA_NOTFOUND, /* The requested item was not found. */ + DA_REGEXBAD, /* An invalid regex was provided. */ + DA_NOMORE, /* Used to indicate the end of an iterator. */ + DA_INVALID_COOKIE, /* Cookie value supplied was invalid */ + DA_INVALID_TYPE, /* A value of an unexpected type was found. */ + DA_INTERNAL_ERROR, + DA_STATUS_LAST /* Placeholder to indicate highest possible error value. (value will change as API matures) */ +}; + +enum da_severity { + DA_SEV_FATAL, /* The operation will not continue, and the operation will return an error. */ + DA_SEV_ERROR, /* An error occurred, but the API call will return at least some valid information */ + DA_SEV_WARN, /* An unexpected event occurred, but the system dealt with it */ + DA_SEV_INFO /* An informational message. */ +}; +/* Forward references to tagged types */ +struct atlas_image; +struct da_atlas; +struct da_deviceinfo; +struct da_jsonparser; +struct da_node; +struct da_propset; +union da_value; +struct da_evidence; +struct da_bitset; +struct da_allocator; +struct da_config; + +/** + * @brief Primary types of the interface. + * Primary types used by API client. + * Non-typedef structures and unions are considered private to the API. + * + */ +typedef enum da_severity da_severity_t; /* A severity for the error callback. */ +typedef enum da_status da_status_t; /* An error code - returned from most API calls. */ +typedef da_status_t (*da_setpos_fn)(void *ctx, off_t off); /* callback provided to API to rewind input stream */ +typedef enum da_type da_type_t; /* A value type (integer, string, etc) */ + +/** + * @brief An operation on an atlas involves converting a set of evidence strings into a set of property/value pairs. + * The ID for a particular type of evidence is extract from the atlas (eg, for a specific HTTP header, use: + * + * da_evidence_id_t evidence = da_atlas_header_evidence_id(atlas, "User-Agent"); + * + */ +typedef int da_evidence_id_t; + +/** + * @brief The search result encompasses a key/value set. Keys are handles retrieved via + * _either_ da_atlas_getpropid() or da_getpropid(). + * Some search results may have keys not available when the atlas is opened (eg, + * when the name of the property itself is contained within the evidence) + * Such properties by necessity are given a "local" da_propid_t + * + * You can ensure any properties you are interested in get a global propid by + * passing a list of interesting named properties to da_atlas_open() + */ +typedef int da_propid_t; +typedef size_t (*da_read_fn)(void *ctx, size_t maxlen, char *ptr); +typedef struct da_atlas da_atlas_t; +typedef struct da_deviceinfo da_deviceinfo_t; +typedef struct da_evidence da_evidence_t; +typedef struct da_jsonparser da_jsonparser_t; +typedef struct da_node da_node_t; +typedef struct da_property_decl da_property_decl_t; +typedef struct da_propset da_propset_t; +typedef struct da_config da_config_t; +typedef void *(*da_alloc_fn)(void *ctx, size_t); +typedef void (*da_free_fn)(void *ctx, void *); +typedef void *(*da_realloc_fn)(void *ctx, void *, size_t); +typedef void (*da_errorfunc_t)(da_severity_t severity, da_status_t status, const char *msg, va_list args); + + +/* Manifest constants. */ +enum { + /* + * used as the initial guess for the compiled size of an atlas. + * If atlas sizes grow more beyond this, it can be expanded to avoid multiple scans of the data. + */ + DA_INITIAL_MEMORY_ESTIMATE = 1024 * 1024 * 14 +}; + +struct da_config { + unsigned int ua_props; + unsigned int lang_props; + unsigned int __reserved[14]; /* enough reserved keywords for future use */ +}; + +/** + * Functional interface. + */ + +/** + * @brief Initialize process to use the DA API. + */ +void da_init(void); + + +/** + * @brief Release all resources used by the API + */ +void da_fini(void); + +/** + * @brief User-supplied callback to be invoked with information about an error. + * Note this may use thread-local storage etc to store the info on return from the current call + * It is guaranteed that an error-reporting function returning an error-code will have called + * this function at least once. + * @param callback function + */ +void da_seterrorfunc(da_errorfunc_t callback); + +/** + * @brief Given a specific HTTP header, return the associated ID for that header. + * When passing evidence to the API, its type is identified using its da_evidince_id_t. + * @param atlas atlas instance + * @param header_name Header's name + * @return evidence id + */ +da_evidence_id_t da_atlas_header_evidence_id(const da_atlas_t *atlas, const char *header_name); +/** + * @brief Return the associated ID of the client side properties evidence + * @param atlas Atlas instance + * @return evidence id + */ +da_evidence_id_t da_atlas_clientprop_evidence_id(const da_atlas_t *atlas); +/** + * @brief Return the associated ID of the accept language header evidence + * @param atlas Atlas instance + * @return evidence id + */ +da_evidence_id_t da_atlas_accept_language_evidence_id(const da_atlas_t *atlas); + +/** + * @brief readfn should present JSON content from ctx. + * atlasp points to an uninitialized da_atlas structure. + * Result is a compiled atlas at atlasp. + * Result is allocated via normal memory-allocation methods, malloc/calloc/realloc, so should be + * Free'd with free() + * XXX TODO: Change this to take a da_allocator + * @param ctx pointer given to read the json file + * @param readfn function pointer, set accordingly to the attended given pointer + * @param setposfn function pointer + * @param ptr Pointer dynamically allocated if the json parsing happened normally + * @param len size of the atlas image + * @return status of atlas compilation + */ +da_status_t da_atlas_compile(void *ctx, da_read_fn readfn, da_setpos_fn setposfn, void **ptr, size_t *len); + +/** + * @brief opens a previously compiled atlas for operations. extra_props will be available in calls to + * da_getpropid on the atlas, and if generated by the search, the ID will be consistent across + * different calls to search. + * Properties added by a search that are neither in the compiled atlas, nor in the extra_props list + * Are assigned an ID within the context that is not transferrable through different search results + * within the same atlas. + * @param atlas Atlas instance + * @param extra_props properties + * @param ptr given pointer from previously compiled atlas + * @param pos atlas image size + * @return status of atlas data opening + */ +da_status_t da_atlas_open(da_atlas_t *atlas, da_property_decl_t *extra_props, const void *ptr, size_t pos); + +/** + * @brief read from a mapped data which then replace da_atlas_compile call + * + * @param dumppath, anonymous if NULL + * @param map for anonymous, it is the responsibility of the caller to unmap it, ignored otherwise + * @param maplen for anonymous, it is the size of the mapped data, ignored otherwise + * @param ptr Pointer dynamically allocated if the mapping happened normally + * @param len size of the atlas image + * @return status of mapping + */ +da_status_t da_atlas_read_mapped(const char *path, void *m, void **p, size_t *l); +/** + * @brief Release any resources associated with the atlas structure atlas, which was previously generated from + * da_read_atlas or da_compile_atlas. + * @param atlas instance + */ +void da_atlas_close(da_atlas_t *atlas); + +/** + * @brief Find device properties given a set of evidence. + * Search results are returned in da_deviceinfo_t, and must be cleaned using da_close + * "Evidence" is an array of length count, of string data tagged with an evidence ID. + * @param atlas Atlas instance + * @param info Device info + * @param ev Array of evidences + * @param count Number of evidence given + * @return status of the search + */ +da_status_t da_searchv(const da_atlas_t *atlas, da_deviceinfo_t *info, da_evidence_t *ev, size_t count); + +/** + * @brief As da_search, but unrolls the evidence array into variable arguments for simpler calling + * convention with known evidence types. + * varargs are pairs of (da_evidence_id, string), terminated with da_evidence_id DA_END + * @code da_search(&myAtlas, &deviceInfo, da_get_header_evidence_id("User-Agent"), + * "Mozilla/5.0 (Linux...", DA_END); + * @endcode + * @param atlas Atlas instance + * @param info given device info which holds on device properties + * @param pairs of evidence id / evidence value + * @return status of the search + */ +da_status_t da_search(const da_atlas_t *atlas, da_deviceinfo_t *info, ...); + +/** + * @brief After finishing with a search result, release resources associated with it. + * @param info Device info previously allocated by search functions + */ +void da_close(da_deviceinfo_t *info); + +/** + * @brief Given a property name (Eg, "displayWidth"), return the property ID associated with it for the + * specified atlas. + * @param atlas Atlas instance + * @param propname Property name + * @param propid Property id + * @return status of the property id search + */ +da_status_t da_atlas_getpropid(const da_atlas_t *atlas, const char *propname, da_propid_t *propid); + +/** + * @brief Given a property ID, return the type of that property. + * @code + * da_getproptype(&myAtlas, da_getpropid(&myAtlas, "displayWidth"), &propertyType); + * assert(propertyType == DA_TYPE_INT); + * @endcode + * @param atlas Atlas instance + * @param propid Property id + * @param type Type id of the property + * @return status of the type id search + */ +da_status_t da_atlas_getproptype(const da_atlas_t *atlas, da_propid_t propid, da_type_t *type); + +/** + * @brief Given a property ID, return the name of that property. + * @code + * da_atlas_getpropname(&myAtlas, da_getpropid(&myAtlas, "displayWidth"), &propertyName); + * assert(strcmp("displayWidth", propertyName) == 0); + * @endcode + * @param atlas Atlas instance + * @param propid property id + * @param propname property name returned + * @return status of the property name search + */ +da_status_t da_atlas_getpropname(const da_atlas_t *atlas, da_propid_t propid, const char **propname); + + +/** + * @brief Given an atlas instance, return its counters + the builtins + * @code + * da_atlas_getpropcount(&myAtlas); + * @endcode + * @param atlas Atlas instance + * @return counters + */ +size_t da_atlas_getpropcount(const da_atlas_t *atlas); + +/** + * @brief Given an atlas instance, set the detection config + * @param atlas Atlas instance + * @param config instance + */ +void da_atlas_setconfig(da_atlas_t *atlas, da_config_t *config); + +/** + * @brief Given a search result, find the value of a specific property. + * @code + * long displayWidth; // width of display in pixels. + * da_getpropinteger(&deviceInfo, da_getpropid(&myAtlas, "displayWidth"), &displayWidth); + * @endcode + * String contents are owned by the search result, and are valid until the search is closed. + */ +/** + * @brief returns a property value as a string from a given string typed property id + * @param info Device info + * @param propid Property id + * @param value Value of the property + * @return status of property value search + */ +da_status_t da_getpropstring(const da_deviceinfo_t *info, da_propid_t propid, const char **value); +/** + * @brief returns a property value as a long from a given long typed property id + * @param info Device info + * @param propid Property id + * @param value Value of the property + * @return status of property value search + */ +da_status_t da_getpropinteger(const da_deviceinfo_t *info, da_propid_t propid, long *value); +/** + * @brief returns a property value as a boolean from a given boolean typed property id + * @param info Device info + * @param propid Property id + * @param value Value of the property + * @return status of property value search + */ +da_status_t da_getpropboolean(const da_deviceinfo_t *info, da_propid_t propid, bool *value); +/** + * @brief returns a property value as a float from a given float typed property id + * @param info Device info + * @param propid Property id + * @param value Value of the property + * @return status of property value search + */ +da_status_t da_getpropfloat(const da_deviceinfo_t *info, da_propid_t propid, double *value); + +/** + * @brief Some properties may not be not known to the atlas before the search commences. + * Such properties cannot have a da_propid_t assigned to them on the atlas, but will + * have a local property assigned during search. The name and type of such properties + * can be discovered here. + * + * Properties that are used in the atlas source and properties specifically registered + * with da_atlas_open() will always be assigned to a property discovered during search. + * Therefore, if there are specific properties that you want to use, and are unsure + * if they are in your device atlas source, registering them with da_atlas_open will + * make access to them easier and more efficient + */ +/** + * @brief returns the type of a given device property from the search functions + * @param info Device info + * @param propid Property id + * @param type Type id + * @return status of property type search + */ +da_status_t da_getproptype(const da_deviceinfo_t *info, da_propid_t propid, da_type_t *type); +/** + * @brief returns the name of a given device property from the search functions + * @param info Device info + * @param propid Property id + * @param propname Property name + * @return status of property type search + */ +da_status_t da_getpropname(const da_deviceinfo_t *info, da_propid_t propid, const char **propname); + +/** + * @brief da_getfirstprop/da_getnextprop provide iteration over all properties + * in a search result. + * Both will return DA_OK if there is a result available, and DA_NOMORE + * if the search is complete. + * @code + * + * da_propid_t *propidp; + * for (da_status_t status = da_getfirstprop(&result, &propidp); + * status == DA_OK; + * status = da_getnextprop(&result, &propidp)) { + * const char *propname; + * if (da_getpropname(&result, *propidp, &propname) == DA_OK) + * fprintf("found property %s\n", propname); + * } + * @endcode + */ + +/** + * @brief returns the first property from device info + * @param info Device info + * @param propid Property + * @return status + */ +da_status_t da_getfirstprop(const da_deviceinfo_t *info, da_propid_t **propid); +/** + * @brief device info properties iterator + * @param info Device info + * @param propid Property + * @return status + */ +da_status_t da_getnextprop(const da_deviceinfo_t *info, da_propid_t **propid); + +/** + * @brief Report an error, as per a report from the API to the user-callback. + * @param severity Severity level of the error + * @param fmt format error message + * @param va_list + * @return status + */ +da_status_t da_reporterror(da_status_t severity, const char *fmt, ...); + +/** + * @brief returns a textual description of the type "type". + * @param type Type id + * @return type name + */ +const char *da_typename(da_type_t type); + +/** + * @brief returns the version from the JSON in memory + * @param atlas + * @return version + */ +char *da_getdataversion(da_atlas_t *atlas); + +/** + * @brief returns the date creation's timestamp from the JSON in memory + * @param atlas + * @return version + */ +time_t da_getdatacreation(da_atlas_t *atlas); + +/** + * @brief returns the revision's number from the JSON in memory + * @param atlas + * @return version + */ +int da_getdatarevision(da_atlas_t *atlas); + +/** + * @brief returns the name of a global property + * @param atlas Atlas instance + * @param propid Property id + * @return property name + */ +const char *da_get_property_name(const da_atlas_t *atlas, da_propid_t propid); + +/** + * @brief returns the number of properties in a result. + * @param info Device info + * @return properties count + */ +size_t da_getpropcount(const da_deviceinfo_t *info); + +/* + * Details below should not be required for usage of the API + */ + +/** + * @brief Represents a usable device atlas interface. + * + * No user servicable parts inside: access should + * be via the functional API. + */ +struct da_atlas { + const struct atlas_image *image; + struct header_evidence_entry *header_priorities; + size_t header_evidence_count; + + struct pcre_regex_info *uar_regexes; + size_t uar_regex_count; + + struct pcre_regex_info *replacement_regexes; + size_t replacement_regex_count; + + da_evidence_id_t user_agent_evidence; + da_evidence_id_t clientprops_evidence; + da_evidence_id_t accept_language_evidence; + da_evidence_id_t next_evidence; + + da_propset_t *properties; + da_propid_t id_propid; + da_propid_t id_proplang; + da_propid_t id_proplang_locale; + + da_config_t config; + + da_deviceinfo_t **cpr_props; + size_t cpr_count; +}; + +/* fixed constants. */ +enum { + DA_BUFSIZE = 16000 +}; + +/** + * Represents a chunk of memory. See comments on da_deviceinfo. + * This is presented here to allow aggregation in da_deviceinfo: + * Not for public consumption. + */ +struct da_buf { + struct da_buf *next; + char *cur; + char *limit; + char buf[DA_BUFSIZE]; +}; + +/** + * A callback interface for allocating memory from some source + * Not for public consumption. + */ +struct da_allocator { + da_alloc_fn alloc; + da_free_fn free; + da_realloc_fn realloc; + void *context; +}; + + +/** + * Represents a search result + * Can be used to retrieve values of known properties discovered from the evidence, + * iterate over the properties with known values, and query property types that are + * local to this result. + * + * The atlas the search is carried out on must survive any da_deviceinfo results + * it provides. + */ +struct da_deviceinfo { + struct da_allocator allocator; + const da_atlas_t *atlas; /* reference to the atlas the search was carried out on. */ + struct da_bitset *present; /* property received from tree */ + struct da_bitset *localprop; /* property was received from UAR rule or CPR */ + struct da_bitset *cprprop; /* property was received from CPR */ + union da_value *properties; /* properties - indexed by property id. */ + da_propid_t *proplist; /* list of properties present in this result. */ + size_t propcount; /* size of proplist */ + da_propset_t *local_types; /* property descriptors local to this search result. */ + + /** + * The per-deviceinfo heap is stored here. Allocations for data in the result + * come from the raw data in these buffers. The size of the fixed-size buffer + * built in to da_buf is sized such that all known search results will not + * require memory allocation via malloc() + */ + struct da_buf *heap; + struct da_buf initial_heap; +}; + +/** + * Used to pass evidence to da_searchv() + */ +struct da_evidence { + da_evidence_id_t key; + char *value; +}; + +/** + * Used to pass properties the API intends to query to the da_atlas_open function + * This can be used to improve performance of lookup on properties well-known + * to the API user, but not present in the JSON database. + */ +struct da_property_decl { + const char *name; + da_type_t type; +}; + + +#endif /* DEVATLAS_DAC_H */ diff --git a/addons/deviceatlas/dummy/dadwcom.c b/addons/deviceatlas/dummy/dadwcom.c new file mode 100644 index 0000000..53c5fdf --- /dev/null +++ b/addons/deviceatlas/dummy/dadwcom.c @@ -0,0 +1 @@ +#include <stdio.h> diff --git a/addons/deviceatlas/dummy/dasch.c b/addons/deviceatlas/dummy/dasch.c new file mode 100644 index 0000000..53c5fdf --- /dev/null +++ b/addons/deviceatlas/dummy/dasch.c @@ -0,0 +1 @@ +#include <stdio.h> diff --git a/addons/deviceatlas/dummy/json.c b/addons/deviceatlas/dummy/json.c new file mode 100644 index 0000000..53c5fdf --- /dev/null +++ b/addons/deviceatlas/dummy/json.c @@ -0,0 +1 @@ +#include <stdio.h> |