diff --git a/src/ckdb.c b/src/ckdb.c index a1255fca..60c2b291 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -479,9 +479,10 @@ static const char *hashpatt = "^[A-Fa-f0-9]*$"; #define JSON_TRANSFER_LEN (sizeof(JSON_TRANSFER)-1) // Methods for sharelog (common function for all 3) -#define METHOD_WORKINFO "workinfo" -#define METHOD_SHARES "shares" -#define METHOD_SHAREERRORS "shareerror" +#define STR_WORKINFO "workinfo" +#define STR_SHARES "shares" +#define STR_SHAREERRORS "shareerror" +#define STR_AGEWORKINFO "ageworkinfo" // TRANSFER #define NAME_SIZE 63 @@ -793,11 +794,10 @@ typedef struct sharesummary { #define DATA_SHARESUMMARY(_item) ((SHARESUMMARY *)(_item->data)) #define SUMMARY_NEW 'n' +#define SUMMARY_AGED 'a' #define SUMMARY_CONFIRM 'y' static K_TREE *sharesummary_root; -/* TODO: for 'complete' flagging i.e. - older items should be flagged if we flag a newer item */ static K_TREE *sharesummary_workinfoid_root; static K_LIST *sharesummary_list; static K_STORE *sharesummary_store; @@ -2468,6 +2468,87 @@ unparam: return workinfoid; } +static double cmp_shares(K_ITEM *a, K_ITEM *b); +static double cmp_sharesummary_workinfoid(K_ITEM *a, K_ITEM *b); +static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, K_ITEM *ss_item, + tv_t *now, char *by, char *code, char *inet); + +static bool workinfo_age(PGconn *conn, char *workinfoidstr, char *poolinstance, + tv_t *now, char *by, char *code, char *inet) +{ + K_ITEM *wi_item, ss_look, *ss_item, s_look, *s_item, *tmp_item; + K_TREE_CTX ss_ctx[1], s_ctx[1], tmp_ctx[1]; + int64_t workinfoid; + SHARESUMMARY sharesummary; + SHARES shares; + bool ok = false, conned = false; + + LOGDEBUG("%s(): complete", __func__); + + TXT_TO_BIGINT("workinfoid", workinfoidstr, workinfoid); + + wi_item = find_workinfo(workinfoid); + if (!wi_item) + goto bye; + + if (strcmp(poolinstance, DATA_WORKINFO(wi_item)->poolinstance) != 0) + goto bye; + + // Find the first matching sharesummary + sharesummary.workinfoid = workinfoid; + sharesummary.userid = -1; + sharesummary.workername[0] = '\0'; + + ok = true; + ss_look.data = (void *)(&sharesummary); + ss_item = find_after_in_ktree(sharesummary_workinfoid_root, &ss_look, cmp_sharesummary_workinfoid, ss_ctx); + while (ss_item && DATA_SHARESUMMARY(ss_item)->workinfoid == workinfoid) { + if (conn == NULL) { + conn = dbconnect(); + conned = true; + } + + if (!sharesummary_update(conn, NULL, NULL, ss_item, now, by, code, inet)) { + LOGERR("%s(): Failed to age share summary %"PRId64"/%s/%"PRId64, + __func__, DATA_SHARESUMMARY(ss_item)->userid, + DATA_SHARESUMMARY(ss_item)->workername, + DATA_SHARESUMMARY(ss_item)->workinfoid); + ok = false; + } + + // Discard the shares either way + shares.workinfoid = workinfoid; + shares.userid = DATA_SHARESUMMARY(ss_item)->userid; + strcpy(shares.workername, DATA_SHARESUMMARY(ss_item)->workername); + shares.createdate.tv_sec = 0; + shares.createdate.tv_usec = 0; + + s_look.data = (void *)(&shares); + s_item = find_after_in_ktree(shares_root, &s_look, cmp_shares, s_ctx); + K_WLOCK(shares_list); + while (s_item) { + if (DATA_SHARES(s_item)->workinfoid != workinfoid || + DATA_SHARES(s_item)->userid != shares.userid || + strcmp(DATA_SHARES(s_item)->workername, shares.workername) != 0) + break; + + tmp_item = next_in_ktree(s_ctx); + shares_root = remove_from_ktree(shares_root, s_item, cmp_shares, tmp_ctx); + k_unlink_item(shares_store, s_item); + k_add_head(shares_list, s_item); + s_item = tmp_item; + } + K_WUNLOCK(shares_list); + ss_item = next_in_ktree(ss_ctx); + } + + if (conned) + PQfinish(conn); + +bye: + return ok; +} + static bool workinfo_fill(PGconn *conn) { ExecStatusType rescode; @@ -2611,7 +2692,7 @@ void workinfo_reload() */ } -// order by workinfoid asc,userid asc,createdate asc,nonce asc,expirydate desc +// order by workinfoid asc,userid asc,workername asc,createdate asc,nonce asc,expirydate desc static double cmp_shares(K_ITEM *a, K_ITEM *b) { double c = (double)(DATA_SHARES(a)->workinfoid - @@ -2620,14 +2701,18 @@ static double cmp_shares(K_ITEM *a, K_ITEM *b) c = (double)(DATA_SHARES(a)->userid - DATA_SHARES(b)->userid); if (c == 0) { - c = tvdiff(&(DATA_SHARES(a)->createdate), - &(DATA_SHARES(b)->createdate)); + c = strcmp(DATA_SHARES(a)->workername, + DATA_SHARES(b)->workername); if (c == 0) { - c = strcmp(DATA_SHARES(a)->nonce, - DATA_SHARES(b)->nonce); + c = tvdiff(&(DATA_SHARES(a)->createdate), + &(DATA_SHARES(b)->createdate)); if (c == 0) { - c = tvdiff(&(DATA_SHARES(b)->expirydate), - &(DATA_SHARES(a)->expirydate)); + c = strcmp(DATA_SHARES(a)->nonce, + DATA_SHARES(b)->nonce); + if (c == 0) { + c = tvdiff(&(DATA_SHARES(b)->expirydate), + &(DATA_SHARES(a)->expirydate)); + } } } } @@ -2635,9 +2720,6 @@ static double cmp_shares(K_ITEM *a, K_ITEM *b) return c; } -static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, - tv_t *now, char *by, char *code, char *inet); - // Memory (and log file) only static bool shares_add(char *workinfoid, char *username, char *workername, char *clientid, char *enonce1, char *nonce2, char *nonce, char *diff, char *sdiff, @@ -2684,7 +2766,7 @@ static bool shares_add(char *workinfoid, char *username, char *workername, char if (!w_item) goto unitem; - sharesummary_update(NULL, shares, NULL, now, by, code, inet); + sharesummary_update(NULL, shares, NULL, NULL, now, by, code, inet); ok = true; unitem: @@ -2771,7 +2853,7 @@ static bool shareerrors_add(char *workinfoid, char *username, char *workername, if (!w_item) goto unitem; - sharesummary_update(NULL, NULL, shareerrors, now, by, code, inet); + sharesummary_update(NULL, NULL, shareerrors, NULL, now, by, code, inet); ok = true; unitem: @@ -2865,10 +2947,7 @@ static K_ITEM *find_sharesummary(int64_t userid, char *workername, int64_t worki return find_in_ktree(sharesummary_root, &look, cmp_sharesummary, ctx); } -// TODO: discard shares,shareerrors in caller if this returns true? -// or keep them for a specific history so can check the errors? -// or error checking should use the log files? -static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, +static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, K_ITEM *ss_item, tv_t *now, char *by, char *code, char *inet) { ExecStatusType rescode; @@ -2882,115 +2961,128 @@ static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, int64_t userid, workinfoid; char *workername; tv_t *sharecreatedate; - bool conned = false; + bool must_update = false, conned = false; - LOGDEBUG("%s(): add", __func__); + LOGDEBUG("%s(): update", __func__); - if (s_row) { - if (e_row) { - quithere(1, "ERR: %s() one of s_row and e_row must be NULL", - __func__); - } - userid = s_row->userid; - workername = s_row->workername; - workinfoid = s_row->workinfoid; - sharecreatedate = &(s_row->createdate); - } else { - if (!e_row) { - quithere(1, "ERR: %s() both s_row and e_row are NULL", + if (ss_item) { + if (s_row || e_row) { + quithere(1, "ERR: %s() only one of s_row, e_row and ss_item allowed", __func__); } - userid = e_row->userid; - workername = e_row->workername; - workinfoid = e_row->workinfoid; - sharecreatedate = &(e_row->createdate); - } - - item = find_sharesummary(userid, workername, workinfoid); - if (item) { new = false; + item = ss_item; row = DATA_SHARESUMMARY(item); - } else { - new = true; - K_WLOCK(sharesummary_list); - item = k_unlink_head(sharesummary_list); - K_WUNLOCK(sharesummary_list); - row = DATA_SHARESUMMARY(item); - row->userid = userid; - STRNCPY(row->workername, workername); - row->workinfoid = workinfoid; - row->diffacc = row->diffsta = row->diffdup = row->diffhi = - row->diffrej = row->shareacc = row->sharesta = row->sharedup = - row->sharehi = row->sharerej = 0.0; - row->sharecount = row->errorcount = row->countlastupdate = 0; - row->inserted = false; - row->firstshare.tv_sec = sharecreatedate->tv_sec; - row->firstshare.tv_usec = sharecreatedate->tv_usec; - row->lastshare.tv_sec = row->firstshare.tv_sec; - row->lastshare.tv_usec = row->firstshare.tv_usec; - row->complete[0] = SUMMARY_NEW; + must_update = true; + row->complete[0] = SUMMARY_AGED; row->complete[1] = '\0'; - } - - if (e_row) - row->errorcount += 1; - else { - row->sharecount += 1; - switch (s_row->errn) { - case SE_NONE: - row->diffacc += s_row->diff; - row->shareacc++; - break; - case SE_STALE: - row->diffsta += s_row->diff; - row->sharesta++; - break; - case SE_DUPE: - row->diffdup += s_row->diff; - row->sharedup++; - break; - case SE_HIGH_DIFF: - row->diffhi += s_row->diff; - row->sharehi++; - break; - default: - row->diffrej += s_row->diff; - row->sharerej++; - break; + } else { + if (s_row) { + if (e_row) { + quithere(1, "ERR: %s() only one of s_row, e_row (and ss_item) allowed", + __func__); + } + userid = s_row->userid; + workername = s_row->workername; + workinfoid = s_row->workinfoid; + sharecreatedate = &(s_row->createdate); + } else { + if (!e_row) { + quithere(1, "ERR: %s() all s_row, e_row and ss_item are NULL", + __func__); + } + userid = e_row->userid; + workername = e_row->workername; + workinfoid = e_row->workinfoid; + sharecreatedate = &(e_row->createdate); } - } - if (!new) { - double td; - td = tvdiff(sharecreatedate, &(row->firstshare)); - if (td <= 0.0) { - char *tmp1, *tmp2; - LOGERR("%s(): %s createdate (%s) is before summary firstshare (%s)", - __func__, s_row ? "shares" : "shareerrors", - (tmp1 = ctv_to_buf(sharecreatedate, NULL, 0)), - (tmp2 = ctv_to_buf(&(row->firstshare), NULL, 0))); - free(tmp2); - free(tmp1); + item = find_sharesummary(userid, workername, workinfoid); + if (item) { + new = false; + row = DATA_SHARESUMMARY(item); + } else { + new = true; + K_WLOCK(sharesummary_list); + item = k_unlink_head(sharesummary_list); + K_WUNLOCK(sharesummary_list); + row = DATA_SHARESUMMARY(item); + row->userid = userid; + STRNCPY(row->workername, workername); + row->workinfoid = workinfoid; + row->diffacc = row->diffsta = row->diffdup = row->diffhi = + row->diffrej = row->shareacc = row->sharesta = row->sharedup = + row->sharehi = row->sharerej = 0.0; + row->sharecount = row->errorcount = row->countlastupdate = 0; + row->inserted = false; row->firstshare.tv_sec = sharecreatedate->tv_sec; row->firstshare.tv_usec = sharecreatedate->tv_usec; + row->lastshare.tv_sec = row->firstshare.tv_sec; + row->lastshare.tv_usec = row->firstshare.tv_usec; + row->complete[0] = SUMMARY_NEW; + row->complete[1] = '\0'; } - td = tvdiff(sharecreatedate, &(row->lastshare)); - if (td > 0.0) { - row->lastshare.tv_sec = sharecreatedate->tv_sec; - row->lastshare.tv_usec = sharecreatedate->tv_usec; - } else { - char *tmp1, *tmp2; - LOGERR("%s(): %s createdate (%s) is before summary lastshare (%s)", - __func__, s_row ? "shares" : "shareerrors", - (tmp1 = ctv_to_buf(sharecreatedate, NULL, 0)), - (tmp2 = ctv_to_buf(&(row->lastshare), NULL, 0))); - free(tmp2); - free(tmp1); + + if (e_row) + row->errorcount += 1; + else { + row->sharecount += 1; + switch (s_row->errn) { + case SE_NONE: + row->diffacc += s_row->diff; + row->shareacc++; + break; + case SE_STALE: + row->diffsta += s_row->diff; + row->sharesta++; + break; + case SE_DUPE: + row->diffdup += s_row->diff; + row->sharedup++; + break; + case SE_HIGH_DIFF: + row->diffhi += s_row->diff; + row->sharehi++; + break; + default: + row->diffrej += s_row->diff; + row->sharerej++; + break; + } } - if (row->complete[0] != SUMMARY_NEW) { - LOGDEBUG("%s(): updating sharesummary not '%c' %"PRId64"/%s/%"PRId64"/%s", - __func__, SUMMARY_NEW, row->userid, row->workername, - row->workinfoid, row->complete); + + if (!new) { + double td; + td = tvdiff(sharecreatedate, &(row->firstshare)); + if (td <= 0.0) { + char *tmp1, *tmp2; + LOGERR("%s(): %s createdate (%s) is before summary firstshare (%s)", + __func__, s_row ? "shares" : "shareerrors", + (tmp1 = ctv_to_buf(sharecreatedate, NULL, 0)), + (tmp2 = ctv_to_buf(&(row->firstshare), NULL, 0))); + free(tmp2); + free(tmp1); + row->firstshare.tv_sec = sharecreatedate->tv_sec; + row->firstshare.tv_usec = sharecreatedate->tv_usec; + } + td = tvdiff(sharecreatedate, &(row->lastshare)); + if (td > 0.0) { + row->lastshare.tv_sec = sharecreatedate->tv_sec; + row->lastshare.tv_usec = sharecreatedate->tv_usec; + } else { + char *tmp1, *tmp2; + LOGERR("%s(): %s createdate (%s) is before summary lastshare (%s)", + __func__, s_row ? "shares" : "shareerrors", + (tmp1 = ctv_to_buf(sharecreatedate, NULL, 0)), + (tmp2 = ctv_to_buf(&(row->lastshare), NULL, 0))); + free(tmp2); + free(tmp1); + } + if (row->complete[0] != SUMMARY_NEW) { + LOGDEBUG("%s(): updating sharesummary not '%c' %"PRId64"/%s/%"PRId64"/%s", + __func__, SUMMARY_NEW, row->userid, row->workername, + row->workinfoid, row->complete); + } } } @@ -3040,10 +3132,18 @@ static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, row->countlastupdate = row->sharecount + row->errorcount; row->inserted = true; } else { + bool stats_update = false; + MODIFYUPDATE(row, now, by, code, inet); if ((row->countlastupdate + SHARESUMMARY_UPDATE_EVERY) < - (row->sharecount + row->errorcount)) { + (row->sharecount + row->errorcount)) + stats_update = true; + + if (must_update && row->countlastupdate < (row->sharecount + row->errorcount)) + stats_update = true; + + if (stats_update) { par = 0; params[par++] = bigint_to_buf(row->userid, NULL, 0); params[par++] = str_to_buf(row->workername, NULL, 0); @@ -3060,26 +3160,49 @@ static bool sharesummary_update(PGconn *conn, SHARES *s_row, SHAREERRORS *e_row, params[par++] = double_to_buf(row->sharerej, NULL, 0); params[par++] = tv_to_buf(&(row->firstshare), NULL, 0); params[par++] = tv_to_buf(&(row->lastshare), NULL, 0); + params[par++] = str_to_buf(row->complete, NULL, 0); MODIFYUPDATEPARAMS(params, par, row); - PARCHKVAL(par, 19, params); + PARCHKVAL(par, 20, params); upd = "update sharesummary " "set diffacc=$4,diffsta=$5,diffdup=$6,diffhi=$7,diffrej=$8," "shareacc=$9,sharesta=$10,sharedup=$11,sharehi=$12," - "sharerej=$13,firstshare=$14,lastshare=$15" - ",modifydate=$16,modifyby=$17,modifycode=$18,modifyinet=$19 " + "sharerej=$13,firstshare=$14,lastshare=$15,complete=$16" + ",modifydate=$17,modifyby=$18,modifycode=$19,modifyinet=$20 " "where userid=$1 and workername=$2 and workinfoid=$3"; res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0); rescode = PQresultStatus(res); if (!PGOK(rescode)) { - PGLOGERR("Insert", rescode, conn); + PGLOGERR("Update", rescode, conn); goto unparam; } row->countlastupdate = row->sharecount + row->errorcount; } else { - ok = true; - goto late; + if (!must_update) { + ok = true; + goto late; + } else { + par = 0; + params[par++] = bigint_to_buf(row->userid, NULL, 0); + params[par++] = str_to_buf(row->workername, NULL, 0); + params[par++] = bigint_to_buf(row->workinfoid, NULL, 0); + params[par++] = str_to_buf(row->complete, NULL, 0); + MODIFYUPDATEPARAMS(params, par, row); + PARCHKVAL(par, 8, params); + + upd = "update sharesummary " + "set complete=$4,modifydate=$5,modifyby=$6,modifycode=$7,modifyinet=$8 " + "where userid=$1 and workername=$2 and workinfoid=$3"; + + res = PQexecParams(conn, upd, par, NULL, (const char **)params, NULL, NULL, 0); + rescode = PQresultStatus(res); + if (!PGOK(rescode)) { + PGLOGERR("MustUpdate", rescode, conn); + goto unparam; + } + row->countlastupdate = row->sharecount + row->errorcount; + } } } @@ -3092,7 +3215,7 @@ late: if (conned) PQfinish(conn); - // We keep the new item anyway since it can be inserted next time + // We keep the new item no matter what 'ok' is, since it will be inserted later K_WLOCK(sharesummary_list); if (new) { sharesummary_root = add_to_ktree(sharesummary_root, item, cmp_sharesummary); @@ -4798,7 +4921,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ __maybe_unused char *code, __maybe_unused char *inet) { K_ITEM *i_username, *i_stats, wlook, *u_item, *w_item, uslook, *us_item; - K_TREE_CTX wctx[1], usctx[1]; + K_TREE_CTX w_ctx[1], us_ctx[1]; WORKERS workers; USERSTATS userstats; char reply[1024] = ""; @@ -4830,7 +4953,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ workers.expirydate.tv_sec = 0; workers.expirydate.tv_usec = 0; wlook.data = (void *)(&workers); - w_item = find_after_in_ktree(workers_root, &wlook, cmp_workers, wctx); + w_item = find_after_in_ktree(workers_root, &wlook, cmp_workers, w_ctx); APPEND_REALLOC_INIT(buf, off, len); APPEND_REALLOC(buf, off, len, "ok."); rows = 0; @@ -4854,7 +4977,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ if (stats) { K_TREE *userstats_workername_root = new_ktree(); - K_TREE_CTX uswctx[1]; + K_TREE_CTX usw_ctx[1]; double w_hashrate5m, w_hashrate1hr; int64_t w_elapsed; tv_t w_lastshare; @@ -4871,7 +4994,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ userstats.poolinstance[0] = '\0'; userstats.workername[0] = '\0'; uslook.data = (void *)(&userstats); - us_item = find_before_in_ktree(userstats_root, &uslook, cmp_userstats, usctx); + us_item = find_before_in_ktree(userstats_root, &uslook, cmp_userstats, us_ctx); while (us_item && DATA_USERSTATS(us_item)->userid == userstats.userid) { if (strcmp(DATA_USERSTATS(us_item)->workername, DATA_WORKERS(w_item)->workername) == 0) { // first found is the newest share @@ -4880,7 +5003,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ if (tvdiff(now, &(DATA_USERSTATS(us_item)->createdate)) < USERSTATS_PER_S) { // TODO: add together the latest per pool instance (this is the latest per worker) - if (!find_in_ktree(userstats_workername_root, us_item, cmp_userstats_workername, uswctx)) { + if (!find_in_ktree(userstats_workername_root, us_item, cmp_userstats_workername, usw_ctx)) { w_hashrate5m += DATA_USERSTATS(us_item)->hashrate5m; w_hashrate1hr += DATA_USERSTATS(us_item)->hashrate1hr; if (w_elapsed == -1 || w_elapsed > DATA_USERSTATS(us_item)->elapsed) @@ -4894,7 +5017,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ break; } - us_item = prev_in_ktree(usctx); + us_item = prev_in_ktree(us_ctx); } double_to_buf(w_hashrate5m, reply, sizeof(reply)); @@ -4918,7 +5041,7 @@ static char *cmd_workers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe_ rows++; } - w_item = next_in_ktree(wctx); + w_item = next_in_ktree(w_ctx); } snprintf(tmp, sizeof(tmp), "rows=%d", rows); APPEND_REALLOC(buf, off, len, tmp); @@ -4932,7 +5055,7 @@ static char *cmd_allusers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe { K_TREE *userstats_workername_root = new_ktree(); K_ITEM *us_item, *usw_item, *tmp_item, *u_item; - K_TREE_CTX usctx[1], uswctx[1]; + K_TREE_CTX us_ctx[1], usw_ctx[1]; char reply[1024] = ""; char tmp[1024]; char *buf; @@ -4945,9 +5068,9 @@ static char *cmd_allusers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe // Find last records for each user/worker in ALLUSERS_LIMIT_S // TODO: include pool_instance - us_item = last_in_ktree(userstats_root, usctx); + us_item = last_in_ktree(userstats_root, us_ctx); while (us_item && tvdiff(now, &(DATA_USERSTATS(us_item)->createdate)) < ALLUSERS_LIMIT_S) { - usw_item = find_in_ktree(userstats_workername_root, us_item, cmp_userstats_workername, uswctx); + usw_item = find_in_ktree(userstats_workername_root, us_item, cmp_userstats_workername, usw_ctx); if (!usw_item) { K_WLOCK(userstats_list); usw_item = k_unlink_head(userstats_list); @@ -4966,7 +5089,7 @@ static char *cmd_allusers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe APPEND_REALLOC(buf, off, len, "ok."); rows = 0; // Add up per user - usw_item = first_in_ktree(userstats_workername_root, uswctx); + usw_item = first_in_ktree(userstats_workername_root, usw_ctx); while (usw_item) { if (DATA_USERSTATS(usw_item)->userid != userid) { if (userid != -1) { @@ -4996,7 +5119,7 @@ static char *cmd_allusers(char *cmd, char *id, __maybe_unused tv_t *now, __maybe u_hashrate1hr += DATA_USERSTATS(usw_item)->hashrate1hr; tmp_item = usw_item; - usw_item = tmp_item->next; + usw_item = next_in_ktree(usw_ctx); K_WLOCK(userstats_list); k_add_head(userstats_list, tmp_item); @@ -5043,7 +5166,7 @@ static char *cmd_sharelog(char *cmd, char *id, tv_t *now, char *by, char *code, LOGDEBUG("%s(): cmd '%s'", __func__, cmd); - if (strcasecmp(cmd, METHOD_WORKINFO) == 0) { + if (strcasecmp(cmd, STR_WORKINFO) == 0) { K_ITEM *i_workinfoid, *i_poolinstance, *i_transactiontree, *i_merklehash; K_ITEM *i_prevhash, *i_coinbase1, *i_coinbase2, *i_version, *i_bits; K_ITEM *i_ntime, *i_reward; @@ -5115,7 +5238,7 @@ static char *cmd_sharelog(char *cmd, char *id, tv_t *now, char *by, char *code, LOGDEBUG("%s.ok.added %"PRId64, id, workinfoid); snprintf(reply, siz, "ok.%"PRId64, workinfoid); return strdup(reply); - } else if (strcasecmp(cmd, METHOD_SHARES) == 0) { + } else if (strcasecmp(cmd, STR_SHARES) == 0) { K_ITEM *i_workinfoid, *i_username, *i_workername, *i_clientid, *i_enonce1; K_ITEM *i_nonce2, *i_nonce, *i_diff, *i_sdiff, *i_secondaryuserid; bool ok; @@ -5179,7 +5302,7 @@ static char *cmd_sharelog(char *cmd, char *id, tv_t *now, char *by, char *code, LOGDEBUG("%s.ok.added %s", id, DATA_TRANSFER(i_nonce)->data); snprintf(reply, siz, "ok.added %s", DATA_TRANSFER(i_nonce)->data); return strdup(reply); - } else if (strcasecmp(cmd, METHOD_SHAREERRORS) == 0) { + } else if (strcasecmp(cmd, STR_SHAREERRORS) == 0) { K_ITEM *i_workinfoid, *i_username, *i_workername, *i_clientid, *i_errn; K_ITEM *i_error, *i_secondaryuserid; bool ok; @@ -5226,6 +5349,34 @@ static char *cmd_sharelog(char *cmd, char *id, tv_t *now, char *by, char *code, } LOGDEBUG("%s.ok.added %s", id, DATA_TRANSFER(i_username)->data); snprintf(reply, siz, "ok.added %s", DATA_TRANSFER(i_username)->data); + return strdup(reply); + } else if (strcasecmp(cmd, STR_AGEWORKINFO) == 0) { + K_ITEM *i_workinfoid, *i_poolinstance; + bool ok; + + i_workinfoid = require_name("workinfoid", 1, NULL, reply, siz); + if (!i_workinfoid) + return strdup(reply); + + i_poolinstance = require_name("poolinstance", 1, NULL, reply, siz); + if (!i_poolinstance) + return strdup(reply); + + ok = workinfo_age(NULL, DATA_TRANSFER(i_workinfoid)->data, + DATA_TRANSFER(i_poolinstance)->data, + now, by, code, inet); + + if (!ok) { + LOGERR("%s.failed.DATA", id); + return strdup("failed.DATA"); + } + LOGDEBUG("%s.ok.aged %.*s", + id, BIGINT_BUFSIZ, + DATA_TRANSFER(i_workinfoid)->data); + snprintf(reply, siz, "ok.%.*s", + BIGINT_BUFSIZ, + DATA_TRANSFER(i_workinfoid)->data); + return strdup(reply); } LOGERR("%s.bad.cmd %s", cmd); @@ -5398,7 +5549,7 @@ static char *cmd_homepage(char *cmd, char *id, __maybe_unused tv_t *now, __maybe char reply[1024], tmp[1024], *buf; USERSTATS userstats; int64_t u_elapsed; - K_TREE_CTX ctx[1], wctx[1]; + K_TREE_CTX ctx[1], w_ctx[1]; size_t len, off; bool has_uhr; @@ -5483,7 +5634,7 @@ static char *cmd_homepage(char *cmd, char *id, __maybe_unused tv_t *now, __maybe DATA_USERSTATS(us_item)->userid == userstats.userid && tvdiff(now, &(DATA_USERSTATS(us_item)->createdate)) < USERSTATS_PER_S) { // TODO: add the latest per pool instance (this is the latest per worker) - if (!find_in_ktree(userstats_workername_root, us_item, cmp_userstats_workername, wctx)) { + if (!find_in_ktree(userstats_workername_root, us_item, cmp_userstats_workername, w_ctx)) { u_hashrate5m += DATA_USERSTATS(us_item)->hashrate5m; u_hashrate1hr += DATA_USERSTATS(us_item)->hashrate1hr; if (u_elapsed == -1 || @@ -5584,24 +5735,25 @@ static struct CMDS { char *(*func)(char *, char *, tv_t *, char *, char *, char *); char *access; } cmds[] = { - { CMD_SHUTDOWN, "shutdown", NULL, ACCESS_SYSTEM }, - { CMD_PING, "ping", NULL, ACCESS_SYSTEM ACCESS_POOL ACCESS_WEB }, - { CMD_SHARELOG, "workinfo", cmd_sharelog, ACCESS_POOL }, - { CMD_SHARELOG, "shares", cmd_sharelog, ACCESS_POOL }, - { CMD_SHARELOG, "shareerrors", cmd_sharelog, ACCESS_POOL }, - { CMD_AUTH, "authorise", cmd_auth, ACCESS_POOL }, - { CMD_ADDUSER, "adduser", cmd_adduser, ACCESS_WEB }, - { CMD_CHKPASS, "chkpass", cmd_chkpass, ACCESS_WEB }, - { CMD_POOLSTAT, "poolstats", cmd_poolstats, ACCESS_POOL }, - { CMD_USERSTAT, "userstats", cmd_userstats, ACCESS_POOL }, - { CMD_BLOCK, "block", cmd_blocks, ACCESS_POOL }, - { CMD_NEWID, "newid", cmd_newid, ACCESS_SYSTEM }, - { CMD_PAYMENTS, "payments", cmd_payments, ACCESS_WEB }, - { CMD_WORKERS, "workers", cmd_workers, ACCESS_WEB }, - { CMD_ALLUSERS, "allusers", cmd_allusers, ACCESS_WEB }, - { CMD_HOMEPAGE, "homepage", cmd_homepage, ACCESS_WEB }, - { CMD_DSP, "dsp", cmd_dsp, ACCESS_SYSTEM }, - { CMD_END, NULL, NULL, NULL } + { CMD_SHUTDOWN, "shutdown", NULL, ACCESS_SYSTEM }, + { CMD_PING, "ping", NULL, ACCESS_SYSTEM ACCESS_POOL ACCESS_WEB }, + { CMD_SHARELOG, STR_WORKINFO, cmd_sharelog, ACCESS_POOL }, + { CMD_SHARELOG, STR_SHARES, cmd_sharelog, ACCESS_POOL }, + { CMD_SHARELOG, STR_SHAREERRORS, cmd_sharelog, ACCESS_POOL }, + { CMD_SHARELOG, STR_AGEWORKINFO, cmd_sharelog, ACCESS_POOL }, + { CMD_AUTH, "authorise", cmd_auth, ACCESS_POOL }, + { CMD_ADDUSER, "adduser", cmd_adduser, ACCESS_WEB }, + { CMD_CHKPASS, "chkpass", cmd_chkpass, ACCESS_WEB }, + { CMD_POOLSTAT, "poolstats", cmd_poolstats, ACCESS_POOL }, + { CMD_USERSTAT, "userstats", cmd_userstats, ACCESS_POOL }, + { CMD_BLOCK, "block", cmd_blocks, ACCESS_POOL }, + { CMD_NEWID, "newid", cmd_newid, ACCESS_SYSTEM }, + { CMD_PAYMENTS, "payments", cmd_payments, ACCESS_WEB }, + { CMD_WORKERS, "workers", cmd_workers, ACCESS_WEB }, + { CMD_ALLUSERS, "allusers", cmd_allusers, ACCESS_WEB }, + { CMD_HOMEPAGE, "homepage", cmd_homepage, ACCESS_WEB }, + { CMD_DSP, "dsp", cmd_dsp, ACCESS_SYSTEM }, + { CMD_END, NULL, NULL, NULL } }; static enum cmd_values breakdown(char *buf, int *which_cmds, char *cmd, char *id)