diff --git a/pool/base.php b/pool/base.php index a109a23c..66eb014d 100644 --- a/pool/base.php +++ b/pool/base.php @@ -259,7 +259,7 @@ function safetext($txt, $len = 1024) # function dbd($data, $user) { - return "
Web site is currently down
"; + return "
Database is reloading, mining is all OK
"; } # function dbdown() diff --git a/src/ckdb.c b/src/ckdb.c index ac39b470..ecf4af24 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -151,6 +151,8 @@ static char *restorefrom; // Only accessed in here static bool markersummary_auto; +int switch_state = SWITCH_STATE_ALL; + // disallow: '/' '.' '_' and FLDSEP const char *userpatt = "^[^/\\._"FLDSEPSTR"]*$"; const char *mailpatt = "^[A-Za-z0-9_-][A-Za-z0-9_\\.-]*@[A-Za-z0-9][A-Za-z0-9\\.-]*[A-Za-z0-9]$"; diff --git a/src/ckdb.h b/src/ckdb.h index ecddeebc..6893ecfa 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -55,7 +55,7 @@ #define DB_VLOCK "1" #define DB_VERSION "1.0.0" -#define CKDB_VERSION DB_VERSION"-1.023" +#define CKDB_VERSION DB_VERSION"-1.031" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -81,6 +81,27 @@ #define TRUE_CHR 'Y' #define FALSE_CHR 'N' +/* Set by cmd_setopts() and used by whatever code needs it + * It's loaded during startup but set to SWITCH_STATE_ALL if it's missing, + * meaning all switches are active + * The idea is that if you need to manually switch code over from one version + * up to the next version at an indeterminate time then you can do that by + * coding a switch_state test and then externally switch the code over via + * the cmd_setopts() socket interface + * It's not for coding runtime options into the code since that can be done + * using optioncontrol directly + * It's stored in optioncontrol so that it's value is permanent and + * activationdate and activationheight have their values overridden to + * disable using them + * N.B. optioncontrol_item_add() intercepts the change by name and updates + * switch_state but ONLY if the DB update succeeds */ +extern int switch_state; +#define SWITCH_STATE_NAME "SwitchState" +/* Each switch state must be higher than all previous + * so that future states don't undo old changes */ +#define SWITCH_STATE_AUTHWORKERS 1 +#define SWITCH_STATE_ALL 666666 + extern char *EMPTY; extern const char *userpatt; @@ -1726,6 +1747,7 @@ extern cmp_t cmp_useratts(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_useratts(int64_t userid, char *attname); extern cmp_t cmp_workers(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_workers(int64_t userid, char *workername); +extern K_ITEM *first_workers(int64_t userid, K_TREE_CTX *ctx); extern K_ITEM *new_worker(PGconn *conn, bool update, int64_t userid, char *workername, char *diffdef, char *idlenotificationenabled, char *idlenotificationtime, char *by, diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 80b1f9d1..93c42a69 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -2308,15 +2308,18 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root) { + K_TREE_CTX ctx[1]; char reply[1024] = ""; size_t siz = sizeof(reply); K_ITEM *i_poolinstance, *i_username, *i_workername, *i_clientid; - K_ITEM *i_enonce1, *i_useragent, *i_preauth, *u_item, *oc_item; + K_ITEM *i_enonce1, *i_useragent, *i_preauth, *u_item, *oc_item, *w_item; USERS *users = NULL; char *username; WORKERS *workers = NULL; OPTIONCONTROL *optioncontrol; - bool ok; + size_t len, off; + char *buf; + bool ok, first; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -2378,18 +2381,51 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by, if (!ok) { LOGDEBUG("%s() %s.failed.DBE", __func__, id); return strdup("failed.DBE"); - } else { - // Only flag a successful auth - ck_wlock(&last_lock); - setnow(&last_auth); - ck_wunlock(&last_lock); } + + // Only flag a successful auth + ck_wlock(&last_lock); + setnow(&last_auth); + ck_wunlock(&last_lock); + + if (switch_state < SWITCH_STATE_AUTHWORKERS) { + snprintf(reply, siz, + "ok.authorise={\"secondaryuserid\":\"%s\"," + "\"difficultydefault\":%d}", + users->secondaryuserid, workers->difficultydefault); + LOGDEBUG("%s.%s", id, reply); + return strdup(reply); + } + + APPEND_REALLOC_INIT(buf, off, len); snprintf(reply, siz, "ok.authorise={\"secondaryuserid\":\"%s\"," - "\"difficultydefault\":%d}", - users->secondaryuserid, workers->difficultydefault); - LOGDEBUG("%s.%s", id, reply); - return strdup(reply); + "\"workers\":[", + users->secondaryuserid); + APPEND_REALLOC(buf, off, len, reply); + first = true; + K_RLOCK(workers_free); + w_item = first_workers(users->userid, ctx); + DATA_WORKERS_NULL(workers, w_item); + while (w_item && workers->userid == users->userid) { + if (CURRENT(&(workers->expirydate))) { + snprintf(reply, siz, + "%s{\"workername\":\"%s\"," + "\"difficultydefault\":%"PRId32"}", + first ? EMPTY : ",", + workers->workername, + workers->difficultydefault); + APPEND_REALLOC(buf, off, len, reply); + first = false; + } + w_item = next_in_ktree(ctx); + DATA_WORKERS_NULL(workers, w_item); + } + K_RUNLOCK(workers_free); + APPEND_REALLOC(buf, off, len, "]}"); + + LOGDEBUG("%s.%s", id, buf); + return buf; } static char *cmd_auth(PGconn *conn, char *cmd, char *id, @@ -2404,13 +2440,16 @@ static char *cmd_addrauth_do(PGconn *conn, char *cmd, char *id, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root) { + K_TREE_CTX ctx[1]; char reply[1024] = ""; size_t siz = sizeof(reply); K_ITEM *i_poolinstance, *i_username, *i_workername, *i_clientid; - K_ITEM *i_enonce1, *i_useragent, *i_preauth; + K_ITEM *i_enonce1, *i_useragent, *i_preauth, *w_item; USERS *users = NULL; WORKERS *workers = NULL; - bool ok; + size_t len, off; + char *buf; + bool ok, first; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -2456,18 +2495,51 @@ static char *cmd_addrauth_do(PGconn *conn, char *cmd, char *id, char *by, if (!ok) { LOGDEBUG("%s() %s.failed.DBE", __func__, id); return strdup("failed.DBE"); - } else { - // Only flag a successful auth - ck_wlock(&last_lock); - setnow(&last_auth); - ck_wunlock(&last_lock); } + + // Only flag a successful auth + ck_wlock(&last_lock); + setnow(&last_auth); + ck_wunlock(&last_lock); + + if (switch_state < SWITCH_STATE_AUTHWORKERS) { + snprintf(reply, siz, + "ok.addrauth={\"secondaryuserid\":\"%s\"," + "\"difficultydefault\":%d}", + users->secondaryuserid, workers->difficultydefault); + LOGDEBUG("%s.%s", id, reply); + return strdup(reply); + } + + APPEND_REALLOC_INIT(buf, off, len); snprintf(reply, siz, "ok.addrauth={\"secondaryuserid\":\"%s\"," - "\"difficultydefault\":%d}", - users->secondaryuserid, workers->difficultydefault); - LOGDEBUG("%s.%s", id, reply); - return strdup(reply); + "\"workers\":[", + users->secondaryuserid); + APPEND_REALLOC(buf, off, len, reply); + first = true; + K_RLOCK(workers_free); + w_item = first_workers(users->userid, ctx); + DATA_WORKERS_NULL(workers, w_item); + while (w_item && workers->userid == users->userid) { + if (CURRENT(&(workers->expirydate))) { + snprintf(reply, siz, + "%s{\"workername\":\"%s\"," + "\"difficultydefault\":%"PRId32"}", + first ? EMPTY : ",", + workers->workername, + workers->difficultydefault); + APPEND_REALLOC(buf, off, len, reply); + first = false; + } + w_item = next_in_ktree(ctx); + DATA_WORKERS_NULL(workers, w_item); + } + K_RUNLOCK(workers_free); + APPEND_REALLOC(buf, off, len, "]}"); + + LOGDEBUG("%s.%s", id, buf); + return buf; } static char *cmd_addrauth(PGconn *conn, char *cmd, char *id, diff --git a/src/ckdb_data.c b/src/ckdb_data.c index a4227e7e..f1303265 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -1113,6 +1113,26 @@ K_ITEM *find_workers(int64_t userid, char *workername) return find_in_ktree(workers_root, &look, cmp_workers, ctx); } +K_ITEM *first_workers(int64_t userid, K_TREE_CTX *ctx) +{ + WORKERS workers; + K_TREE_CTX ctx0[1]; + K_ITEM look; + + if (ctx == NULL) + ctx = ctx0; + + workers.userid = userid; + workers.workername[0] = '\0'; + workers.expirydate.tv_sec = 0L; + workers.expirydate.tv_usec = 0L; + + INIT_WORKERS(&look); + look.data = (void *)(&workers); + // Caller needs to check userid/expirydate if the result != NULL + return find_after_in_ktree(workers_root, &look, cmp_workers, ctx); +} + K_ITEM *new_worker(PGconn *conn, bool update, int64_t userid, char *workername, char *diffdef, char *idlenotificationenabled, char *idlenotificationtime, char *by, diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 91e43fd8..5b3c1544 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -2183,6 +2183,13 @@ K_ITEM *optioncontrol_item_add(PGconn *conn, K_ITEM *oc_item, tv_t *cd, bool beg DATA_OPTIONCONTROL(row, oc_item); + // Enforce the rule that switch_state isn't date/height controlled + if (strcmp(row->optionname, SWITCH_STATE_NAME) == 0) { + row->activationdate.tv_sec = date_begin.tv_sec; + row->activationdate.tv_usec = date_begin.tv_usec; + row->activationheight = OPTIONCONTROL_HEIGHT; + } + INIT_OPTIONCONTROL(&look); look.data = (void *)row; K_RLOCK(optioncontrol_free); @@ -2282,6 +2289,11 @@ nostart: } optioncontrol_root = add_to_ktree(optioncontrol_root, oc_item, cmp_optioncontrol); k_add_head(optioncontrol_store, oc_item); + if (strcmp(row->optionname, SWITCH_STATE_NAME) == 0) { + switch_state = atoi(row->optionvalue); + LOGWARNING("%s() set switch_state to %d", + __func__, switch_state); + } } K_WUNLOCK(optioncontrol_free); @@ -2320,7 +2332,7 @@ K_ITEM *optioncontrol_add(PGconn *conn, char *optionname, char *optionvalue, TXT_TO_INT("activationheight", activationheight, row->activationheight); } else - row->activationheight = 1; + row->activationheight = OPTIONCONTROL_HEIGHT; HISTORYDATEINIT(row, cd, by, code, inet); HISTORYDATETRANSFER(trf_root, row); @@ -2406,6 +2418,14 @@ bool optioncontrol_fill(PGconn *conn) optioncontrol_root = add_to_ktree(optioncontrol_root, item, cmp_optioncontrol); k_add_head(optioncontrol_store, item); + + // There should only be one CURRENT version of switch_state + if (CURRENT(&(row->expirydate)) && + strcmp(row->optionname, SWITCH_STATE_NAME) == 0) { + switch_state = atoi(row->optionvalue); + LOGWARNING("%s() set switch_state to %d", + __func__, switch_state); + } } if (!ok) { FREENULL(row->optionvalue); @@ -2418,6 +2438,8 @@ bool optioncontrol_fill(PGconn *conn) if (ok) { LOGDEBUG("%s(): built", __func__); LOGWARNING("%s(): loaded %d optioncontrol records", __func__, n); + LOGWARNING("%s() switch_state initially %d", + __func__, switch_state); } return ok;