Browse Source

ckdb - optioncontrol table and commands - getopts/setopts

master
kanoi 10 years ago
parent
commit
48ffb1d109
  1. 675
      src/ckdb.c

675
src/ckdb.c

@ -49,7 +49,7 @@
#define DB_VLOCK "1"
#define DB_VERSION "0.9.2"
#define CKDB_VERSION DB_VERSION"-0.303"
#define CKDB_VERSION DB_VERSION"-0.305"
#define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -236,6 +236,7 @@ static LOADSTATUS dbstatus;
// Share stats since last block
typedef struct poolstatus {
int64_t workinfoid; // Last block
int32_t height;
double diffacc;
double diffinv; // Non-acc
double shareacc;
@ -314,6 +315,8 @@ static char *pqerrmsg(PGconn *conn)
{
char *ptr, *buf = strdup(PQerrorMessage(conn));
if (!buf)
quithere(1, "malloc OOM");
ptr = buf + strlen(buf) - 1;
while (ptr >= buf && (*ptr == '\n' || *ptr == '\r'))
*(ptr--) = '\0';
@ -388,7 +391,7 @@ enum data_type {
#define TXT_TO_INT(__nam, __fld, __data) txt_to_int(__nam, __fld, &(__data), sizeof(__data))
#define TXT_TO_TV(__nam, __fld, __data) txt_to_tv(__nam, __fld, &(__data), sizeof(__data))
#define TXT_TO_CTV(__nam, __fld, __data) txt_to_ctv(__nam, __fld, &(__data), sizeof(__data))
#define TXT_TO_BLOB(__nam, __fld, __data) txt_to_blob(__nam, __fld, __data)
#define TXT_TO_BLOB(__nam, __fld, __data) txt_to_blob(__nam, __fld, &(__data))
#define TXT_TO_DOUBLE(__nam, __fld, __data) txt_to_double(__nam, __fld, &(__data), sizeof(__data))
#define PQ_GET_FLD(__res, __row, __name, __fld, __ok) do { \
@ -803,6 +806,8 @@ enum cmd_values {
CMD_GETATTS,
CMD_SETATTS,
CMD_EXPATTS,
CMD_GETOPTS,
CMD_SETOPTS,
CMD_DSP,
CMD_STATS,
CMD_PPLNS,
@ -1135,7 +1140,6 @@ typedef struct idcontrol {
static K_LIST *idcontrol_free;
static K_STORE *idcontrol_store;
/* unused yet
// OPTIONCONTROL
typedef struct optioncontrol {
char optionname[TXT_SML+1];
@ -1149,11 +1153,14 @@ typedef struct optioncontrol {
#define LIMIT_OPTIONCONTROL 0
#define INIT_OPTIONCONTROL(_item) INIT_GENERIC(_item, optioncontrol)
#define DATA_OPTIONCONTROL(_var, _item) DATA_GENERIC(_var, _item, optioncontrol, true)
#define DATA_OPTIONCONTROL_NULL(_var, _item) DATA_GENERIC(_var, _item, optioncontrol, false)
// Value it must default to (to work properly)
#define OPTIONCONTROL_HEIGHT 1
static K_TREE *optioncontrol_root;
static K_LIST *optioncontrol_free;
static K_STORE *optioncontrol_store;
*/
// TODO: discarding workinfo,shares
// WORKINFO workinfo.id.json={...}
@ -1535,6 +1542,8 @@ static K_STORE *userstats_summ;
((_old)->tv_sec < (_new)->tv_sec))
#define tv_equal(_a, _b) (((_a)->tv_sec == (_b)->tv_sec) && \
((_a)->tv_usec == (_b)->tv_usec))
// newer OR equal
#define tv_newer_eq(_old, _new) (!(tv_newer(_new, _old)))
// WORKERSTATUS from various incoming data
typedef struct workerstatus {
@ -1716,9 +1725,9 @@ static void _txt_to_ctv(char *nam, char *fld, tv_t *data, size_t siz, WHERE_FFL_
_txt_to_data(TYPE_CTV, nam, fld, (void *)data, siz, WHERE_FFL_PASS);
}
static void _txt_to_blob(char *nam, char *fld, char *data, WHERE_FFL_ARGS)
static void _txt_to_blob(char *nam, char *fld, char **data, WHERE_FFL_ARGS)
{
_txt_to_data(TYPE_BLOB, nam, fld, (void *)(&data), 0, WHERE_FFL_PASS);
_txt_to_data(TYPE_BLOB, nam, fld, (void *)data, 0, WHERE_FFL_PASS);
}
static void _txt_to_double(char *nam, char *fld, double *data, size_t siz, WHERE_FFL_ARGS)
@ -1760,7 +1769,7 @@ static char *_data_to_buf(enum data_type typ, void *data, char *buf, size_t siz,
buf = malloc(siz);
if (!buf)
quithere(1, "OOM (%d)" WHERE_FFL, (int)siz, WHERE_FFL_PASS);
quithere(1, "(%d) OOM" WHERE_FFL, (int)siz, WHERE_FFL_PASS);
}
switch (typ) {
@ -1896,6 +1905,8 @@ static void log_queue_message(char *msg)
lq_item = k_unlink_head(logqueue_free);
DATA_LOGQUEUE(lq, lq_item);
lq->msg = strdup(msg);
if (!(lq->msg))
quithere(1, "malloc (%d) OOM", (int)strlen(msg));
k_add_tail(logqueue_store, lq_item);
K_WUNLOCK(logqueue_free);
}
@ -3164,7 +3175,7 @@ static bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begu
char *upd, *ins;
bool ok = false;
char *params[9 + HISTORYDATECOUNT];
int n, par = 0;
int n, par;
LOGDEBUG("%s(): add", __func__);
@ -3178,6 +3189,7 @@ static bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begu
/* N.B. the values of the old ua_item record, if it exists,
* are completely ignored i.e. you must provide all values required */
par = 0;
if (!conn) {
conn = dbconnect();
conned = true;
@ -3187,11 +3199,11 @@ static bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begu
// Beginning of a write txn
res = PQexec(conn, "Begin", CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Begin", rescode, conn);
goto unparam;
}
PQclear(res);
}
if (old_item) {
@ -3206,6 +3218,7 @@ static bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begu
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Update", rescode, conn);
goto unparam;
@ -3235,22 +3248,23 @@ static bool useratts_item_add(PGconn *conn, K_ITEM *ua_item, tv_t *cd, bool begu
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto rollback;
}
ok = true;
rollback:
if (!begun) {
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
if (ok)
res = PQexec(conn, "Commit", CKPQ_WRITE);
else
res = PQexec(conn, "Rollback", CKPQ_WRITE);
goto unparam;
}
res = PQexec(conn, "Commit", CKPQ_WRITE);
ok = true;
} else {
if (PGOK(rescode))
ok = true;
PQclear(res);
}
unparam:
PQclear(res);
if (conned)
PQfinish(conn);
for (n = 0; n < par; n++)
@ -3258,6 +3272,7 @@ unparam:
K_WLOCK(useratts_free);
if (ok) {
// Update it
if (old_item) {
useratts_root = remove_from_ktree(useratts_root, old_item, cmp_useratts, ctx);
copy_tv(&(old_useratts->expirydate), cd);
@ -3333,10 +3348,11 @@ static __maybe_unused K_ITEM *useratts_add(PGconn *conn, char *username, char *a
ok = useratts_item_add(conn, item, cd, begun);
unitem:
K_WLOCK(useratts_free);
if (!ok)
if (!ok) {
K_WLOCK(useratts_free);
k_add_head(useratts_free, item);
K_WUNLOCK(useratts_free);
K_WUNLOCK(useratts_free);
}
if (ok)
return item;
@ -4407,6 +4423,350 @@ void payments_reload()
PQfinish(conn);
}
// order by optionname asc,activationdate asc,activationheight asc,expirydate desc
static cmp_t cmp_optioncontrol(K_ITEM *a, K_ITEM *b)
{
OPTIONCONTROL *oca, *ocb;
DATA_OPTIONCONTROL(oca, a);
DATA_OPTIONCONTROL(ocb, b);
cmp_t c = CMP_STR(oca->optionname, ocb->optionname);
if (c == 0) {
c = CMP_TV(oca->activationdate, ocb->activationdate);
if (c == 0) {
c = CMP_INT(oca->activationheight, ocb->activationheight);
if (c == 0)
c = CMP_TV(ocb->expirydate, oca->expirydate);
}
}
return c;
}
// Must be R or W locked before call
static K_ITEM *find_optioncontrol(char *optionname, tv_t *now)
{
OPTIONCONTROL optioncontrol, *oc, *ocbest;
K_TREE_CTX ctx[1];
K_ITEM look, *item, *best;
/* Step through all records having optionaname and check:
* 1) activationdate is <= now
* and
* 2) height <= current
* Remember the active record with the newest activationdate
* If two records have the same activation date, then
* remember the active record with the highest height
* In optioncontrol_add(), when not specified,
* the default activation date is DATE_BEGIN
* and the default height is 1 (OPTIONCONTROL_HEIGHT)
* Thus if records have both values set, then
* activationdate will determine the newests record
* To have activationheight decide selection,
* create all records with only activationheight and then
* activationdate will all be the default value and not
* decide the outcome */
STRNCPY(optioncontrol.optionname, optionname);
optioncontrol.activationdate.tv_sec = 0L;
optioncontrol.activationdate.tv_usec = 0L;
optioncontrol.activationheight = OPTIONCONTROL_HEIGHT - 1;
optioncontrol.expirydate.tv_sec = default_expiry.tv_sec;
optioncontrol.expirydate.tv_usec = default_expiry.tv_usec;
INIT_OPTIONCONTROL(&look);
look.data = (void *)(&optioncontrol);
item = find_after_in_ktree(optioncontrol_root, &look, cmp_optioncontrol, ctx);
ocbest = NULL;
best = NULL;
while (item) {
DATA_OPTIONCONTROL(oc, item);
// Ordered first by optionname
if (strcmp(oc->optionname, optionname) != 0)
break;
// Is oc active?
if (CURRENT(&(oc->expirydate)) &&
oc->activationheight <= pool.height &&
tv_newer_eq(&(oc->activationdate), now)) {
// Is oc newer than ocbest?
if (!ocbest ||
tv_newer(&(ocbest->activationdate), &(oc->activationdate)) ||
(tv_equal(&(ocbest->activationdate), &(oc->activationdate)) &&
ocbest->activationheight < oc->activationheight)) {
ocbest = oc;
best = item;
}
}
item = next_in_ktree(ctx);
}
return best;
}
static K_ITEM *optioncontrol_item_add(PGconn *conn, K_ITEM *oc_item, tv_t *cd, bool begun)
{
ExecStatusType rescode;
bool conned = false;
K_TREE_CTX ctx[1];
PGresult *res;
K_ITEM *old_item, look;
int n;
OPTIONCONTROL *row;
char *upd, *ins;
bool ok = false;
char *params[4 + HISTORYDATECOUNT];
int par;
LOGDEBUG("%s(): add", __func__);
DATA_OPTIONCONTROL(row, oc_item);
INIT_OPTIONCONTROL(&look);
look.data = (void *)row;
K_RLOCK(optioncontrol_free);
old_item = find_in_ktree(optioncontrol_root, &look, cmp_optioncontrol, ctx);
K_RUNLOCK(optioncontrol_free);
par = 0;
if (!conn) {
conn = dbconnect();
conned = true;
}
if (!begun) {
res = PQexec(conn, "Begin", CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Begin", rescode, conn);
goto nostart;
}
}
if (old_item) {
upd = "update optioncontrol "
"set expirydate=$1 where optionname=$2 and "
"activationdate=$3 and activationheight=$4 and "
"expirydate=$5";
par = 0;
params[par++] = tv_to_buf(cd, NULL, 0);
params[par++] = str_to_buf(row->optionname, NULL, 0);
params[par++] = tv_to_buf(&(row->activationdate), NULL, 0);
params[par++] = int_to_buf(row->activationheight, NULL, 0);
params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0);
PARCHKVAL(par, 5, params);
res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Update", rescode, conn);
goto rollback;
}
for (n = 0; n < par; n++)
free(params[n]);
}
par = 0;
params[par++] = str_to_buf(row->optionname, NULL, 0);
params[par++] = str_to_buf(row->optionvalue, NULL, 0);
params[par++] = tv_to_buf(&(row->activationdate), NULL, 0);
params[par++] = int_to_buf(row->activationheight, NULL, 0);
HISTORYDATEPARAMS(params, par, row);
PARCHK(par, params);
ins = "insert into optioncontrol "
"(optionname,optionvalue,activationdate,activationheight"
HISTORYDATECONTROL ") values (" PQPARAM9 ")";
res = PQexecParams(conn, ins, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Insert", rescode, conn);
goto rollback;
}
ok = true;
rollback:
if (!begun) {
if (ok)
res = PQexec(conn, "Commit", CKPQ_WRITE);
else
res = PQexec(conn, "Rollback", CKPQ_WRITE);
PQclear(res);
}
nostart:
if (conned)
PQfinish(conn);
for (n = 0; n < par; n++)
free(params[n]);
K_WLOCK(optioncontrol_free);
if (!ok)
k_add_head(optioncontrol_free, oc_item);
else {
// Discard it
if (old_item) {
optioncontrol_root = remove_from_ktree(optioncontrol_root, old_item,
cmp_optioncontrol, ctx);
k_add_head(optioncontrol_free, old_item);
}
optioncontrol_root = add_to_ktree(optioncontrol_root, oc_item, cmp_optioncontrol);
k_add_head(optioncontrol_store, oc_item);
}
K_WUNLOCK(optioncontrol_free);
if (ok)
return oc_item;
else
return NULL;
}
static __maybe_unused K_ITEM *optioncontrol_add(PGconn *conn, char *optionname, char *optionvalue,
char *activationdate, char *activationheight,
char *by, char *code, char *inet, tv_t *cd,
K_TREE *trf_root, bool begun)
{
K_ITEM *item;
OPTIONCONTROL *row;
bool ok = false;
LOGDEBUG("%s(): add", __func__);
K_WLOCK(optioncontrol_free);
item = k_unlink_head(optioncontrol_free);
K_WUNLOCK(optioncontrol_free);
DATA_OPTIONCONTROL(row, item);
STRNCPY(row->optionname, optionname);
row->optionvalue = strdup(optionvalue);
if (!(row->optionvalue))
quithere(1, "malloc (%d) OOM", (int)strlen(optionvalue));
if (activationdate && *activationdate) {
TXT_TO_CTV("activationdate", activationdate,
row->activationdate);
} else
copy_tv(&(row->activationdate), &date_begin);
if (activationheight && *activationheight) {
TXT_TO_INT("activationheight", activationheight,
row->activationheight);
} else
row->activationheight = 1;
HISTORYDATEINIT(row, cd, by, code, inet);
HISTORYDATETRANSFER(trf_root, row);
ok = optioncontrol_item_add(conn, item, cd, begun);
if (!ok) {
free(row->optionvalue);
K_WLOCK(optioncontrol_free);
k_add_head(optioncontrol_free, item);
K_WUNLOCK(optioncontrol_free);
}
if (ok)
return item;
else
return NULL;
}
static bool optioncontrol_fill(PGconn *conn)
{
ExecStatusType rescode;
PGresult *res;
K_ITEM *item;
int n, i;
OPTIONCONTROL *row;
char *params[1];
int par;
char *field;
char *sel;
int fields = 4;
bool ok;
LOGDEBUG("%s(): select", __func__);
// No need to keep old versions in ram for now ...
sel = "select "
"optionname,optionvalue,activationdate,activationheight"
HISTORYDATECONTROL
" from optioncontrol where expirydate=$1";
par = 0;
params[par++] = tv_to_buf((tv_t *)(&default_expiry), NULL, 0);
PARCHK(par, params);
res = PQexecParams(conn, sel, par, NULL, (const char **)params, NULL, NULL, 0, CKPQ_READ);
rescode = PQresultStatus(res);
if (!PGOK(rescode)) {
PGLOGERR("Select", rescode, conn);
PQclear(res);
return false;
}
n = PQnfields(res);
if (n != (fields + HISTORYDATECOUNT)) {
LOGERR("%s(): Invalid field count - should be %d, but is %d",
__func__, fields + HISTORYDATECOUNT, n);
PQclear(res);
return false;
}
n = PQntuples(res);
LOGDEBUG("%s(): tree build count %d", __func__, n);
ok = true;
K_WLOCK(optioncontrol_free);
for (i = 0; i < n; i++) {
item = k_unlink_head(optioncontrol_free);
DATA_OPTIONCONTROL(row, item);
if (everyone_die) {
ok = false;
break;
}
PQ_GET_FLD(res, i, "optionname", field, ok);
if (!ok)
break;
TXT_TO_STR("optionname", field, row->optionname);
PQ_GET_FLD(res, i, "optionvalue", field, ok);
if (!ok)
break;
TXT_TO_BLOB("optionvalue", field, row->optionvalue);
PQ_GET_FLD(res, i, "activationdate", field, ok);
if (!ok)
break;
TXT_TO_TV("activationdate", field, row->activationdate);
PQ_GET_FLD(res, i, "activationheight", field, ok);
if (!ok)
break;
TXT_TO_INT("activationheight", field, row->activationheight);
HISTORYDATEFLDS(res, i, row, ok);
if (!ok)
break;
optioncontrol_root = add_to_ktree(optioncontrol_root, item, cmp_optioncontrol);
k_add_head(optioncontrol_store, item);
}
if (!ok)
k_add_head(optioncontrol_free, item);
K_WUNLOCK(optioncontrol_free);
PQclear(res);
if (ok) {
LOGDEBUG("%s(): built", __func__);
LOGWARNING("%s(): loaded %d optioncontrol records", __func__, n);
}
return ok;
}
// order by workinfoid asc,expirydate asc
static cmp_t cmp_workinfo(K_ITEM *a, K_ITEM *b)
{
@ -4513,7 +4873,11 @@ static int64_t workinfo_add(PGconn *conn, char *workinfoidstr, char *poolinstanc
TXT_TO_BIGINT("workinfoid", workinfoidstr, row->workinfoid);
STRNCPY(row->poolinstance, poolinstance);
row->transactiontree = strdup(transactiontree);
if (!(row->transactiontree))
quithere(1, "malloc (%d) OOM", (int)strlen(transactiontree));
row->merklehash = strdup(merklehash);
if (!(row->merklehash))
quithere(1, "malloc (%d) OOM", (int)strlen(merklehash));
STRNCPY(row->prevhash, prevhash);
STRNCPY(row->coinbase1, coinbase1);
STRNCPY(row->coinbase2, coinbase2);
@ -6565,6 +6929,7 @@ flail:
pool.diffacc, est, pct, cd_buf);
if (pool.workinfoid < row->workinfoid) {
pool.workinfoid = row->workinfoid;
pool.height = row->height;
zero_on_new_block();
}
break;
@ -6728,8 +7093,10 @@ static bool blocks_fill(PGconn *conn)
if (tv_newer(&(dbstatus.newest_createdate_blocks), &(row->createdate)))
copy_tv(&(dbstatus.newest_createdate_blocks), &(row->createdate));
if (pool.workinfoid < row->workinfoid)
if (pool.workinfoid < row->workinfoid) {
pool.workinfoid = row->workinfoid;
pool.height = row->height;
}
}
if (!ok)
k_add_head(blocks_free, item);
@ -8052,6 +8419,8 @@ static bool check_db_version(PGconn *conn)
/* Load tables required to support auths,adduser,chkpass and newid
* N.B. idcontrol is DB internal so is always ready
* OptionControl is loaded first in case it is needed by other loads
* (though not yet)
*/
static bool getdata1()
{
@ -8060,6 +8429,8 @@ static bool getdata1()
if (!(ok = check_db_version(conn)))
goto matane;
if (!(ok = optioncontrol_fill(conn)))
goto matane;
if (!(ok = users_fill(conn)))
goto matane;
if (!(ok = workers_fill(conn)))
@ -8293,6 +8664,12 @@ static void alloc_storage()
useratts_store = k_new_store(useratts_free);
useratts_root = new_ktree();
optioncontrol_free = k_new_list("OptionControl", sizeof(OPTIONCONTROL),
ALLOC_OPTIONCONTROL,
LIMIT_OPTIONCONTROL, true);
optioncontrol_store = k_new_store(optioncontrol_free);
optioncontrol_root = new_ktree();
workers_free = k_new_list("Workers", sizeof(WORKERS),
ALLOC_WORKERS, LIMIT_WORKERS, true);
workers_store = k_new_store(workers_free);
@ -10511,7 +10888,7 @@ static char *cmd_setatts(PGconn *conn, char *cmd, char *id,
char reply[1024] = "";
size_t siz = sizeof(reply);
TRANSFER *transfer;
USERATTS *useratts;
USERATTS *useratts = NULL;
USERS *users;
char attname[sizeof(useratts->attname)*2];
char *reason = NULL;
@ -10742,6 +11119,239 @@ rats:
return strdup(reply);
}
/* Return the list of optioncontrols
* Format is optlist=optionname,optionname,optionname,...
* Replies will be optionname=value
* Any optionnames not in the DB or not yet active will be missing
*/
static char *cmd_getopts(__maybe_unused PGconn *conn, char *cmd, char *id,
tv_t *now, __maybe_unused char *by,
__maybe_unused char *code, __maybe_unused char *inet,
__maybe_unused tv_t *notcd, K_TREE *trf_root)
{
K_ITEM *i_optlist, *oc_item;
char reply[1024] = "";
size_t siz = sizeof(reply);
char tmp[1024];
OPTIONCONTROL *optioncontrol;
char *reason = NULL;
char *answer = NULL;
char *optlist = NULL, *ptr, *comma;
size_t len, off;
bool first;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
i_optlist = require_name(trf_root, "optlist", 1, NULL, reply, siz);
if (!i_optlist) {
reason = "Missing optlist";
goto ruts;
}
APPEND_REALLOC_INIT(answer, off, len);
optlist = ptr = strdup(transfer_data(i_optlist));
first = true;
while (ptr && *ptr) {
comma = strchr(ptr, ',');
if (comma)
*(comma++) = '\0';
K_RLOCK(optioncontrol_free);
oc_item = find_optioncontrol(ptr, now);
K_RUNLOCK(optioncontrol_free);
/* web code must check the existance of the optionname
* in the reply since it will be missing if it doesn't
* exist in the DB */
if (oc_item) {
DATA_OPTIONCONTROL(optioncontrol, oc_item);
snprintf(tmp, sizeof(tmp), "%s%s=%s",
first ? EMPTY : FLDSEPSTR,
optioncontrol->optionname,
optioncontrol->optionvalue);
APPEND_REALLOC(answer, off, len, tmp);
first = false;
}
ptr = comma;
}
ruts:
if (optlist)
free(optlist);
if (reason) {
if (answer)
free(answer);
snprintf(reply, siz, "ERR.%s", reason);
LOGERR("%s.%s.%s", cmd, id, reply);
return strdup(reply);
}
snprintf(reply, siz, "ok.%s", answer);
LOGDEBUG("%s.%s", id, answer);
free(answer);
return strdup(reply);
}
// This is the same as att_set_date() for now
#define opt_set_date(_date, _data, _now) att_set_date(_date, _data, _now)
/* Store optioncontrols in the DB
* Format is 1 or more: oc_optionname.fld=value
* i.e. each starts with the constant "oc_"
* optionname cannot contain Tab . or =
* fld is one of the 3: value, date, height
* value must exist
* None, one or both of date and height can exist
* If a matching optioncontrol (same name, date and height) exists,
* it will have it's expiry date set to now and be replaced with the new value
* The date field requires either an epoch sec,usec
* (usec is optional and defaults to 0) or one of: now or now+NNN
* now is the current epoch value and now+NNN is the epoch + NNN seconds
* See opt_set_date() above */
static char *cmd_setopts(PGconn *conn, char *cmd, char *id,
tv_t *now, char *by, char *code, char *inet,
__maybe_unused tv_t *notcd, K_TREE *trf_root)
{
ExecStatusType rescode;
PGresult *res;
bool conned = false;
K_ITEM *t_item, *oc_item = NULL;
K_TREE_CTX ctx[1];
char reply[1024] = "";
size_t siz = sizeof(reply);
TRANSFER *transfer;
OPTIONCONTROL *optioncontrol;
char optionname[sizeof(optioncontrol->optionname)*2];
char *reason = NULL;
char *dot, *data;
bool begun = false, gotvalue = false;
int db = 0;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
t_item = first_in_ktree(trf_root, ctx);
while (t_item) {
DATA_TRANSFER(transfer, t_item);
if (strncmp(transfer->name, "oc_", 3) == 0) {
data = transfer_data(t_item);
STRNCPY(optionname, transfer->name + 3);
dot = strchr(optionname, '.');
if (!dot) {
reason = "Missing field";
goto rollback;
}
*(dot++) = '\0';
// If we already had a different one, save it to the DB
if (oc_item && strcmp(optioncontrol->optionname, optionname) != 0) {
if (!gotvalue) {
reason = "Missing value";
goto rollback;
}
if (conn == NULL) {
conn = dbconnect();
conned = true;
}
if (!begun) {
// Beginning of a write txn
res = PQexec(conn, "Begin", CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Begin", rescode, conn);
reason = "DBERR";
goto rollback;
}
begun = true;
}
if (optioncontrol_item_add(conn, oc_item, now, begun)) {
oc_item = NULL;
db++;
} else {
reason = "DBERR";
goto rollback;
}
}
if (!oc_item) {
K_RLOCK(optioncontrol_free);
oc_item = k_unlink_head(optioncontrol_free);
K_RUNLOCK(optioncontrol_free);
DATA_OPTIONCONTROL(optioncontrol, oc_item);
bzero(optioncontrol, sizeof(*optioncontrol));
STRNCPY(optioncontrol->optionname, optionname);
optioncontrol->activationheight = OPTIONCONTROL_HEIGHT;
HISTORYDATEINIT(optioncontrol, now, by, code, inet);
HISTORYDATETRANSFER(trf_root, optioncontrol);
gotvalue = false;
}
if (strcmp(dot, "value") == 0) {
optioncontrol->optionvalue = strdup(data);
if (!(optioncontrol->optionvalue))
quithere(1, "malloc (%d) OOM", (int)strlen(data));
gotvalue = true;
} else if (strcmp(dot, "date") == 0) {
att_to_date(&(optioncontrol->activationdate), data, now);
} else if (strcmp(dot, "height") == 0) {
TXT_TO_INT("height", data, optioncontrol->activationheight);
} else {
reason = "Unknown field";
goto rollback;
}
}
t_item = next_in_ktree(ctx);
}
if (oc_item) {
if (!gotvalue) {
reason = "Missing value";
goto rollback;
}
if (conn == NULL) {
conn = dbconnect();
conned = true;
}
if (!begun) {
// Beginning of a write txn
res = PQexec(conn, "Begin", CKPQ_WRITE);
rescode = PQresultStatus(res);
PQclear(res);
if (!PGOK(rescode)) {
PGLOGERR("Begin", rescode, conn);
reason = "DBERR";
goto rollback;
}
begun = true;
}
if (!optioncontrol_item_add(conn, oc_item, now, begun)) {
reason = "DBERR";
goto rollback;
}
db++;
}
rollback:
if (begun) {
if (reason)
res = PQexec(conn, "Rollback", CKPQ_WRITE);
else
res = PQexec(conn, "Commit", CKPQ_WRITE);
PQclear(res);
}
if (conned)
PQfinish(conn);
if (reason) {
if (oc_item) {
if (optioncontrol->optionvalue)
free(optioncontrol->optionvalue);
K_WLOCK(optioncontrol_free);
k_add_head(optioncontrol_free, oc_item);
K_WUNLOCK(optioncontrol_free);
}
snprintf(reply, siz, "ERR.%s", reason);
LOGERR("%s.%s.%s", cmd, id, reply);
return strdup(reply);
}
snprintf(reply, siz, "ok.set %d", db);
LOGDEBUG("%s.%s", id, reply);
return strdup(reply);
}
// order by userid asc
static cmp_t cmp_mu(K_ITEM *a, K_ITEM *b)
{
@ -11327,6 +11937,8 @@ static struct CMDS {
{ CMD_GETATTS, "getatts", false, false, cmd_getatts, ACCESS_WEB },
{ CMD_SETATTS, "setatts", false, false, cmd_setatts, ACCESS_WEB },
{ CMD_EXPATTS, "expatts", false, false, cmd_expatts, ACCESS_WEB },
{ CMD_GETOPTS, "getopts", false, false, cmd_getopts, ACCESS_WEB },
{ CMD_SETOPTS, "setopts", false, false, cmd_setopts, ACCESS_WEB },
{ CMD_DSP, "dsp", false, false, cmd_dsp, ACCESS_SYSTEM },
{ CMD_STATS, "stats", true, false, cmd_stats, ACCESS_SYSTEM },
{ CMD_PPLNS, "pplns", false, false, cmd_pplns, ACCESS_SYSTEM },
@ -11996,6 +12608,7 @@ static void *socketer(__maybe_unused void *arg)
char *last_userset = NULL, *reply_userset = NULL;
char *last_newid = NULL, *reply_newid = NULL;
char *last_setatts = NULL, *reply_setatts = NULL;
char *last_setopts = NULL, *reply_setopts = NULL;
char *last_web = NULL, *reply_web = NULL;
char *reply_last, duptype[CMD_SIZ+1];
enum cmd_values cmdnum;
@ -12090,6 +12703,9 @@ static void *socketer(__maybe_unused void *arg)
} else if (last_setatts && strcmp(last_setatts, buf) == 0) {
reply_last = reply_setatts;
dup = true;
} else if (last_setopts && strcmp(last_setopts, buf) == 0) {
reply_last = reply_setopts;
dup = true;
} else if (last_web && strcmp(last_web, buf) == 0) {
reply_last = reply_web;
dup = true;
@ -12179,6 +12795,8 @@ static void *socketer(__maybe_unused void *arg)
case CMD_GETATTS:
case CMD_SETATTS:
case CMD_EXPATTS:
case CMD_GETOPTS:
case CMD_SETOPTS:
case CMD_BLOCKLIST:
case CMD_NEWID:
case CMD_STATS:
@ -12218,6 +12836,9 @@ static void *socketer(__maybe_unused void *arg)
case CMD_SETATTS:
STORELASTREPLY(setatts);
break;
case CMD_SETOPTS:
STORELASTREPLY(setopts);
break;
// The rest
default:
free(rep);
@ -12416,6 +13037,8 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
case CMD_GETATTS:
case CMD_SETATTS:
case CMD_EXPATTS:
case CMD_GETOPTS:
case CMD_SETOPTS:
case CMD_DSP:
case CMD_STATS:
case CMD_PPLNS:
@ -12490,7 +13113,7 @@ static bool reload_from(tv_t *start)
reload_buf = malloc(MAX_READ);
if (!reload_buf)
quithere(1, "OOM");
quithere(1, "(%d) OOM", MAX_READ);
reloading = true;

Loading…
Cancel
Save