747 lines
23 KiB
C
747 lines
23 KiB
C
/*
|
|
Copyright (c) 2013 Anton Titov.
|
|
|
|
Copyright (c) 2013 pCloud Ltd. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met: Redistributions of source code must retain the above
|
|
copyright notice, this list of conditions and the following
|
|
disclaimer. Redistributions in binary form must reproduce the above
|
|
copyright notice, this list of conditions and the following
|
|
disclaimer in the documentation and/or other materials provided with
|
|
the distribution. Neither the name of pCloud Ltd nor the names of
|
|
its contributors may be used to endorse or promote products derived
|
|
from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL pCloud Ltd BE
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
|
|
#define MAX_API_RESPONSE_SIZE (256 * 1024 * 1024) // 256MB
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "papi.h"
|
|
#include "plibs.h"
|
|
#include "psettings.h"
|
|
#include "psynclib.h"
|
|
#include "ptimer.h"
|
|
#include "pdbg.h"
|
|
|
|
/*
|
|
commented definitions are unused, but kept because they may be
|
|
important later.
|
|
*/
|
|
#define RPARAM_STR1 0
|
|
// #define RPARAM_STR2 1
|
|
// #define RPARAM_STR3 2
|
|
#define RPARAM_STR4 3
|
|
#define RPARAM_RSTR1 4
|
|
// #define RPARAM_RSTR2 5
|
|
// #define RPARAM_RSTR3 6
|
|
#define RPARAM_RSTR4 7
|
|
#define RPARAM_NUM1 8
|
|
// #define RPARAM_NUM2 9
|
|
// #define RPARAM_NUM3 10
|
|
// #define RPARAM_NUM4 11
|
|
// #define RPARAM_NUM5 12
|
|
// #define RPARAM_NUM6 13
|
|
// #define RPARAM_NUM7 14
|
|
#define RPARAM_NUM8 15
|
|
#define RPARAM_HASH 16
|
|
#define RPARAM_ARRAY 17
|
|
#define RPARAM_BFALSE 18
|
|
#define RPARAM_BTRUE 19
|
|
#define RPARAM_DATA 20
|
|
/* 100-149 inclusive - strings 0-49 bytes in length without additional len
|
|
* parameter */
|
|
#define RPARAM_SHORT_STR_BASE 100
|
|
/* 150-199 inclusive - reused strings id 0-49 bytes in length without additional
|
|
* id parameter */
|
|
#define RPARAM_SHORT_RSTR_BASE 150
|
|
/* 200-219 inclusive - small numbers 0-19 */
|
|
#define RPARAM_SMALL_NUM_BASE 200
|
|
#define RPARAM_END 255
|
|
|
|
#define VSHORT_STR_LEN 50
|
|
#define VSHORT_RSTR_CNT 50
|
|
#define VSMALL_NUMBER_NUM 20
|
|
|
|
static const binresult BOOL_TRUE = {PARAM_BOOL, 0, {1}};
|
|
static const binresult BOOL_FALSE = {PARAM_BOOL, 0, {0}};
|
|
static const binresult STR_EMPTY = {PARAM_STR, 0, {0}};
|
|
static const binresult NUM_ZERO = {PARAM_NUM, 0, {0}};
|
|
static const binresult HASH_EMPTY = {PARAM_HASH, 0, {0}};
|
|
static const binresult ARRAY_EMPTY = {PARAM_ARRAY, 0, {0}};
|
|
static const binresult DATA_EMPTY = {PARAM_DATA, 0, {0}};
|
|
|
|
static const binresult *empty_types[] = {
|
|
&STR_EMPTY, &NUM_ZERO, &BOOL_FALSE, &ARRAY_EMPTY, &HASH_EMPTY, &DATA_EMPTY};
|
|
static const char *type_names[] = {"string", "number", "boolean",
|
|
"array", "hash", "data"};
|
|
|
|
static const binresult NUM_SMALL[VSMALL_NUMBER_NUM] = {
|
|
{PARAM_NUM, 0, {0}}, {PARAM_NUM, 0, {1}}, {PARAM_NUM, 0, {2}},
|
|
{PARAM_NUM, 0, {3}}, {PARAM_NUM, 0, {4}}, {PARAM_NUM, 0, {5}},
|
|
{PARAM_NUM, 0, {6}}, {PARAM_NUM, 0, {7}}, {PARAM_NUM, 0, {8}},
|
|
{PARAM_NUM, 0, {9}}, {PARAM_NUM, 0, {10}}, {PARAM_NUM, 0, {11}},
|
|
{PARAM_NUM, 0, {12}}, {PARAM_NUM, 0, {13}}, {PARAM_NUM, 0, {14}},
|
|
{PARAM_NUM, 0, {15}}, {PARAM_NUM, 0, {16}}, {PARAM_NUM, 0, {17}},
|
|
{PARAM_NUM, 0, {18}}, {PARAM_NUM, 0, {19}}};
|
|
|
|
static uint32_t connfailures = 0;
|
|
|
|
psock_t *papi_connect(const char *hostname, int usessl) {
|
|
static time_t notuntil = 0;
|
|
psock_t *ret;
|
|
const char *userapi = psync_setting_get_string(_PS(api_server));
|
|
if (ptimer_time() > notuntil || !userapi) {
|
|
ret = psock_connect(
|
|
hostname, usessl ? PSYNC_API_PORT_SSL : PSYNC_API_PORT, usessl);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
if (!userapi || !strcmp(hostname, userapi))
|
|
return NULL;
|
|
ret = psock_connect(
|
|
userapi, usessl ? PSYNC_API_PORT_SSL : PSYNC_API_PORT, usessl);
|
|
if (ret) {
|
|
pdbg_logf(D_NOTICE, "failed to connect to %s, but was able to connect to %s",
|
|
hostname, userapi);
|
|
notuntil = ptimer_time() + 1800;
|
|
}
|
|
return ret;
|
|
}
|
|
return psock_connect(
|
|
userapi, usessl ? PSYNC_API_PORT_SSL : PSYNC_API_PORT, usessl);
|
|
}
|
|
|
|
void papi_conn_fail_inc() { connfailures++; }
|
|
|
|
void papi_conn_fail_reset() {
|
|
if (connfailures % 5 == 4)
|
|
connfailures = 4;
|
|
else
|
|
connfailures = 0;
|
|
}
|
|
|
|
#define _NEED_DATA(cnt) \
|
|
if (pdbg_unlikely(*datalen < (cnt))) \
|
|
return -1
|
|
#define ALIGN_BYTES psync_alignof(uint64_t)
|
|
|
|
static ssize_t calc_ret_len(unsigned char **restrict data,
|
|
size_t *restrict datalen, size_t *restrict strcnt) {
|
|
size_t type, len;
|
|
long cond;
|
|
_NEED_DATA(1);
|
|
type = **data;
|
|
(*data)++;
|
|
(*datalen)--;
|
|
if ((cond = (type >= RPARAM_SHORT_STR_BASE &&
|
|
type < RPARAM_SHORT_STR_BASE + VSHORT_STR_LEN)) ||
|
|
(type >= RPARAM_STR1 && type <= RPARAM_STR4)) {
|
|
if (cond)
|
|
len = type - RPARAM_SHORT_STR_BASE;
|
|
else {
|
|
size_t l = type - RPARAM_STR1 + 1;
|
|
_NEED_DATA(l);
|
|
len = 0;
|
|
memcpy(&len, *data, l);
|
|
*data += l;
|
|
*datalen -= l;
|
|
}
|
|
_NEED_DATA(len);
|
|
*data += len;
|
|
*datalen -= len;
|
|
len = ((len + ALIGN_BYTES) / ALIGN_BYTES) * ALIGN_BYTES;
|
|
(*strcnt)++;
|
|
return offsetof(binresult, str) + len;
|
|
} else if ((cond = (type >= RPARAM_RSTR1 && type <= RPARAM_RSTR4)) ||
|
|
(type >= RPARAM_SHORT_RSTR_BASE &&
|
|
type < RPARAM_SHORT_RSTR_BASE + VSHORT_RSTR_CNT)) {
|
|
if (cond) {
|
|
size_t l = type - RPARAM_RSTR1 + 1;
|
|
_NEED_DATA(l);
|
|
len = 0;
|
|
memcpy(&len, *data, l);
|
|
*data += l;
|
|
*datalen -= l;
|
|
} else
|
|
len = type - RPARAM_SHORT_RSTR_BASE;
|
|
if (len < *strcnt)
|
|
return 0;
|
|
else
|
|
return -1;
|
|
} else if (type >= RPARAM_NUM1 && type <= RPARAM_NUM8) {
|
|
len = type - RPARAM_NUM1 + 1;
|
|
_NEED_DATA(len);
|
|
*data += len;
|
|
*datalen -= len;
|
|
return sizeof(binresult);
|
|
} else if (type >= RPARAM_SMALL_NUM_BASE &&
|
|
type < RPARAM_SMALL_NUM_BASE + VSMALL_NUMBER_NUM)
|
|
return 0;
|
|
else if (type == RPARAM_BFALSE || type == RPARAM_BTRUE)
|
|
return 0;
|
|
else if (type == RPARAM_ARRAY) {
|
|
ssize_t ret, r;
|
|
int unsigned cnt;
|
|
cnt = 0;
|
|
ret = sizeof(binresult);
|
|
_NEED_DATA(1);
|
|
while (**data != RPARAM_END) {
|
|
r = calc_ret_len(data, datalen, strcnt);
|
|
if (r == -1)
|
|
return -1;
|
|
ret += r;
|
|
cnt++;
|
|
_NEED_DATA(1);
|
|
}
|
|
(*data)++;
|
|
(*datalen)--;
|
|
ret += sizeof(binresult *) * cnt;
|
|
return ret;
|
|
} else if (type == RPARAM_HASH) {
|
|
ssize_t ret, r;
|
|
int unsigned cnt;
|
|
cnt = 0;
|
|
ret = sizeof(binresult);
|
|
_NEED_DATA(1);
|
|
while (**data != RPARAM_END) {
|
|
r = calc_ret_len(data, datalen, strcnt);
|
|
if (r == -1)
|
|
return -1;
|
|
ret += r;
|
|
r = calc_ret_len(data, datalen, strcnt);
|
|
if (r == -1)
|
|
return -1;
|
|
ret += r;
|
|
cnt++;
|
|
_NEED_DATA(1);
|
|
}
|
|
(*data)++;
|
|
(*datalen)--;
|
|
ret += sizeof(hashpair) * cnt;
|
|
return ret;
|
|
} else if (type == RPARAM_DATA) {
|
|
_NEED_DATA(8);
|
|
*data += 8;
|
|
*datalen -= 8;
|
|
return sizeof(binresult);
|
|
} else
|
|
return -1;
|
|
}
|
|
|
|
static binresult *do_parse_result(unsigned char **restrict indata,
|
|
unsigned char **restrict odata,
|
|
binresult **restrict strings,
|
|
size_t *restrict nextstrid) {
|
|
binresult *ret;
|
|
long cond;
|
|
unsigned long type, len;
|
|
type = **indata;
|
|
(*indata)++;
|
|
if ((cond = (type >= RPARAM_SHORT_STR_BASE &&
|
|
type < RPARAM_SHORT_STR_BASE + VSHORT_STR_LEN)) ||
|
|
(type >= RPARAM_STR1 && type <= RPARAM_STR4)) {
|
|
if (cond)
|
|
len = type - RPARAM_SHORT_STR_BASE;
|
|
else {
|
|
size_t l = type - RPARAM_STR1 + 1;
|
|
len = 0;
|
|
memcpy(&len, *indata, l);
|
|
*indata += l;
|
|
}
|
|
ret = (binresult *)(*odata);
|
|
*odata += offsetof(binresult, str);
|
|
ret->type = PARAM_STR;
|
|
strings[*nextstrid] = ret;
|
|
(*nextstrid)++;
|
|
ret->length = len;
|
|
memcpy(*odata, *indata, len);
|
|
(*odata)[len] = 0;
|
|
*odata += ((len + ALIGN_BYTES) / ALIGN_BYTES) * ALIGN_BYTES;
|
|
*indata += len;
|
|
return ret;
|
|
} else if ((cond = (type >= RPARAM_RSTR1 && type <= RPARAM_RSTR4)) ||
|
|
(type >= RPARAM_SHORT_RSTR_BASE &&
|
|
type < RPARAM_SHORT_RSTR_BASE + VSHORT_RSTR_CNT)) {
|
|
size_t id;
|
|
if (cond) {
|
|
len = type - RPARAM_RSTR1 + 1;
|
|
id = 0;
|
|
memcpy(&id, *indata, len);
|
|
*indata += len;
|
|
} else
|
|
id = type - RPARAM_SHORT_RSTR_BASE;
|
|
return strings[id];
|
|
} else if (type >= RPARAM_NUM1 && type <= RPARAM_NUM8) {
|
|
ret = (binresult *)(*odata);
|
|
*odata += sizeof(binresult);
|
|
ret->type = PARAM_NUM;
|
|
len = type - RPARAM_NUM1 + 1;
|
|
ret->num = 0;
|
|
memcpy(&ret->num, *indata, len);
|
|
*indata += len;
|
|
return ret;
|
|
} else if (type >= RPARAM_SMALL_NUM_BASE &&
|
|
type < RPARAM_SMALL_NUM_BASE + VSMALL_NUMBER_NUM)
|
|
return (binresult *)&NUM_SMALL[type - RPARAM_SMALL_NUM_BASE];
|
|
else if (type == RPARAM_BTRUE)
|
|
return (binresult *)&BOOL_TRUE;
|
|
else if (type == RPARAM_BFALSE)
|
|
return (binresult *)&BOOL_FALSE;
|
|
else if (type == RPARAM_ARRAY) {
|
|
binresult **arr;
|
|
unsigned long cnt, alloc;
|
|
ret = (binresult *)(*odata);
|
|
*odata += sizeof(binresult);
|
|
ret->type = PARAM_ARRAY;
|
|
arr = NULL;
|
|
cnt = 0;
|
|
alloc = 128;
|
|
arr = (binresult **)malloc(sizeof(binresult *) * alloc);
|
|
if (!arr)
|
|
return NULL;
|
|
while (**indata != RPARAM_END) {
|
|
if (cnt == alloc) {
|
|
binresult **tmp;
|
|
alloc *= 2;
|
|
tmp = (binresult **)realloc(arr, sizeof(binresult *) * alloc);
|
|
if (!tmp) {
|
|
free(arr);
|
|
return NULL;
|
|
}
|
|
arr = tmp;
|
|
}
|
|
arr[cnt++] = do_parse_result(indata, odata, strings, nextstrid);
|
|
}
|
|
(*indata)++;
|
|
ret->length = cnt;
|
|
ret->array = (struct _binresult **)*odata;
|
|
*odata += sizeof(struct _binresult *) * cnt;
|
|
memcpy(ret->array, arr, sizeof(struct _binresult *) * cnt);
|
|
free(arr);
|
|
return ret;
|
|
} else if (type == RPARAM_HASH) {
|
|
struct _hashpair *arr;
|
|
unsigned long cnt, alloc;
|
|
binresult *key;
|
|
ret = (binresult *)(*odata);
|
|
*odata += sizeof(binresult);
|
|
ret->type = PARAM_HASH;
|
|
arr = NULL;
|
|
cnt = 0;
|
|
alloc = 32;
|
|
arr = (struct _hashpair *)malloc(sizeof(struct _hashpair) * alloc);
|
|
if (!arr)
|
|
return NULL;
|
|
while (**indata != RPARAM_END) {
|
|
if (cnt == alloc) {
|
|
struct _hashpair *tmp;
|
|
alloc *= 2;
|
|
tmp = (struct _hashpair *)realloc(arr, sizeof(struct _hashpair) *
|
|
alloc);
|
|
if (!tmp) {
|
|
free(arr);
|
|
return NULL;
|
|
}
|
|
arr = tmp;
|
|
}
|
|
key = do_parse_result(indata, odata, strings, nextstrid);
|
|
arr[cnt].value = do_parse_result(indata, odata, strings, nextstrid);
|
|
if (key->type == PARAM_STR) {
|
|
arr[cnt].key = key->str;
|
|
cnt++;
|
|
}
|
|
}
|
|
(*indata)++;
|
|
ret->length = cnt;
|
|
ret->hash = (struct _hashpair *)*odata;
|
|
*odata += sizeof(struct _hashpair) * cnt;
|
|
memcpy(ret->hash, arr, sizeof(struct _hashpair) * cnt);
|
|
free(arr);
|
|
return ret;
|
|
} else if (type == RPARAM_DATA) {
|
|
ret = (binresult *)(*odata);
|
|
*odata += sizeof(binresult);
|
|
ret->type = PARAM_DATA;
|
|
memcpy(&ret->num, *indata, 8);
|
|
*indata += 8;
|
|
return ret;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static binresult *parse_result(unsigned char *data, size_t datalen) {
|
|
unsigned char *datac;
|
|
binresult **strings;
|
|
binresult *res;
|
|
ssize_t retlen;
|
|
size_t datalenc, strcnt;
|
|
datac = data;
|
|
datalenc = datalen;
|
|
strcnt = 0;
|
|
retlen = calc_ret_len(&datac, &datalenc, &strcnt);
|
|
if (retlen == -1)
|
|
return NULL;
|
|
datac = malloc(sizeof(unsigned char) * retlen);
|
|
if (!datac)
|
|
return NULL;
|
|
strings = malloc(sizeof(binresult *) * strcnt);
|
|
if (!strings) {
|
|
free(datac);
|
|
return NULL;
|
|
}
|
|
strcnt = 0;
|
|
res = do_parse_result(&data, &datac, strings, &strcnt);
|
|
free(strings);
|
|
return res;
|
|
}
|
|
|
|
binresult *papi_result(psock_t *sock) {
|
|
unsigned char *data;
|
|
binresult *res;
|
|
uint32_t ressize;
|
|
|
|
if (pdbg_unlikely(psock_readall(sock, &ressize, sizeof(uint32_t)) !=
|
|
sizeof(uint32_t))) {
|
|
return NULL;
|
|
}
|
|
|
|
if (ressize > MAX_API_RESPONSE_SIZE) {
|
|
pdbg_logf(D_WARNING, "API response size %u exceeds limit %u, rejecting",
|
|
ressize, MAX_API_RESPONSE_SIZE);
|
|
return NULL;
|
|
}
|
|
|
|
data = (unsigned char *)malloc(ressize);
|
|
if (!data) {
|
|
pdbg_logf(D_ERROR, "Failed to allocate %u bytes for API response", ressize);
|
|
return NULL;
|
|
}
|
|
|
|
if (pdbg_unlikely(psock_readall(sock, data, ressize) != ressize)) {
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
|
|
res = parse_result(data, ressize);
|
|
free(data);
|
|
|
|
return res;
|
|
}
|
|
|
|
binresult *papi_result_thread(psock_t *sock) {
|
|
unsigned char *data;
|
|
binresult *res;
|
|
uint32_t ressize;
|
|
if (pdbg_unlikely(psock_readall_thread(
|
|
sock, &ressize, sizeof(uint32_t)) != sizeof(uint32_t)))
|
|
return NULL;
|
|
if (ressize > MAX_API_RESPONSE_SIZE) {
|
|
pdbg_logf(D_WARNING, "API response size %u exceeds limit %u, rejecting",
|
|
ressize, MAX_API_RESPONSE_SIZE);
|
|
return NULL;
|
|
}
|
|
data = (unsigned char *)malloc(ressize);
|
|
if (!data) {
|
|
pdbg_logf(D_ERROR, "Failed to allocate %u bytes for API response", ressize);
|
|
return NULL;
|
|
}
|
|
if (pdbg_unlikely(psock_readall_thread(sock, data, ressize) !=
|
|
ressize)) {
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
res = parse_result(data, ressize);
|
|
free(data);
|
|
return res;
|
|
}
|
|
|
|
void papi_rdr_alloc(async_result_reader *reader) {
|
|
reader->state = 0;
|
|
reader->bytesread = 0;
|
|
reader->bytestoread = sizeof(uint32_t);
|
|
reader->data = (unsigned char *)&reader->respsize;
|
|
}
|
|
|
|
void papi_rdr_free(async_result_reader *reader) {
|
|
if (reader->state == 1)
|
|
free(reader->data);
|
|
}
|
|
|
|
int papi_result_async(psock_t *sock, async_result_reader *reader) {
|
|
int rd;
|
|
again:
|
|
rd = psock_read_noblock(sock, reader->data + reader->bytesread,
|
|
reader->bytestoread - reader->bytesread);
|
|
if (rd == PSYNC_SOCKET_WOULDBLOCK)
|
|
return ASYNC_RES_NEEDMORE;
|
|
else if (rd == PSYNC_SOCKET_ERROR || rd == 0) {
|
|
if (reader->state == 1)
|
|
free(reader->data);
|
|
papi_rdr_alloc(reader);
|
|
reader->result = NULL;
|
|
return ASYNC_RES_READY;
|
|
}
|
|
reader->bytesread += rd;
|
|
if (reader->bytesread == reader->bytestoread) {
|
|
if (reader->state == 0) {
|
|
reader->state = 1;
|
|
reader->bytesread = 0;
|
|
reader->bytestoread = reader->respsize;
|
|
if (reader->respsize > MAX_API_RESPONSE_SIZE) {
|
|
pdbg_logf(D_WARNING, "API response size %u exceeds limit %u, rejecting",
|
|
reader->respsize, MAX_API_RESPONSE_SIZE);
|
|
reader->result = NULL;
|
|
papi_rdr_alloc(reader);
|
|
return ASYNC_RES_READY;
|
|
}
|
|
reader->data = (unsigned char *)malloc(reader->respsize);
|
|
if (!reader->data) {
|
|
pdbg_logf(D_ERROR, "Failed to allocate %u bytes for API response",
|
|
reader->respsize);
|
|
reader->result = NULL;
|
|
papi_rdr_alloc(reader);
|
|
return ASYNC_RES_READY;
|
|
}
|
|
goto again;
|
|
} else {
|
|
pdbg_assert(reader->state == 1);
|
|
reader->result = parse_result(reader->data, reader->respsize);
|
|
free(reader->data);
|
|
papi_rdr_alloc(reader);
|
|
return ASYNC_RES_READY;
|
|
}
|
|
} else
|
|
return ASYNC_RES_NEEDMORE;
|
|
}
|
|
|
|
unsigned char *papi_prepare(const char *command, size_t cmdlen,
|
|
const binparam *params, size_t paramcnt,
|
|
int64_t datalen, size_t additionalalloc,
|
|
size_t *retlen) {
|
|
size_t i, plen;
|
|
unsigned char *data, *sdata;
|
|
/* 2 byte len (not included), 1 byte cmdlen, 1 byte paramcnt, cmdlen bytes
|
|
* cmd*/
|
|
plen = cmdlen + 2;
|
|
if (datalen != -1)
|
|
plen += sizeof(uint64_t);
|
|
|
|
for (i = 0; i < paramcnt; i++) {
|
|
if (params[i].paramtype == PARAM_STR) {
|
|
plen += params[i].paramnamelen + params[i].opts +
|
|
5; /* 1byte type+paramnamelen, nbytes paramnamelen, 4byte strlen,
|
|
nbytes str */
|
|
} else if (params[i].paramtype == PARAM_NUM) {
|
|
plen += params[i].paramnamelen + 1 + sizeof(uint64_t);
|
|
} else if (params[i].paramtype == PARAM_BOOL) {
|
|
plen += params[i].paramnamelen + 2;
|
|
}
|
|
}
|
|
if (pdbg_unlikely(plen > 0xffff))
|
|
return NULL;
|
|
sdata = data = (unsigned char *)malloc(plen + 2 + additionalalloc);
|
|
if (!data)
|
|
return NULL;
|
|
memcpy(data, &plen, 2);
|
|
data += 2;
|
|
if (datalen != -1) {
|
|
*data++ = cmdlen | 0x80;
|
|
memcpy(data, &datalen, sizeof(uint64_t));
|
|
data += sizeof(uint64_t);
|
|
} else
|
|
*data++ = cmdlen;
|
|
memcpy(data, command, cmdlen);
|
|
data += cmdlen;
|
|
*data++ = paramcnt;
|
|
for (i = 0; i < paramcnt; i++) {
|
|
*data++ = (params[i].paramtype << 6) + params[i].paramnamelen;
|
|
memcpy(data, params[i].paramname, params[i].paramnamelen);
|
|
data += params[i].paramnamelen;
|
|
if (params[i].paramtype == PARAM_STR) {
|
|
memcpy(data, ¶ms[i].opts, 4);
|
|
data += 4;
|
|
memcpy(data, params[i].str, params[i].opts);
|
|
data += params[i].opts;
|
|
} else if (params[i].paramtype == PARAM_NUM) {
|
|
memcpy(data, ¶ms[i].num, sizeof(uint64_t));
|
|
data += sizeof(uint64_t);
|
|
} else if (params[i].paramtype == PARAM_BOOL)
|
|
*data++ = params[i].num & 1;
|
|
}
|
|
plen += 2;
|
|
*retlen = plen;
|
|
return sdata;
|
|
}
|
|
|
|
binresult *papi_send(psock_t *sock, const char *command,
|
|
size_t cmdlen, const binparam *params,
|
|
size_t paramcnt, int64_t datalen, int readres) {
|
|
unsigned char *sdata;
|
|
size_t plen;
|
|
|
|
sdata =
|
|
papi_prepare(command, cmdlen, params, paramcnt, datalen, 0, &plen);
|
|
|
|
if (!sdata) {
|
|
return NULL;
|
|
}
|
|
|
|
if (readres & 2) {
|
|
if (pdbg_unlikely(psock_writeall_thread(sock, sdata, plen) != plen)) {
|
|
free(sdata);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
if (pdbg_unlikely(psock_writeall(sock, sdata, plen) != plen)) {
|
|
free(sdata);
|
|
return NULL;
|
|
}
|
|
}
|
|
free(sdata);
|
|
if (readres & 1) {
|
|
return papi_result(sock);
|
|
} else {
|
|
return PTR_OK;
|
|
}
|
|
}
|
|
|
|
void papi_dump(const binresult *res, const char *file,
|
|
const char *function, int unsigned line) {
|
|
uint32_t i;
|
|
pdbg_printf(file, function, line, D_NOTICE,
|
|
"dumping existing fields of the hash");
|
|
for (i = 0; i < res->length; i++)
|
|
switch (res->hash[i].value->type) {
|
|
case PARAM_HASH:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=[hash]",
|
|
res->hash[i].key);
|
|
break;
|
|
case PARAM_ARRAY:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=[array]",
|
|
res->hash[i].key);
|
|
break;
|
|
case PARAM_DATA:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=[data]",
|
|
res->hash[i].key);
|
|
break;
|
|
case PARAM_NUM:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=%llu", res->hash[i].key,
|
|
(long long unsigned)res->hash[i].value->num);
|
|
break;
|
|
case PARAM_STR:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=\"%s\"",
|
|
res->hash[i].key, res->hash[i].value->str);
|
|
break;
|
|
case PARAM_BOOL:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=%s", res->hash[i].key,
|
|
res->hash[i].value->num ? "true" : "false");
|
|
break;
|
|
default:
|
|
pdbg_printf(file, function, line, D_NOTICE, " %s=!unknown type %u",
|
|
res->hash[i].key, (unsigned)res->hash[i].value->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
const binresult *papi_find_result(const binresult *res, const char *name,
|
|
uint32_t type, const char *file,
|
|
const char *function, int unsigned line) {
|
|
uint32_t i;
|
|
if (unlikely(!res || res->type != PARAM_HASH)) {
|
|
if (D_CRITICAL <= DEBUG_LEVEL) {
|
|
const char *nm = "NULL";
|
|
if (res) {
|
|
nm = type_names[res->type];
|
|
}
|
|
pdbg_printf(file, function, line, D_CRITICAL,
|
|
"expecting hash as first parameter, got %s", nm);
|
|
}
|
|
return empty_types[type];
|
|
}
|
|
for (i = 0; i < res->length; i++)
|
|
if (!strcmp(res->hash[i].key, name)) {
|
|
if (likely(res->hash[i].value->type == type))
|
|
return res->hash[i].value;
|
|
else {
|
|
if (D_CRITICAL <= DEBUG_LEVEL)
|
|
pdbg_printf(file, function, line, D_CRITICAL,
|
|
"type error for key %s, expected %s got %s", name,
|
|
type_names[type], type_names[res->hash[i].value->type]);
|
|
return empty_types[type];
|
|
}
|
|
}
|
|
if (D_CRITICAL <= DEBUG_LEVEL)
|
|
pdbg_printf(file, function, line, D_CRITICAL, "could not find key %s",
|
|
name);
|
|
if (IS_DEBUG)
|
|
papi_dump(res, file, function, line);
|
|
return empty_types[type];
|
|
}
|
|
|
|
const binresult *papi_check_result(const binresult *res, const char *name,
|
|
uint32_t type, const char *file,
|
|
const char *function,
|
|
int unsigned line) {
|
|
uint32_t i;
|
|
if (unlikely(!res || res->type != PARAM_HASH)) {
|
|
if (D_CRITICAL <= DEBUG_LEVEL) {
|
|
const char *nm = "NULL";
|
|
if (res)
|
|
nm = type_names[res->type];
|
|
pdbg_printf(file, function, line, D_CRITICAL,
|
|
"expecting hash as first parameter, got %s", nm);
|
|
}
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < res->length; i++)
|
|
if (!strcmp(res->hash[i].key, name)) {
|
|
if (likely(res->hash[i].value->type == type))
|
|
return res->hash[i].value;
|
|
else {
|
|
if (D_CRITICAL <= DEBUG_LEVEL)
|
|
pdbg_printf(file, function, line, D_CRITICAL,
|
|
"type error for key %s, expected %s got %s", name,
|
|
type_names[type], type_names[res->hash[i].value->type]);
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const binresult *papi_get_result(const binresult *res, const char *name,
|
|
const char *file, const char *function,
|
|
int unsigned line) {
|
|
uint32_t i;
|
|
if (unlikely(!res || res->type != PARAM_HASH)) {
|
|
if (D_CRITICAL <= DEBUG_LEVEL) {
|
|
const char *nm = "NULL";
|
|
if (res)
|
|
nm = type_names[res->type];
|
|
pdbg_printf(file, function, line, D_CRITICAL,
|
|
"expecting hash as first parameter, got %s", nm);
|
|
}
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < res->length; i++)
|
|
if (!strcmp(res->hash[i].key, name))
|
|
return res->hash[i].value;
|
|
return NULL;
|
|
}
|