From 21c1aa10fcfdbc49860a49fe3cde7f51d324c547 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 28 Nov 2014 13:36:25 +1100 Subject: [PATCH 01/21] ckdb - stat report every 10s during logger shutdown - if it's slow --- src/ckdb.c | 18 ++++++++++++++---- src/ckdb.h | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ckdb.c b/src/ckdb.c index 71e5eed3..98a2fd91 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -2016,7 +2016,8 @@ static void *logger(__maybe_unused void *arg) K_ITEM *lq_item; LOGQUEUE *lq; char buf[128]; - tv_t now; + tv_t now, then; + int count; pthread_detach(pthread_self()); @@ -2051,18 +2052,26 @@ static void *logger(__maybe_unused void *arg) } K_WLOCK(logqueue_free); + count = logqueue_store->count; setnow(&now); snprintf(buf, sizeof(buf), "logstopping.%d.%ld,%ld", - logqueue_store->count, - now.tv_sec, now.tv_usec); + count, now.tv_sec, now.tv_usec); LOGFILE(buf); - if (logqueue_store->count) + if (count) LOGERR("%s", buf); lq_item = logqueue_store->head; + copy_tv(&then, &now); while (lq_item) { DATA_LOGQUEUE(lq, lq_item); LOGFILE(lq->msg); free(lq->msg); + count--; + setnow(&now); + if ((now.tv_sec - then.tv_sec) > 10) { + snprintf(buf, sizeof(buf), "logging ... %d", count); + LOGERR("%s", buf); + copy_tv(&then, &now); + } lq_item = lq_item->next; } K_WUNLOCK(logqueue_free); @@ -2073,6 +2082,7 @@ static void *logger(__maybe_unused void *arg) snprintf(buf, sizeof(buf), "logstop.%ld,%ld", now.tv_sec, now.tv_usec); LOGFILE(buf); + LOGWARNING("%s", buf); return NULL; } diff --git a/src/ckdb.h b/src/ckdb.h index 9ed96f68..39a26dc7 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.665" +#define CKDB_VERSION DB_VERSION"-0.666" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ From 1e578f4e89b4ca217fb6a2ce11c5e26ad312b663 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 28 Nov 2014 14:38:48 +1100 Subject: [PATCH 02/21] ckdb - optioncontrol AutoAddUser to auth/add unknown users automatically --- src/ckdb.h | 8 +++++++- src/ckdb_cmd.c | 22 ++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/ckdb.h b/src/ckdb.h index 39a26dc7..e7c0187a 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.666" +#define CKDB_VERSION DB_VERSION"-0.667" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -857,6 +857,12 @@ typedef struct optioncontrol { #error "START_POOL_HEIGHT must = (OPTIONCONTROL_HEIGHT+1)" #endif +/* If set, then cmd_auth() will create unknown users + * It will use the optionvalue as the hex sha256 password hash + * A blank or random/invalid hash will mean the accounts created + * are password locked, like an address account is */ +#define OPTIONCONTROL_AUTOADDUSER "AutoAddUser" + extern K_TREE *optioncontrol_root; extern K_LIST *optioncontrol_free; extern K_STORE *optioncontrol_store; diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 1e9ac011..bcf74968 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -1801,9 +1801,11 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by, 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, *u_item, *oc_item; USERS *users = NULL; + char *username; WORKERS *workers = NULL; + OPTIONCONTROL *optioncontrol; bool ok; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -1816,6 +1818,7 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by, i_username = require_name(trf_root, "username", 1, NULL, reply, siz); if (!i_username) return strdup(reply); + username = transfer_data(i_username); i_workername = require_name(trf_root, "workername", 1, NULL, reply, siz); if (!i_workername) @@ -1837,8 +1840,23 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by, if (!i_preauth) i_preauth = &auth_preauth; + K_RLOCK(optioncontrol_free); + oc_item = find_optioncontrol(OPTIONCONTROL_AUTOADDUSER, cd); + K_RUNLOCK(optioncontrol_free); + if (oc_item) { + K_RLOCK(users_free); + u_item = find_users(username); + K_RUNLOCK(users_free); + if (!u_item) { + DATA_OPTIONCONTROL(optioncontrol, oc_item); + u_item = users_add(conn, username, EMPTY, + optioncontrol->optionvalue, + by, code, inet, cd, trf_root); + } + } + ok = auths_add(conn, transfer_data(i_poolinstance), - transfer_data(i_username), + username, transfer_data(i_workername), transfer_data(i_clientid), transfer_data(i_enonce1), From 503ec09f27fc162a0e063537657a1efc7b571cef Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 28 Nov 2014 17:33:34 +1100 Subject: [PATCH 03/21] make LOG* macros typecheck and fix related errors/warnings --- src/ckdb.c | 4 ++-- src/ckdb.h | 2 +- src/ckdb_btc.c | 2 +- src/ckdb_cmd.c | 2 +- src/ckdb_data.c | 8 ++++---- src/connector.c | 12 ++++++------ src/libckpool.h | 27 +++++++++++++++++++-------- src/stratifier.c | 36 +++++++++++++++++++++++------------- 8 files changed, 57 insertions(+), 36 deletions(-) diff --git a/src/ckdb.c b/src/ckdb.c index 98a2fd91..6bc2239a 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -3160,7 +3160,7 @@ static void confirm_reload() } if (confirm_last_workinfoid == 0) { LOGWARNING("%s(): there are no unconfirmed sharesummary records in the DB", - __func__, buf); + __func__); return; } @@ -3230,7 +3230,7 @@ static void confirm_reload() // last from default if (confirm_last_workinfoid < confirm_first_workinfoid) { LOGWARNING("%s(): no unconfirmed sharesummary records before start", - __func__, buf); + __func__); return; } first_reason = "start range"; diff --git a/src/ckdb.h b/src/ckdb.h index e7c0187a..611c3612 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.667" +#define CKDB_VERSION DB_VERSION"-0.700" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ diff --git a/src/ckdb_btc.c b/src/ckdb_btc.c index 7ede69e5..0523b164 100644 --- a/src/ckdb_btc.c +++ b/src/ckdb_btc.c @@ -329,7 +329,7 @@ void btc_blockstatus(BLOCKS *blocks) len = strlen(blocks->blockhash); if (len != SHA256SIZHEX) { LOGERR("%s() invalid blockhash size %d (%d) for block %d", - __func__, len, SHA256SIZHEX, blocks->height); + __func__, (int)len, SHA256SIZHEX, blocks->height); /* So we don't keep repeating the message * This should never happen */ diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index bcf74968..35da99d4 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -1665,7 +1665,7 @@ awconf: return strdup(reply); } - LOGERR("%s.bad.cmd %s", cmd); + LOGERR("%s.bad.cmd %s", id, cmd); return strdup("bad.cmd"); } diff --git a/src/ckdb_data.c b/src/ckdb_data.c index e7005975..9657176c 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -488,7 +488,7 @@ K_ITEM *_optional_name(K_TREE *trf_root, char *name, int len, char *patt, dlen = 0; if (!mvalue || (int)dlen < len) { if (!mvalue) { - LOGERR("%s(): field '%s' NULL (%d) from %s():%d", + LOGERR("%s(): field '%s' NULL (%d:%d) from %s():%d", __func__, name, (int)dlen, len, func, line); } else snprintf(reply, siz, "failed.short %s", name); @@ -1749,8 +1749,8 @@ void auto_age_older(PGconn *conn, int64_t workinfoid, char *poolinstance, min_buf, max_buf); } LOGWARNING("%s() Auto-aged %"PRId64"(%"PRId64") " - "share%s %d sharesummar%s %d workinfoid%s " - "%s %s", + "share%s %"PRId64" sharesummar%s %"PRId32 + " workinfoid%s %s %s", __func__, s_count_tot, s_diff_tot, (s_count_tot == 1) ? "" : "s", @@ -2028,7 +2028,7 @@ void set_block_share_counters() LOGEMERG("%s(): ERROR workmarker %"PRId64" has an invalid" " workinfoid range start=%"PRId64" end=%"PRId64 " due to pool lastblock=%"PRId32 - " workinfoid="PRId64, + " workinfoid=%"PRId64, __func__, workmarkers->markerid, workmarkers->workinfoidstart, workmarkers->workinfoidend, diff --git a/src/connector.c b/src/connector.c index 401f70a4..93ed41e1 100644 --- a/src/connector.c +++ b/src/connector.c @@ -176,7 +176,7 @@ static int drop_client(conn_instance_t *ci, client_instance_t *client) ck_wunlock(&ci->lock); if (fd > -1) - LOGINFO("Connector dropped client %d fd %d", client->id, fd); + LOGINFO("Connector dropped client %"PRId64" fd %d", client->id, fd); return fd; } @@ -261,7 +261,7 @@ reparse: if (!(val = json_loads(msg, 0, NULL))) { char *buf = strdup("Invalid JSON, disconnecting\n"); - LOGINFO("Client id %d sent invalid json message %s", client->id, msg); + LOGINFO("Client id %"PRId64" sent invalid json message %s", client->id, msg); send_client(ci, client->id, buf); invalidate_client(ckp, ci, client); return; @@ -421,13 +421,13 @@ void *sender(void *arg) ret = wait_write_select(fd, 0); if (ret < 1) { if (ret < 0) { - LOGINFO("Client id %d fd %d interrupted", client->id, fd); + LOGINFO("Client id %"PRId64" fd %d interrupted", client->id, fd); invalidate_client(ckp, ci, client); free(sender_send->buf); free(sender_send); continue; } - LOGDEBUG("Client %d not ready for writes", client->id); + LOGDEBUG("Client %"PRId64" not ready for writes", client->id); /* Append it to the tail of the delayed sends list. * This is the only function that alters it so no @@ -439,7 +439,7 @@ void *sender(void *arg) while (sender_send->len) { ret = send(fd, sender_send->buf + ofs, sender_send->len , 0); if (unlikely(ret < 0)) { - LOGINFO("Client id %d fd %d disconnected", client->id, fd); + LOGINFO("Client id %"PRId64" fd %d disconnected", client->id, fd); invalidate_client(ckp, ci, client); break; } @@ -519,7 +519,7 @@ static void passthrough_client(conn_instance_t *ci, client_instance_t *client) { char *buf; - LOGINFO("Connector adding passthrough client %d", client->id); + LOGINFO("Connector adding passthrough client %"PRId64, client->id); client->passthrough = true; ASPRINTF(&buf, "{\"result\": true}\n"); send_client(ci, client->id, buf); diff --git a/src/libckpool.h b/src/libckpool.h index b6d3c8f7..154cec75 100644 --- a/src/libckpool.h +++ b/src/libckpool.h @@ -190,14 +190,25 @@ static inline void flip_80(void *dest_p, const void *src_p) void logmsg(int loglevel, const char *fmt, ...); -#define LOGEMERG(fmt, ...) logmsg(LOG_EMERG, fmt, ##__VA_ARGS__) -#define LOGALERT(fmt, ...) logmsg(LOG_ALERT, fmt, ##__VA_ARGS__) -#define LOGCRIT(fmt, ...) logmsg(LOG_CRIT, fmt, ##__VA_ARGS__) -#define LOGERR(fmt, ...) logmsg(LOG_ERR, fmt, ##__VA_ARGS__) -#define LOGWARNING(fmt, ...) logmsg(LOG_WARNING, fmt, ##__VA_ARGS__) -#define LOGNOTICE(fmt, ...) logmsg(LOG_NOTICE, fmt, ##__VA_ARGS__) -#define LOGINFO(fmt, ...) logmsg(LOG_INFO, fmt, ##__VA_ARGS__) -#define LOGDEBUG(fmt, ...) logmsg(LOG_DEBUG, fmt, ##__VA_ARGS__) +#define DEFLOGBUFSIZ 512 + +#define LOGMSGSIZ(__siz, __lvl, __fmt, ...) do { \ + char tmp42[__siz]; \ + snprintf(tmp42, sizeof(tmp42), __fmt, ##__VA_ARGS__); \ + logmsg(__lvl, "%s", tmp42); \ + } while(0) + +#define LOGMSG(_lvl, _fmt, ...) \ + LOGMSGSIZ(DEFLOGBUFSIZ, _lvl, _fmt, ##__VA_ARGS__) + +#define LOGEMERG(fmt, ...) LOGMSG(LOG_EMERG, fmt, ##__VA_ARGS__) +#define LOGALERT(fmt, ...) LOGMSG(LOG_ALERT, fmt, ##__VA_ARGS__) +#define LOGCRIT(fmt, ...) LOGMSG(LOG_CRIT, fmt, ##__VA_ARGS__) +#define LOGERR(fmt, ...) LOGMSG(LOG_ERR, fmt, ##__VA_ARGS__) +#define LOGWARNING(fmt, ...) LOGMSG(LOG_WARNING, fmt, ##__VA_ARGS__) +#define LOGNOTICE(fmt, ...) LOGMSG(LOG_NOTICE, fmt, ##__VA_ARGS__) +#define LOGINFO(fmt, ...) LOGMSG(LOG_INFO, fmt, ##__VA_ARGS__) +#define LOGDEBUG(fmt, ...) LOGMSG(LOG_DEBUG, fmt, ##__VA_ARGS__) #define IN_FMT_FFL " in %s %s():%d" #define quitfrom(status, _file, _func, _line, fmt, ...) do { \ diff --git a/src/stratifier.c b/src/stratifier.c index c248f7aa..a47e801d 100644 --- a/src/stratifier.c +++ b/src/stratifier.c @@ -2500,12 +2500,12 @@ out: if (client->first_invalid < client->last_share.tv_sec || !client->first_invalid) client->first_invalid = now_t; else if (client->first_invalid && client->first_invalid < now_t - 120) { - LOGNOTICE("Client %d rejecting for 120s, disconnecting", client->id); + LOGNOTICE("Client %"PRId64" rejecting for 120s, disconnecting", client->id); stratum_send_message(client, "Disconnecting for continuous invalid shares"); client->reject = 2; } else if (client->first_invalid && client->first_invalid < now_t - 60) { if (!client->reject) { - LOGINFO("Client %d rejecting for 60s, sending diff", client->id); + LOGINFO("Client %"PRId64" rejecting for 60s, sending diff", client->id); stratum_send_diff(client); client->reject = 1; } @@ -2625,8 +2625,10 @@ static void set_worker_mindiff(ckpool_t *ckp, const char *workername, int mindif ck_runlock(&instance_lock); /* They may just have not connected yet */ - if (!instance) - return LOGINFO("Failed to find user %s in set_worker_mindiff", username); + if (!instance) { + LOGINFO("Failed to find user %s in set_worker_mindiff", username); + return; + } /* Then find the matching worker instance */ ck_rlock(&instance_lock); @@ -2639,11 +2641,15 @@ static void set_worker_mindiff(ckpool_t *ckp, const char *workername, int mindif ck_runlock(&instance_lock); /* They may just not be connected at the moment */ - if (!worker) - return LOGINFO("Failed to find worker %s in set_worker_mindiff", workername); + if (!worker) { + LOGINFO("Failed to find worker %s in set_worker_mindiff", workername); + return; + } - if (mindiff < 1) - return LOGINFO("Worker %s requested invalid diff %ld", worker->workername, mindiff); + if (mindiff < 1) { + LOGINFO("Worker %s requested invalid diff %d", worker->workername, mindiff); + return; + } if (mindiff < ckp->mindiff) mindiff = ckp->mindiff; if (mindiff == worker->mindiff) @@ -2676,12 +2682,16 @@ static void suggest_diff(stratum_instance_t *client, const char *method, json_t json_t *arr_val = json_array_get(params_val, 0); int64_t sdiff; - if (unlikely(!client->authorised)) - return LOGWARNING("Attempted to suggest diff on unauthorised client %ld", client->id); + if (unlikely(!client->authorised)) { + LOGWARNING("Attempted to suggest diff on unauthorised client %ld", client->id); + return; + } if (arr_val && json_is_integer(arr_val)) sdiff = json_integer_value(arr_val); - else if (sscanf(method, "mining.suggest_difficulty(%ld", &sdiff) != 1) - return LOGINFO("Failed to parse suggest_difficulty for client %ld", client->id); + else if (sscanf(method, "mining.suggest_difficulty(%ld", &sdiff) != 1) { + LOGINFO("Failed to parse suggest_difficulty for client %"PRId64, client->id); + return; + } if (sdiff == client->suggest_diff) return; client->suggest_diff = sdiff; @@ -2711,7 +2721,7 @@ static void parse_method(const int64_t client_id, json_t *id_val, json_t *method } if (unlikely(client->reject == 2)) { - LOGINFO("Dropping client %d tagged for lazy invalidation", client_id); + LOGINFO("Dropping client %"PRId64" tagged for lazy invalidation", client_id); snprintf(buf, 255, "dropclient=%ld", client->id); send_proc(client->ckp->connector, buf); return; From 23c461c8a7d055ad114b52d4c33cc97d94e5265d Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 28 Nov 2014 19:01:02 +1100 Subject: [PATCH 04/21] ckdb - remove LOGERR warning --- src/ckdb.c | 2 +- src/ckdb.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ckdb.c b/src/ckdb.c index 6bc2239a..dd534d8d 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -1946,7 +1946,7 @@ static void summarise_userstats() //upgrade = false; if (error[0]) - LOGERR(error); + LOGERR("%s", error); } if (locked) { diff --git a/src/ckdb.h b/src/ckdb.h index 611c3612..ab409fc0 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.700" +#define CKDB_VERSION DB_VERSION"-0.701" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ From 738a68fd9f569d213f9cae57dd94ff3633aaf7bf Mon Sep 17 00:00:00 2001 From: kanoi Date: Tue, 2 Dec 2014 08:35:00 +1100 Subject: [PATCH 05/21] ckdb - fix optioncontrol memory - and add FREENULL to free and null if needed --- src/ckdb.c | 64 ++++++++++++++++++++----------------------------- src/ckdb.h | 9 ++++++- src/ckdb_cmd.c | 25 ++++++++----------- src/ckdb_dbio.c | 46 +++++++++++++---------------------- 4 files changed, 61 insertions(+), 83 deletions(-) diff --git a/src/ckdb.c b/src/ckdb.c index dd534d8d..9a10cb35 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -1070,9 +1070,9 @@ static void free_workinfo_data(K_ITEM *item) DATA_WORKINFO(workinfo, item); if (workinfo->transactiontree) - free(workinfo->transactiontree); + FREENULL(workinfo->transactiontree); if (workinfo->merklehash) - free(workinfo->merklehash); + FREENULL(workinfo->merklehash); } static void free_sharesummary_data(K_ITEM *item) @@ -1082,8 +1082,7 @@ static void free_sharesummary_data(K_ITEM *item) DATA_SHARESUMMARY(sharesummary, item); if (sharesummary->workername) { LIST_MEM_SUB(sharesummary_free, sharesummary->workername); - free(sharesummary->workername); - sharesummary->workername = NULL; + FREENULL(sharesummary->workername); } SET_CREATEBY(sharesummary_free, sharesummary->createby, EMPTY); SET_CREATECODE(sharesummary_free, sharesummary->createcode, EMPTY); @@ -1099,7 +1098,7 @@ static void free_optioncontrol_data(K_ITEM *item) DATA_OPTIONCONTROL(optioncontrol, item); if (optioncontrol->optionvalue) - free(optioncontrol->optionvalue); + FREENULL(optioncontrol->optionvalue); } static void free_markersummary_data(K_ITEM *item) @@ -1108,7 +1107,7 @@ static void free_markersummary_data(K_ITEM *item) DATA_MARKERSUMMARY(markersummary, item); if (markersummary->workername) - free(markersummary->workername); + FREENULL(markersummary->workername); SET_CREATEBY(markersummary_free, markersummary->createby, EMPTY); SET_CREATECODE(markersummary_free, markersummary->createcode, EMPTY); SET_CREATEINET(markersummary_free, markersummary->createinet, EMPTY); @@ -1123,9 +1122,9 @@ static void free_workmarkers_data(K_ITEM *item) DATA_WORKMARKERS(workmarkers, item); if (workmarkers->poolinstance) - free(workmarkers->poolinstance); + FREENULL(workmarkers->poolinstance); if (workmarkers->description) - free(workmarkers->description); + FREENULL(workmarkers->description); } static void free_marks_data(K_ITEM *item) @@ -1134,11 +1133,11 @@ static void free_marks_data(K_ITEM *item) DATA_MARKS(marks, item); if (marks->poolinstance && marks->poolinstance != EMPTY) - free(marks->poolinstance); + FREENULL(marks->poolinstance); if (marks->description && marks->description != EMPTY) - free(marks->description); + FREENULL(marks->description); if (marks->extra && marks->extra != EMPTY) - free(marks->extra); + FREENULL(marks->extra); } #define FREE_TREE(_tree) \ @@ -1473,7 +1472,7 @@ static enum cmd_values breakdown(K_TREE **trf_root, K_STORE **trf_store, STRNCPY(transfer->name, json_key); if (!ok || find_in_ktree(*trf_root, item, cmp_transfer, ctx)) { if (transfer->mvalue != transfer->svalue) - free(transfer->mvalue); + FREENULL(transfer->mvalue); k_add_head(transfer_free, item); } else { *trf_root = add_to_ktree(*trf_root, item, cmp_transfer); @@ -1505,7 +1504,7 @@ static enum cmd_values breakdown(K_TREE **trf_root, K_STORE **trf_store, if (find_in_ktree(*trf_root, item, cmp_transfer, ctx)) { if (transfer->mvalue != transfer->svalue) - free(transfer->mvalue); + FREENULL(transfer->mvalue); k_add_head(transfer_free, item); } else { *trf_root = add_to_ktree(*trf_root, item, cmp_transfer); @@ -2038,7 +2037,7 @@ static void *logger(__maybe_unused void *arg) while (lq_item) { DATA_LOGQUEUE(lq, lq_item); LOGFILE(lq->msg); - free(lq->msg); + FREENULL(lq->msg); K_WLOCK(logqueue_free); k_add_head(logqueue_free, lq_item); @@ -2064,7 +2063,7 @@ static void *logger(__maybe_unused void *arg) while (lq_item) { DATA_LOGQUEUE(lq, lq_item); LOGFILE(lq->msg); - free(lq->msg); + FREENULL(lq->msg); count--; setnow(&now); if ((now.tv_sec - then.tv_sec) > 10) { @@ -2330,8 +2329,7 @@ static void *socketer(__maybe_unused void *arg) rep = malloc(siz); snprintf(rep, siz, "%s.%ld.%s", id, now.tv_sec, ans); send_unix_msg(sockd, rep); - free(ans); - ans = NULL; + FREENULL(ans); switch (cmdnum) { case CMD_AUTH: STORELASTREPLY(auth); @@ -2395,8 +2393,7 @@ static void *socketer(__maybe_unused void *arg) rep = malloc(siz); snprintf(rep, siz, "%s.%ld.%s", id, now.tv_sec, ans); send_unix_msg(sockd, rep); - free(ans); - ans = NULL; + FREENULL(ans); if (cmdnum == CMD_DSP) free(rep); else { @@ -2429,10 +2426,8 @@ static void *socketer(__maybe_unused void *arg) rep = malloc(siz); snprintf(rep, siz, "%s.%ld.%s", id, now.tv_sec, ans); send_unix_msg(sockd, rep); - free(ans); - ans = NULL; - free(rep); - rep = NULL; + FREENULL(ans); + FREENULL(rep); } break; // Always queue (ok.queued) @@ -2504,7 +2499,7 @@ static void *socketer(__maybe_unused void *arg) while (item) { DATA_TRANSFER(transfer, item); if (transfer->mvalue != transfer->svalue) - free(transfer->mvalue); + FREENULL(transfer->mvalue); item = item->next; } K_WLOCK(transfer_free); @@ -2632,7 +2627,7 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) while (item) { DATA_TRANSFER(transfer, item); if (transfer->mvalue != transfer->svalue) - free(transfer->mvalue); + FREENULL(transfer->mvalue); item = item->next; } K_WLOCK(transfer_free); @@ -2728,8 +2723,7 @@ static bool reload_from(tv_t *start) fp = fopen(filename, "re"); if (!fp) { missingfirst = strdup(filename); - free(filename); - filename = NULL; + FREENULL(filename); errno = 0; missing_count = 1; setnow(&now); @@ -2753,19 +2747,16 @@ static bool reload_from(tv_t *start) if (missing_count++ > 1) free(missinglast); missinglast = strdup(filename); - free(filename); - filename = NULL; + FREENULL(filename); } if (missing_count == 1) LOGWARNING("%s(): skipped %s", __func__, missingfirst+rflen); else { LOGWARNING("%s(): skipped %d files from %s to %s", __func__, missing_count, missingfirst+rflen, missinglast+rflen); - free(missinglast); - missinglast = NULL; + FREENULL(missinglast); } - free(missingfirst); - missingfirst = NULL; + FREENULL(missingfirst); } } @@ -2793,10 +2784,7 @@ static bool reload_from(tv_t *start) } reloading = false; - - free(reload_buf); - reload_buf = NULL; - + FREENULL(reload_buf); return ret; } @@ -2828,7 +2816,7 @@ static void process_queued(PGconn *conn, K_ITEM *wq_item) while (item) { DATA_TRANSFER(transfer, item); if (transfer->mvalue != transfer->svalue) - free(transfer->mvalue); + FREENULL(transfer->mvalue); item = item->next; } K_WLOCK(transfer_free); diff --git a/src/ckdb.h b/src/ckdb.h index ab409fc0..20bd1c2c 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.701" +#define CKDB_VERSION DB_VERSION"-0.710" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -64,6 +64,13 @@ #define STRINT(x) STRINT2(x) #define STRINT2(x) #x +#define FREENULL(mem) do { \ + if (mem) { \ + free(mem); \ + mem = NULL; \ + } \ + } while (0) + // So they can fit into a 1 byte flag field #define TRUE_STR "Y" #define FALSE_STR "N" diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 35da99d4..0d443b31 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -2758,7 +2758,7 @@ static char *cmd_setopts(PGconn *conn, char *cmd, char *id, ExecStatusType rescode; PGresult *res; bool conned = false; - K_ITEM *t_item, *oc_item = NULL; + K_ITEM *t_item, *oc_item = NULL, *ok = NULL; K_TREE_CTX ctx[1]; char reply[1024] = ""; size_t siz = sizeof(reply); @@ -2806,10 +2806,11 @@ static char *cmd_setopts(PGconn *conn, char *cmd, char *id, } begun = true; } - if (optioncontrol_item_add(conn, oc_item, now, begun)) { - oc_item = NULL; + ok = optioncontrol_item_add(conn, oc_item, now, begun); + oc_item = NULL; + if (ok) db++; - } else { + else { reason = "DBERR"; goto rollback; } @@ -2863,11 +2864,14 @@ static char *cmd_setopts(PGconn *conn, char *cmd, char *id, } begun = true; } - if (!optioncontrol_item_add(conn, oc_item, now, begun)) { + ok = optioncontrol_item_add(conn, oc_item, now, begun); + oc_item = NULL; + if (ok) + db++; + else { reason = "DBERR"; goto rollback; } - db++; } rollback: if (begun) { @@ -2882,15 +2886,6 @@ rollback: if (conned) PQfinish(conn); if (reason) { - if (oc_item) { - if (optioncontrol->optionvalue) { - free(optioncontrol->optionvalue); - optioncontrol->optionvalue = NULL; - } - 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); diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 79ddfb85..471d5137 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -1841,7 +1841,7 @@ K_ITEM *optioncontrol_item_add(PGconn *conn, K_ITEM *oc_item, tv_t *cd, bool beg K_TREE_CTX ctx[1]; PGresult *res; K_ITEM *old_item, look; - OPTIONCONTROL *row; + OPTIONCONTROL *row, *optioncontrol; char *upd, *ins; bool ok = false; char *params[4 + HISTORYDATECOUNT]; @@ -1935,13 +1935,17 @@ nostart: free(params[n]); K_WLOCK(optioncontrol_free); - if (!ok) + if (!ok) { + // Cleanup item passed in + FREENULL(row->optionvalue); k_add_head(optioncontrol_free, oc_item); - else { - // Discard it + } else { + // Discard old if (old_item) { + DATA_OPTIONCONTROL(optioncontrol, old_item); optioncontrol_root = remove_from_ktree(optioncontrol_root, old_item, cmp_optioncontrol); + FREENULL(optioncontrol->optionvalue); k_add_head(optioncontrol_free, old_item); } optioncontrol_root = add_to_ktree(optioncontrol_root, oc_item, cmp_optioncontrol); @@ -1962,7 +1966,6 @@ K_ITEM *optioncontrol_add(PGconn *conn, char *optionname, char *optionvalue, { K_ITEM *item; OPTIONCONTROL *row; - bool ok = false; LOGDEBUG("%s(): add", __func__); @@ -1990,19 +1993,7 @@ K_ITEM *optioncontrol_add(PGconn *conn, char *optionname, char *optionvalue, 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; + return optioncontrol_item_add(conn, item, cd, begun); } bool optioncontrol_fill(PGconn *conn) @@ -2084,8 +2075,10 @@ bool optioncontrol_fill(PGconn *conn) optioncontrol_root = add_to_ktree(optioncontrol_root, item, cmp_optioncontrol); k_add_head(optioncontrol_store, item); } - if (!ok) + if (!ok) { + FREENULL(row->optionvalue); k_add_head(optioncontrol_free, item); + } K_WUNLOCK(optioncontrol_free); PQclear(res); @@ -2148,10 +2141,8 @@ int64_t workinfo_add(PGconn *conn, char *workinfoidstr, char *poolinstance, K_WLOCK(workinfo_free); if (find_in_ktree(workinfo_root, item, cmp_workinfo, ctx)) { - free(row->transactiontree); - row->transactiontree = NULL; - free(row->merklehash); - row->merklehash = NULL; + FREENULL(row->transactiontree); + FREENULL(row->merklehash); workinfoid = row->workinfoid; k_add_head(workinfo_free, item); K_WUNLOCK(workinfo_free); @@ -2213,10 +2204,8 @@ unparam: K_WLOCK(workinfo_free); if (workinfoid == -1) { - free(row->transactiontree); - row->transactiontree = NULL; - free(row->merklehash); - row->merklehash = NULL; + FREENULL(row->transactiontree); + FREENULL(row->merklehash); k_add_head(workinfo_free, item); } else { if (row->transactiontree && *(row->transactiontree)) { @@ -3165,8 +3154,7 @@ bool sharesummary_fill(PGconn *conn) DATA_SHARESUMMARY(row, item); if (row->workername) { LIST_MEM_SUB(sharesummary_free, row->workername); - free(row->workername); - row->workername = NULL; + FREENULL(row->workername); } k_add_head(sharesummary_free, item); } From fb6a9874ecc7b4f2961cf9b5329e72a9646dc41d Mon Sep 17 00:00:00 2001 From: kanoi Date: Thu, 4 Dec 2014 23:13:29 +1100 Subject: [PATCH 06/21] ckdb - add libbz dependency to the ckdb build and allow reload to read both uncompressed and bzip2 compressed log files --- configure.ac | 8 ++- src/Makefile.am | 2 +- src/ckdb.c | 138 ++++++++++++++++++++++++++++++++++++++++++------ src/ckdb.h | 3 +- 4 files changed, 132 insertions(+), 19 deletions(-) diff --git a/configure.ac b/configure.ac index 65d311c0..32de3b12 100644 --- a/configure.ac +++ b/configure.ac @@ -38,6 +38,7 @@ AC_CHECK_HEADERS(alloca.h pthread.h stdio.h math.h signal.h sys/prctl.h) AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/stat.h linux/un.h netdb.h) AC_CHECK_HEADERS(stdint.h netinet/in.h netinet/tcp.h sys/ioctl.h getopt.h) AC_CHECK_HEADERS(sys/epoll.h libpq-fe.h postgresql/libpq-fe.h grp.h) +AC_CHECK_HEADERS(bzlib.h) PTHREAD_LIBS="-lpthread" MATH_LIBS="-lm" @@ -59,13 +60,18 @@ AC_ARG_WITH([ckdb], if test "x$ckdb" != "xno"; then AC_CHECK_LIB([pq], [main],[PQ=-lpq],echo "Error: Required library libpq-dev not found. Install it or disable postgresql support with --without-ckdb" && exit 1) + AC_CHECK_LIB([bz2], [main],[BZ2=-lbz],echo "Error: Required library libbz + not found. Install it or disable libbz2 support with --without-ckdb" && exit 1) AC_DEFINE([USE_CKDB], [1], [Defined to 1 if ckdb support required]) PQ_LIBS="-lpq" + BZ2_LIBS="-lbz2" else PQ_LIBS="" + BZ2_LIBS="" fi AM_CONDITIONAL([WANT_CKDB], [test "x$ckdb" != "xno"]) AC_SUBST(PQ_LIBS) +AC_SUBST(BZ2_LIBS) AC_OUTPUT([Makefile] [src/Makefile]) @@ -75,7 +81,7 @@ echo " CPPFLAGS.............: $CPPFLAGS" echo " CFLAGS...............: $CFLAGS" echo " LDFLAGS..............: $LDFLAGS" echo " LDADD................: $PTHREAD_LIBS $MATH_LIBS $RT_LIBS $JANSSON_LIBS" -echo " db LDADD.............: $PQ_LIBS" +echo " db LDADD.............: $PQ_LIBS $BZ2_LIBS" echo echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" echo " prefix...............: $prefix" diff --git a/src/Makefile.am b/src/Makefile.am index 5e032e62..0dd0d50c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,5 +23,5 @@ if WANT_CKDB bin_PROGRAMS += ckdb ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \ ckdb.h klist.c ktree.c klist.h ktree.h -ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ +ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ @BZ2_LIBS@ endif diff --git a/src/ckdb.c b/src/ckdb.c index 9a10cb35..663cf23d 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -2646,6 +2646,117 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) #define MAX_READ (10 * 1024 * 1024) static char *reload_buf; +#define BZNAME ".bz2" + +static bool logline(char *buf, int siz, FILE *fp, BZFILE *bzf, char *filename) +{ + static char *bzbuf = NULL; + static int bzpos, thisred; + static size_t bzlen; + char *ret, *ptr; + int bzerror, use; + bool repeat; + + if (!bzf) { + ret = fgets_unlocked(buf, siz, fp); + if (!ret && ferror(fp)) { + int err = errno; + quithere(1, "Read failed on %s (%d) '%s'", + filename, err, strerror(err)); + } + if (ret) + return true; + else + return false; + } + + if (!bzbuf) { + bzpos = 0; + bzlen = MAX_READ*2 + 1; + bzbuf = malloc(bzlen); + bzbuf[0] = '\0'; + } + + repeat = true; + // Read at least MAX_READ or to EOF + while (repeat && bzpos < MAX_READ) { + thisred = BZ2_bzRead(&bzerror, bzf, &(bzbuf[bzpos]), MAX_READ*2 - bzpos); + if (bzerror != BZ_OK && bzerror != BZ_STREAM_END) { + quithere(1, "Read failed on %s"BZNAME" (%d)", + filename, bzerror); + } + bzpos += thisred; + bzbuf[bzpos] = '\0'; + if (bzerror == BZ_STREAM_END || thisred == 0) + repeat = false; + } + /* Either EOF or a read returned 0 + * with no buffered data left in both cases */ + if (bzpos == 0) + return false; + + ptr = bzbuf; + while (*ptr && *ptr != '\n' && *ptr != '\r') + ptr++; + /* This will lose blank lines except for the first line + * - which is OK anyway since they are an error */ + while (*ptr == '\n' || *ptr == '\r') + ptr++; + use = ptr - bzbuf; + + // Return the 'line' (up to siz-1) + if (use > (siz - 1)) + use = siz - 1; + memcpy(buf, bzbuf, use); + buf[use] = '\0'; + + // Buffer remainder + if (bzpos > use) { + bzpos -= use; + memmove(bzbuf, bzbuf+use, bzpos); + bzbuf[bzpos] = '\0'; + } else { + bzpos = 0; + bzbuf[0] = '\0'; + } + return true; +} + +static bool logopen(char **filename, FILE **fp, BZFILE **bzf) +{ + char *bzname; + size_t len; + int bzerror; + + *fp = *bzf = NULL; + *fp = fopen(*filename, "re"); + if (*fp) + return true; + + len = strlen(*filename) + sizeof(BZNAME); + bzname = malloc(len + 1); + if (!bzname) + quithere(1, "(%d) OOM", (int)len); + strcpy(bzname, *filename); + strcat(bzname, BZNAME); + + *fp = fopen(bzname, "re"); + if (!(*fp)) { + free(bzname); + return false; + } + + /* An error is fatal, since the file exists, it must be valid + * Skipping files can cause major problems */ + *bzf = BZ2_bzReadOpen(&bzerror, *fp, 0, 0, NULL, 0); + if (bzerror != BZ_OK) + quithere(1, "BZ2_bzReadOpen() failed for '%s' (%d)", bzname, bzerror); + + free(*filename); + *filename = bzname; + return true; +} + /* If the reload start file is missing and -r was specified correctly: * touch the filename reported in "Failed to open 'filename'", * if ckdb aborts at the beginning of the reload, then start again */ @@ -2657,11 +2768,12 @@ static bool reload_from(tv_t *start) char *missingfirst = NULL, *missinglast = NULL; int missing_count; int processing; - bool finished = false, matched = false, ret = true; + bool finished = false, matched = false, ret = true, ok; char *filename = NULL; uint64_t count, total; tv_t now; FILE *fp = NULL; + BZFILE *bzf = NULL; reload_buf = malloc(MAX_READ); if (!reload_buf) @@ -2677,9 +2789,8 @@ static bool reload_from(tv_t *start) LOGWARNING("%s(): from %s (stamp %s)", __func__, buf, run); filename = rotating_filename(restorefrom, reload_timestamp.tv_sec); - fp = fopen(filename, "re"); - if (!fp) - quithere(1, "Failed to open '%s'", filename); + if (!logopen(&filename, &fp, &bzf)) + quithere(1, "Failed to open '%s' (or "BZNAME")", filename); setnow(&now); tvs_to_buf(&now, run, sizeof(run)); @@ -2695,14 +2806,9 @@ static bool reload_from(tv_t *start) processing++; count = 0; - while (!everyone_die && !matched && fgets_unlocked(reload_buf, MAX_READ, fp)) - matched = reload_line(conn, filename, ++count, reload_buf); - - if (ferror(fp)) { - int err = errno; - quithere(1, "Read failed on %s (%d) '%s'", - filename, err, strerror(err)); - } + while (!everyone_die && !matched && + logline(reload_buf, MAX_READ, fp, bzf, filename)) + matched = reload_line(conn, filename, ++count, reload_buf); LOGWARNING("%s(): %sread %"PRIu64" line%s from %s", __func__, @@ -2720,8 +2826,8 @@ static bool reload_from(tv_t *start) break; } filename = rotating_filename(restorefrom, reload_timestamp.tv_sec); - fp = fopen(filename, "re"); - if (!fp) { + ok = logopen(&filename, &fp, &bzf); + if (!ok) { missingfirst = strdup(filename); FREENULL(filename); errno = 0; @@ -2740,8 +2846,8 @@ static bool reload_from(tv_t *start) break; } filename = rotating_filename(restorefrom, reload_timestamp.tv_sec); - fp = fopen(filename, "re"); - if (fp) + ok = logopen(&filename, &fp, &bzf); + if (ok) break; errno = 0; if (missing_count++ > 1) diff --git a/src/ckdb.h b/src/ckdb.h index 20bd1c2c..1826e0e6 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -36,6 +36,7 @@ #elif defined (HAVE_POSTGRESQL_LIBPQ_FE_H) #include #endif +#include #include "ckpool.h" #include "libckpool.h" @@ -52,7 +53,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.710" +#define CKDB_VERSION DB_VERSION"-0.720" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ From 44009cd4d48fd16caee4fb00f0106153a4a4a194 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 5 Dec 2014 17:30:54 +1100 Subject: [PATCH 07/21] remove ckdb bzip2 build requirement --- configure.ac | 8 +------- src/Makefile.am | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 32de3b12..65d311c0 100644 --- a/configure.ac +++ b/configure.ac @@ -38,7 +38,6 @@ AC_CHECK_HEADERS(alloca.h pthread.h stdio.h math.h signal.h sys/prctl.h) AC_CHECK_HEADERS(sys/types.h sys/socket.h sys/stat.h linux/un.h netdb.h) AC_CHECK_HEADERS(stdint.h netinet/in.h netinet/tcp.h sys/ioctl.h getopt.h) AC_CHECK_HEADERS(sys/epoll.h libpq-fe.h postgresql/libpq-fe.h grp.h) -AC_CHECK_HEADERS(bzlib.h) PTHREAD_LIBS="-lpthread" MATH_LIBS="-lm" @@ -60,18 +59,13 @@ AC_ARG_WITH([ckdb], if test "x$ckdb" != "xno"; then AC_CHECK_LIB([pq], [main],[PQ=-lpq],echo "Error: Required library libpq-dev not found. Install it or disable postgresql support with --without-ckdb" && exit 1) - AC_CHECK_LIB([bz2], [main],[BZ2=-lbz],echo "Error: Required library libbz - not found. Install it or disable libbz2 support with --without-ckdb" && exit 1) AC_DEFINE([USE_CKDB], [1], [Defined to 1 if ckdb support required]) PQ_LIBS="-lpq" - BZ2_LIBS="-lbz2" else PQ_LIBS="" - BZ2_LIBS="" fi AM_CONDITIONAL([WANT_CKDB], [test "x$ckdb" != "xno"]) AC_SUBST(PQ_LIBS) -AC_SUBST(BZ2_LIBS) AC_OUTPUT([Makefile] [src/Makefile]) @@ -81,7 +75,7 @@ echo " CPPFLAGS.............: $CPPFLAGS" echo " CFLAGS...............: $CFLAGS" echo " LDFLAGS..............: $LDFLAGS" echo " LDADD................: $PTHREAD_LIBS $MATH_LIBS $RT_LIBS $JANSSON_LIBS" -echo " db LDADD.............: $PQ_LIBS $BZ2_LIBS" +echo " db LDADD.............: $PQ_LIBS" echo echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')" echo " prefix...............: $prefix" diff --git a/src/Makefile.am b/src/Makefile.am index 0dd0d50c..5e032e62 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,5 +23,5 @@ if WANT_CKDB bin_PROGRAMS += ckdb ckdb_SOURCES = ckdb.c ckdb_cmd.c ckdb_data.c ckdb_dbio.c ckdb_btc.c \ ckdb.h klist.c ktree.c klist.h ktree.h -ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ @BZ2_LIBS@ +ckdb_LDADD = libckpool.la @JANSSON_LIBS@ @PQ_LIBS@ endif From a41e994548de182645a8fb71e844ef853afff6f7 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 5 Dec 2014 22:14:43 +1100 Subject: [PATCH 08/21] ckdb - use a pipe for reloading compressed files - allow .bz2 .gz .lrz --- src/ckdb.c | 164 ++++++++++++++++++++--------------------------------- src/ckdb.h | 3 +- 2 files changed, 62 insertions(+), 105 deletions(-) diff --git a/src/ckdb.c b/src/ckdb.c index 663cf23d..abc7e4b0 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -2646,115 +2646,71 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) #define MAX_READ (10 * 1024 * 1024) static char *reload_buf; -#define BZNAME ".bz2" - -static bool logline(char *buf, int siz, FILE *fp, BZFILE *bzf, char *filename) +static bool logline(char *buf, int siz, FILE *fp, char *filename) { - static char *bzbuf = NULL; - static int bzpos, thisred; - static size_t bzlen; - char *ret, *ptr; - int bzerror, use; - bool repeat; - - if (!bzf) { - ret = fgets_unlocked(buf, siz, fp); - if (!ret && ferror(fp)) { - int err = errno; - quithere(1, "Read failed on %s (%d) '%s'", - filename, err, strerror(err)); - } - if (ret) - return true; - else - return false; - } + char *ret; - if (!bzbuf) { - bzpos = 0; - bzlen = MAX_READ*2 + 1; - bzbuf = malloc(bzlen); - bzbuf[0] = '\0'; + ret = fgets_unlocked(buf, siz, fp); + if (!ret && ferror(fp)) { + int err = errno; + quithere(1, "Read failed on %s (%d) '%s'", + filename, err, strerror(err)); } - - repeat = true; - // Read at least MAX_READ or to EOF - while (repeat && bzpos < MAX_READ) { - thisred = BZ2_bzRead(&bzerror, bzf, &(bzbuf[bzpos]), MAX_READ*2 - bzpos); - if (bzerror != BZ_OK && bzerror != BZ_STREAM_END) { - quithere(1, "Read failed on %s"BZNAME" (%d)", - filename, bzerror); - } - bzpos += thisred; - bzbuf[bzpos] = '\0'; - if (bzerror == BZ_STREAM_END || thisred == 0) - repeat = false; - } - /* Either EOF or a read returned 0 - * with no buffered data left in both cases */ - if (bzpos == 0) + if (ret) + return true; + else return false; - - ptr = bzbuf; - while (*ptr && *ptr != '\n' && *ptr != '\r') - ptr++; - /* This will lose blank lines except for the first line - * - which is OK anyway since they are an error */ - while (*ptr == '\n' || *ptr == '\r') - ptr++; - use = ptr - bzbuf; - - // Return the 'line' (up to siz-1) - if (use > (siz - 1)) - use = siz - 1; - memcpy(buf, bzbuf, use); - buf[use] = '\0'; - - // Buffer remainder - if (bzpos > use) { - bzpos -= use; - memmove(bzbuf, bzbuf+use, bzpos); - bzbuf[bzpos] = '\0'; - } else { - bzpos = 0; - bzbuf[0] = '\0'; - } - return true; } -static bool logopen(char **filename, FILE **fp, BZFILE **bzf) +static struct decomp { + char *ext; + char *fmt; +} dec_list[] = { + { ".bz2", "bzcat -q '%s'" }, + { ".gz", "zcat -q '%s'" }, + { ".lrz", "lrzip -q -d -o - '%s'" }, + { NULL, NULL } +}; + +static bool logopen(char **filename, FILE **fp, bool *apipe) { - char *bzname; + char buf[1024]; + char *name; size_t len; - int bzerror; + int i; + + *apipe = false; - *fp = *bzf = NULL; + *fp = NULL; *fp = fopen(*filename, "re"); if (*fp) return true; - len = strlen(*filename) + sizeof(BZNAME); - bzname = malloc(len + 1); - if (!bzname) - quithere(1, "(%d) OOM", (int)len); - strcpy(bzname, *filename); - strcat(bzname, BZNAME); - - *fp = fopen(bzname, "re"); - if (!(*fp)) { - free(bzname); - return false; + for (i = 0; dec_list[i].ext; i++) { + len = strlen(*filename) + strlen(dec_list[i].ext); + name = malloc(len + 1); + if (!name) + quithere(1, "(%d) OOM", (int)len); + strcpy(name, *filename); + strcat(name, dec_list[i].ext); + if (access(name, R_OK)) + free(name); + else { + snprintf(buf, sizeof(buf), dec_list[i].fmt, name); + *fp = popen(buf, "re"); + if (!(*fp)) { + int errn = errno; + quithere(1, "Failed to pipe (%d) \"%s\"", + errn, buf); + } else { + *apipe = true; + free(*filename); + *filename = name; + return true; + } + } } - - /* An error is fatal, since the file exists, it must be valid - * Skipping files can cause major problems */ - *bzf = BZ2_bzReadOpen(&bzerror, *fp, 0, 0, NULL, 0); - if (bzerror != BZ_OK) - quithere(1, "BZ2_bzReadOpen() failed for '%s' (%d)", bzname, bzerror); - - free(*filename); - *filename = bzname; - return true; + return false; } /* If the reload start file is missing and -r was specified correctly: @@ -2768,12 +2724,11 @@ static bool reload_from(tv_t *start) char *missingfirst = NULL, *missinglast = NULL; int missing_count; int processing; - bool finished = false, matched = false, ret = true, ok; + bool finished = false, matched = false, ret = true, ok, apipe = false; char *filename = NULL; uint64_t count, total; tv_t now; FILE *fp = NULL; - BZFILE *bzf = NULL; reload_buf = malloc(MAX_READ); if (!reload_buf) @@ -2789,8 +2744,8 @@ static bool reload_from(tv_t *start) LOGWARNING("%s(): from %s (stamp %s)", __func__, buf, run); filename = rotating_filename(restorefrom, reload_timestamp.tv_sec); - if (!logopen(&filename, &fp, &bzf)) - quithere(1, "Failed to open '%s' (or "BZNAME")", filename); + if (!logopen(&filename, &fp, &apipe)) + quithere(1, "Failed to open '%s'", filename); setnow(&now); tvs_to_buf(&now, run, sizeof(run)); @@ -2807,7 +2762,7 @@ static bool reload_from(tv_t *start) count = 0; while (!everyone_die && !matched && - logline(reload_buf, MAX_READ, fp, bzf, filename)) + logline(reload_buf, MAX_READ, fp, filename)) matched = reload_line(conn, filename, ++count, reload_buf); LOGWARNING("%s(): %sread %"PRIu64" line%s from %s", @@ -2816,7 +2771,10 @@ static bool reload_from(tv_t *start) count, count == 1 ? "" : "s", filename); total += count; - fclose(fp); + if (apipe) + pclose(fp); + else + fclose(fp); free(filename); if (everyone_die || matched) break; @@ -2826,7 +2784,7 @@ static bool reload_from(tv_t *start) break; } filename = rotating_filename(restorefrom, reload_timestamp.tv_sec); - ok = logopen(&filename, &fp, &bzf); + ok = logopen(&filename, &fp, &apipe); if (!ok) { missingfirst = strdup(filename); FREENULL(filename); @@ -2846,7 +2804,7 @@ static bool reload_from(tv_t *start) break; } filename = rotating_filename(restorefrom, reload_timestamp.tv_sec); - ok = logopen(&filename, &fp, &bzf); + ok = logopen(&filename, &fp, &apipe); if (ok) break; errno = 0; diff --git a/src/ckdb.h b/src/ckdb.h index 1826e0e6..0386852d 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -36,7 +36,6 @@ #elif defined (HAVE_POSTGRESQL_LIBPQ_FE_H) #include #endif -#include #include "ckpool.h" #include "libckpool.h" @@ -53,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.720" +#define CKDB_VERSION DB_VERSION"-0.730" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ From 1fca8b81889f0dda248d9b75d9b095a64bcd7c3c Mon Sep 17 00:00:00 2001 From: kanoi Date: Sat, 6 Dec 2014 08:46:28 +1100 Subject: [PATCH 09/21] ckdb - abort if any compressed files return no data - also true if the decompress command doesn't exist --- src/ckdb.c | 9 +++++++-- src/ckdb.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ckdb.c b/src/ckdb.c index abc7e4b0..22a9d01b 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -2771,9 +2771,14 @@ static bool reload_from(tv_t *start) count, count == 1 ? "" : "s", filename); total += count; - if (apipe) + if (apipe) { pclose(fp); - else + if (count == 0) { + quithere(1, "ABORTING - No data returned from " + "compressed file \"%s\"", + filename); + } + } else fclose(fp); free(filename); if (everyone_die || matched) diff --git a/src/ckdb.h b/src/ckdb.h index 0386852d..ee70868e 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.730" +#define CKDB_VERSION DB_VERSION"-0.731" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ From 4c75fe3a275af77a2ec77af5b43b31174bbdfbb8 Mon Sep 17 00:00:00 2001 From: kanoi Date: Thu, 11 Dec 2014 15:52:44 +1100 Subject: [PATCH 10/21] ckdb/php - add code for future support of multiple payout addresses per user --- pool/db.php | 12 +- pool/page_settings.php | 8 +- src/ckdb.h | 15 ++- src/ckdb_cmd.c | 144 +++++++++++++++++----- src/ckdb_data.c | 35 ++++-- src/ckdb_dbio.c | 272 +++++++++++++++++++++++++++-------------- 6 files changed, 350 insertions(+), 136 deletions(-) diff --git a/pool/db.php b/pool/db.php index f8764876..02028a90 100644 --- a/pool/db.php +++ b/pool/db.php @@ -180,7 +180,17 @@ function userSettings($user, $email = null, $addr = null, $pass = null) $flds['email'] = $email; if ($addr != null) { - $flds['address'] = $addr; + $rows = count($addr); + $i = 0; + foreach ($addr as $ar) + { + $flds['address:'.$i] = $ar['addr']; + // optional - missing = use default + if (isset($ar['ratio'])) + $flds['ratio:'.$i] = $ar['ratio']; + $i++; + } + $flds['rows'] = $rows; $tmo = 3; # 3x the timeout } if ($pass != null) diff --git a/pool/page_settings.php b/pool/page_settings.php index 852e3f1a..5838dfdc 100644 --- a/pool/page_settings.php +++ b/pool/page_settings.php @@ -102,8 +102,9 @@ function dosettings($data, $user) break; case 'Address': $addr = getparam('baddr', false); + $addrarr = array(array('addr' => $addr)); $pass = getparam('pass', false); - $ans = userSettings($user, null, $addr, $pass); + $ans = userSettings($user, null, $addrarr, $pass); $check = true; break; case 'Password': @@ -139,8 +140,9 @@ function dosettings($data, $user) $email = $ans['email']; else $email = ''; - if (isset($ans['addr'])) - $addr = $ans['addr']; + // Use the first one - updating will expire all others + if (isset($ans['rows']) and $ans['rows'] > 0) + $addr = $ans['addr:0']; else $addr = ''; $pg = settings($data, $user, $email, $addr, $err); diff --git a/src/ckdb.h b/src/ckdb.h index ee70868e..d8ae51de 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.731" +#define CKDB_VERSION DB_VERSION"-0.740" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -751,17 +751,21 @@ typedef struct paymentaddresses { char payaddress[TXT_BIG+1]; int32_t payratio; HISTORYDATECONTROLFIELDS; + bool match; // Non-db field } PAYMENTADDRESSES; #define ALLOC_PAYMENTADDRESSES 1024 #define LIMIT_PAYMENTADDRESSES 0 #define INIT_PAYMENTADDRESSES(_item) INIT_GENERIC(_item, paymentaddresses) #define DATA_PAYMENTADDRESSES(_var, _item) DATA_GENERIC(_var, _item, paymentaddresses, true) +#define DATA_PAYMENTADDRESSES_NULL(_var, _item) DATA_GENERIC(_var, _item, paymentaddresses, false) extern K_TREE *paymentaddresses_root; extern K_LIST *paymentaddresses_free; extern K_STORE *paymentaddresses_store; +#define PAYRATIODEF 1000000 + // PAYMENTS typedef struct payments { int64_t paymentid; @@ -1553,7 +1557,8 @@ extern K_ITEM *new_default_worker(PGconn *conn, bool update, int64_t userid, cha char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root); extern void dsp_paymentaddresses(K_ITEM *item, FILE *stream); extern cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b); -extern K_ITEM *find_paymentaddresses(int64_t userid); +extern K_ITEM *find_paymentaddresses(int64_t userid, K_TREE_CTX *ctx); +extern K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx); extern cmp_t cmp_payments(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_optioncontrol(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_optioncontrol(char *optionname, tv_t *now); @@ -1695,9 +1700,9 @@ extern bool workers_update(PGconn *conn, K_ITEM *item, char *difficultydefault, char *idlenotificationtime, char *by, char *code, char *inet, tv_t *cd, K_TREE *trf_root, bool check); extern bool workers_fill(PGconn *conn); -extern K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress, - char *by, char *code, char *inet, tv_t *cd, - K_TREE *trf_root); +extern bool paymentaddresses_set(PGconn *conn, int64_t userid, K_LIST *pa_store, + char *by, char *code, char *inet, tv_t *cd, + K_TREE *trf_root); extern bool paymentaddresses_fill(PGconn *conn); extern bool payments_fill(PGconn *conn); extern bool idcontrol_add(PGconn *conn, char *idname, char *idvalue, char *by, diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 0d443b31..d175aea6 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -161,16 +161,21 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, __maybe_unused char *code, __maybe_unused char *inet, __maybe_unused tv_t *notcd, K_TREE *trf_root) { - K_ITEM *i_username, *i_passwordhash, *i_address, *i_email, *u_item, *pa_item; - char *email, *address; + K_ITEM *i_username, *i_passwordhash, *i_rows, *i_address, *i_ratio; + K_ITEM *i_email, *u_item, *pa_item; + char *email; char reply[1024] = ""; size_t siz = sizeof(reply); char tmp[1024]; PAYMENTADDRESSES *paymentaddresses; + K_STORE *pa_store = NULL; + K_TREE_CTX ctx[1]; USERS *users; char *reason = NULL; char *answer = NULL; size_t len, off; + int32_t ratio; + int rows, i; bool ok; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); @@ -206,18 +211,35 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, APPEND_REALLOC(answer, off, len, tmp); K_RLOCK(paymentaddresses_free); - pa_item = find_paymentaddresses(users->userid); - K_RUNLOCK(paymentaddresses_free); - + pa_item = find_paymentaddresses(users->userid, ctx); if (pa_item) { DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); - snprintf(tmp, sizeof(tmp), "addr=%s", - paymentaddresses->payaddress); - APPEND_REALLOC(answer, off, len, tmp); + while (pa_item && CURRENT(&(paymentaddresses->expirydate)) && + paymentaddresses->userid == users->userid) { + snprintf(tmp, sizeof(tmp), "addr:%d=%s%c", + rows, paymentaddresses->payaddress, FLDSEP); + APPEND_REALLOC(answer, off, len, tmp); + snprintf(tmp, sizeof(tmp), "ratio:%d=%d%c", + rows, paymentaddresses->payratio, FLDSEP); + APPEND_REALLOC(answer, off, len, tmp); + rows++; + + pa_item = prev_in_ktree(ctx); + DATA_PAYMENTADDRESSES_NULL(paymentaddresses, pa_item); + } + K_RUNLOCK(paymentaddresses_free); } else { - snprintf(tmp, sizeof(tmp), "addr="); - APPEND_REALLOC(answer, off, len, tmp); + K_RUNLOCK(paymentaddresses_free); + rows = 0; } + + snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", + rows, FLDSEP, + "addr,ratio", FLDSEP); + APPEND_REALLOC(answer, off, len, tmp); + snprintf(tmp, sizeof(tmp), "arn=%s%carp=%s", + "PaymentAddresses", FLDSEP, ""); + APPEND_REALLOC(answer, off, len, tmp); } else { if (!check_hash(users, transfer_data(i_passwordhash))) { reason = "Incorrect password"; @@ -235,29 +257,83 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } email = NULL; } - i_address = optional_name(trf_root, "address", - ADDR_MIN_LEN, - (char *)addrpatt, - reply, siz); - if (i_address) - address = transfer_data(i_address); - else { - if (*reply) { - reason = "Invalid address"; + + // address rows + i_rows = optional_name(trf_root, "rows", + 1, (char *)intpatt, + reply, siz); + if (!i_rows && *reply) { + // Exists, but invalid + reason = "System error"; + goto struckout; + } + if (i_rows) { + rows = atoi(transfer_data(i_rows)); + if (rows < 0) { + reason = "System error"; goto struckout; } - address = NULL; + if (rows > 0) { + pa_store = k_new_store(paymentaddresses_free); + K_WLOCK(paymentaddresses_free); + for (i = 0; i < rows; i++) { + snprintf(tmp, sizeof(tmp), "ratio:%d", i); + i_ratio = optional_name(trf_root, tmp, + 1, (char *)intpatt, + reply, siz); + if (*reply) { + K_WUNLOCK(paymentaddresses_free); + reason = "Invalid ratio"; + goto struckout; + } + if (i_ratio) + ratio = atoi(transfer_data(i_ratio)); + else + ratio = PAYRATIODEF; + + /* 0 = expire/remove the address + * intpatt means it will be >= 0 */ + if (ratio == 0) + continue; + + snprintf(tmp, sizeof(tmp), "address:%d", i); + i_address = require_name(trf_root, tmp, + ADDR_MIN_LEN, + (char *)addrpatt, + reply, siz); + if (!i_address) { + K_WUNLOCK(paymentaddresses_free); + reason = "Invalid address"; + goto struckout; + } + pa_item = k_unlink_head(paymentaddresses_free); + DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); + bzero(paymentaddresses, sizeof(*paymentaddresses)); + STRNCPY(paymentaddresses->payaddress, + transfer_data(i_address)); + paymentaddresses->payratio = ratio; + k_add_head(pa_store, pa_item); + } + K_WUNLOCK(paymentaddresses_free); + } } + /* If all addresses have a ratio of zero + * pa_store->count will be 0 */ if ((email == NULL || *email == '\0') && - (address == NULL || *address == '\0')) { + (pa_store == NULL || pa_store->count == 0)) { reason = "Missing/Invalid value"; goto struckout; } - if (address && *address) { - if (!btc_valid_address(address)) { - reason = "Invalid BTC address"; - goto struckout; + if (pa_store && pa_store->count > 0) { + pa_item = pa_store->head; + while (pa_item) { + DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); + if (!btc_valid_address(paymentaddresses->payaddress)) { + reason = "Invalid BTC address"; + goto struckout; + } + pa_item = pa_item->next; } } @@ -274,9 +350,9 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } } - if (address && *address) { + if (pa_store && pa_store->count > 0) { ok = paymentaddresses_set(conn, users->userid, - address, by, + pa_store, by, code, inet, now, trf_root); if (!ok) { @@ -289,6 +365,15 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, } struckout: + if (pa_store) { + if (pa_store->count) { + K_WLOCK(paymentaddresses_free); + k_list_transfer_to_head(pa_store, paymentaddresses_free); + K_WUNLOCK(paymentaddresses_free); + } + k_free_store(pa_store); + pa_store = NULL; + } if (reason) { snprintf(reply, siz, "ERR.%s", reason); LOGERR("%s.%s.%s", cmd, id, reply); @@ -2988,7 +3073,7 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, int64_t ss_count, wm_count, ms_count; char tv_buf[DATE_BUFSIZ]; tv_t cd, begin_tv, block_tv, end_tv; - K_TREE_CTX ctx[1], wm_ctx[1], ms_ctx[1]; + K_TREE_CTX ctx[1], wm_ctx[1], ms_ctx[1], pay_ctx[1]; double ndiff, total_diff, elapsed; double diff_times = 1.0; double diff_add = 0.0; @@ -3276,7 +3361,8 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, PAYMENTADDRESSES *pa; char *payaddress; - pa_item = find_paymentaddresses(miningpayouts->userid); + // TODO: handle multiple addresses for one user + pa_item = find_paymentaddresses(miningpayouts->userid, pay_ctx); if (pa_item) { DATA_PAYMENTADDRESSES(pa, pa_item); payaddress = pa->payaddress; diff --git a/src/ckdb_data.c b/src/ckdb_data.c index 9657176c..8c17cd35 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -1108,35 +1108,37 @@ void dsp_paymentaddresses(K_ITEM *item, FILE *stream) } } -// order by userid asc,expirydate desc,payaddress asc +// order by expirydate asc,userid asc,payaddress asc cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b) { PAYMENTADDRESSES *pa, *pb; DATA_PAYMENTADDRESSES(pa, a); DATA_PAYMENTADDRESSES(pb, b); - cmp_t c = CMP_BIGINT(pa->userid, pb->userid); + cmp_t c = CMP_TV(pa->expirydate, pb->expirydate); if (c == 0) { - c = CMP_TV(pb->expirydate, pa->expirydate); + c = CMP_BIGINT(pa->userid, pb->userid); if (c == 0) c = CMP_STR(pa->payaddress, pb->payaddress); } return c; } -// Only one for now ... -K_ITEM *find_paymentaddresses(int64_t userid) +/* Find the last CURRENT paymentaddresses for the given userid + * N.B. there can be more than one + * any more will be prev_in_ktree(ctx): CURRENT and userid matches */ +K_ITEM *find_paymentaddresses(int64_t userid, K_TREE_CTX *ctx) { PAYMENTADDRESSES paymentaddresses, *pa; - K_TREE_CTX ctx[1]; K_ITEM look, *item; - paymentaddresses.userid = userid; + paymentaddresses.expirydate.tv_sec = default_expiry.tv_sec; + paymentaddresses.expirydate.tv_usec = default_expiry.tv_usec; + paymentaddresses.userid = userid+1; paymentaddresses.payaddress[0] = '\0'; - paymentaddresses.expirydate.tv_sec = DATE_S_EOT; INIT_PAYMENTADDRESSES(&look); look.data = (void *)(&paymentaddresses); - item = find_after_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); + item = find_before_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); if (item) { DATA_PAYMENTADDRESSES(pa, item); if (pa->userid == userid && CURRENT(&(pa->expirydate))) @@ -1147,6 +1149,21 @@ K_ITEM *find_paymentaddresses(int64_t userid) return NULL; } +K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx) +{ + PAYMENTADDRESSES paymentaddresses; + K_ITEM look; + + paymentaddresses.expirydate.tv_sec = default_expiry.tv_sec; + paymentaddresses.expirydate.tv_usec = default_expiry.tv_usec; + paymentaddresses.userid = userid; + STRNCPY(paymentaddresses.payaddress, payaddress); + + INIT_PAYMENTADDRESSES(&look); + look.data = (void *)(&paymentaddresses); + return find_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); +} + // order by userid asc,paydate asc,payaddress asc,expirydate desc cmp_t cmp_payments(K_ITEM *a, K_ITEM *b) { diff --git a/src/ckdb_dbio.c b/src/ckdb_dbio.c index 471d5137..42be0f87 100644 --- a/src/ckdb_dbio.c +++ b/src/ckdb_dbio.c @@ -1427,56 +1427,44 @@ bool workers_fill(PGconn *conn) return ok; } -/* Whatever the current paymentaddresses are, replace them with this one - * Code allows for zero, one or more current payment address - * even though there currently can only be zero or one */ -K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress, - char *by, char *code, char *inet, tv_t *cd, - K_TREE *trf_root) +/* Whatever the current paymentaddresses are, replace them with the list + * in pa_store + * Code allows for zero, one or more current payment address */ +bool paymentaddresses_set(PGconn *conn, int64_t userid, K_STORE *pa_store, + char *by, char *code, char *inet, tv_t *cd, + K_TREE *trf_root) { ExecStatusType rescode; bool conned = false; PGresult *res; K_TREE_CTX ctx[1]; - K_ITEM *item, *old, *this, look; - PAYMENTADDRESSES *row, pa, *thispa; - char *upd, *ins; - bool ok = false; - char *params[4 + HISTORYDATECOUNT]; - int n, par = 0; + K_ITEM *item, *match, *next, *prev; + PAYMENTADDRESSES *row, *pa; + char *upd = NULL, *ins; + size_t len, off; + bool ok = false, first; + char *params[1002]; // Limit of 999 addresses per user + char tmp[1024]; + int n, par = 0, count, matches; LOGDEBUG("%s(): add", __func__); - K_WLOCK(paymentaddresses_free); - item = k_unlink_head(paymentaddresses_free); - K_WUNLOCK(paymentaddresses_free); - - DATA_PAYMENTADDRESSES(row, item); - - row->paymentaddressid = nextid(conn, "paymentaddressid", 1, - cd, by, code, inet); - if (row->paymentaddressid == 0) - goto unitem; - - row->userid = userid; - STRNCPY(row->payaddress, payaddress); - row->payratio = 1000000; - - HISTORYDATEINIT(row, cd, by, code, inet); - HISTORYDATETRANSFER(trf_root, row); + // Quick early abort + if (pa_store->count > 999) + return false; - upd = "update paymentaddresses set expirydate=$1 where userid=$2 and expirydate=$3"; - par = 0; - params[par++] = tv_to_buf(cd, NULL, 0); - params[par++] = bigint_to_buf(row->userid, NULL, 0); - params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0); - PARCHKVAL(par, 3, params); + /* Since we are merging the changes in rather than just + * replacing the db contents, lock the data for the duration + * of the update to ensure nothing else changes it */ + K_WLOCK(paymentaddresses_free); if (conn == NULL) { conn = dbconnect(); conned = true; } + /* This means the nextid updates will rollback on an error, but also + * means that it will lock the nextid record for the whole update */ res = PQexec(conn, "Begin", CKPQ_WRITE); rescode = PQresultStatus(res); if (!PGOK(rescode)) { @@ -1485,36 +1473,126 @@ K_ITEM *paymentaddresses_set(PGconn *conn, int64_t userid, char *payaddress, } PQclear(res); - 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; + // First step - DB expire all the old/changed records in RAM + LOGDEBUG("%s(): Step 1 userid=%"PRId64, __func__, userid); + count = matches = 0; + APPEND_REALLOC_INIT(upd, off, len); + APPEND_REALLOC(upd, off, len, + "update paymentaddresses set expirydate=$1 where " + "userid=$2 and expirydate=$3 and payaddress in ("); + par = 0; + params[par++] = tv_to_buf(cd, NULL, 0); + params[par++] = bigint_to_buf(userid, NULL, 0); + params[par++] = tv_to_buf((tv_t *)&default_expiry, NULL, 0); + + first = true; + item = find_paymentaddresses(userid, ctx); + DATA_PAYMENTADDRESSES_NULL(row, item); + while (item && CURRENT(&(row->expirydate)) && row->userid == userid) { + /* This is only possible if the DB was directly updated with + * more than 999 records then reloaded (or a code bug) */ + if (++count > 999) + break; + + // Find the RAM record in pa_store + match = pa_store->head; + while (match) { + DATA_PAYMENTADDRESSES(pa, match); + if (strcmp(pa->payaddress, row->payaddress) == 0 && + pa->payratio == row->payratio) { + pa->match = true; // Don't store it + matches++; + break; + } + match = match->next; + } + if (!match) { + // No matching replacement, so expire 'row' + params[par++] = str_to_buf(row->payaddress, NULL, 0); + if (!first) + APPEND_REALLOC(upd, off, len, ","); + first = false; + snprintf(tmp, sizeof(tmp), "$%d", par); + APPEND_REALLOC(upd, off, len, tmp); + } + item = prev_in_ktree(ctx); + DATA_PAYMENTADDRESSES_NULL(row, item); } + LOGDEBUG("%s(): Step 1 par=%d count=%d matches=%d first=%s", __func__, + par, count, matches, first ? "true" : "false"); + // Too many, or none need expiring = don't do the update + if (count > 999 || first == true) { + for (n = 0; n < par; n++) + free(params[n]); + par = 0; + // Too many + if (count > 999) + goto rollback; + } else { + APPEND_REALLOC(upd, off, len, ")"); + PARCHKVAL(par, par, 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]); + LOGDEBUG("%s(): Step 1 expired %d", __func__, par-3); + + for (n = 0; n < par; n++) + free(params[n]); + par = 0; + } + // Second step - add the non-matching records to the DB + LOGDEBUG("%s(): Step 2", __func__); ins = "insert into paymentaddresses " "(paymentaddressid,userid,payaddress,payratio" HISTORYDATECONTROL ") values (" PQPARAM9 ")"; - par = 0; - params[par++] = bigint_to_buf(row->paymentaddressid, NULL, 0); - params[par++] = bigint_to_buf(row->userid, NULL, 0); - params[par++] = str_to_buf(row->payaddress, NULL, 0); - params[par++] = int_to_buf(row->payratio, NULL, 0); - HISTORYDATEPARAMS(params, par, row); - PARCHK(par, params); + count = 0; + match = pa_store->head; + while (match) { + DATA_PAYMENTADDRESSES(row, match); + if (!row->match) { + row->paymentaddressid = nextid(conn, "paymentaddressid", 1, + cd, by, code, inet); + if (row->paymentaddressid == 0) + goto rollback; - 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; + row->userid = userid; + + HISTORYDATEINIT(row, cd, by, code, inet); + HISTORYDATETRANSFER(trf_root, row); + + par = 0; + params[par++] = bigint_to_buf(row->paymentaddressid, NULL, 0); + params[par++] = bigint_to_buf(row->userid, NULL, 0); + params[par++] = str_to_buf(row->payaddress, NULL, 0); + params[par++] = int_to_buf(row->payratio, NULL, 0); + HISTORYDATEPARAMS(params, par, row); + PARCHKVAL(par, 9, params); // As per PQPARAM9 above + + 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; + } + + for (n = 0; n < par; n++) + free(params[n]); + par = 0; + + count++; + } + match = match->next; } + LOGDEBUG("%s(): Step 2 inserted %d", __func__, count); ok = true; rollback: @@ -1529,46 +1607,62 @@ unparam: PQfinish(conn); for (n = 0; n < par; n++) free(params[n]); -unitem: - K_WLOCK(paymentaddresses_free); - if (!ok) - k_add_head(paymentaddresses_free, item); - else { - // Change the expiry on all the old ones - pa.userid = userid; - pa.expirydate.tv_sec = DATE_S_EOT; - pa.payaddress[0] = '\0'; - INIT_PAYMENTADDRESSES(&look); - look.data = (void *)(&pa); - // Tree order is expirydate desc - old = find_after_in_ktree(paymentaddresses_root, &look, - cmp_paymentaddresses, ctx); - while (old) { - this = old; - DATA_PAYMENTADDRESSES(thispa, this); - if (thispa->userid != userid) - break; - old = next_in_ktree(ctx); - /* Tree remove+add below doesn't matter since - * this test will avoid reprocessing */ - if (CURRENT(&(thispa->expirydate))) { - paymentaddresses_root = remove_from_ktree(paymentaddresses_root, this, + FREENULL(upd); + // Third step - do step 1 and 2 to the RAM version of the DB + LOGDEBUG("%s(): Step 3, ok=%s", __func__, ok ? "true" : "false"); + matches = count = n = 0; + if (ok) { + // Change the expiry on all records that we expired in the DB + item = find_paymentaddresses(userid, ctx); + DATA_PAYMENTADDRESSES_NULL(row, item); + while (item && CURRENT(&(row->expirydate)) && row->userid == userid) { + prev = prev_in_ktree(ctx); + // Find the RAM record in pa_store + match = pa_store->head; + while (match) { + DATA_PAYMENTADDRESSES(pa, match); + if (strcmp(pa->payaddress, row->payaddress) == 0 && + pa->payratio == row->payratio) { + break; + } + match = match->next; + } + if (match) + matches++; + else { + // It wasn't a match, thus it was expired + n++; + paymentaddresses_root = remove_from_ktree(paymentaddresses_root, item, cmp_paymentaddresses); - copy_tv(&(thispa->expirydate), cd); - paymentaddresses_root = add_to_ktree(paymentaddresses_root, this, + copy_tv(&(row->expirydate), cd); + paymentaddresses_root = add_to_ktree(paymentaddresses_root, item, cmp_paymentaddresses); } + item = prev; + DATA_PAYMENTADDRESSES_NULL(row, item); + } + + // Add in all the non-matching ps_store + match = pa_store->head; + while (match) { + next = match->next; + DATA_PAYMENTADDRESSES(pa, match); + if (!pa->match) { + paymentaddresses_root = add_to_ktree(paymentaddresses_root, match, + cmp_paymentaddresses); + k_unlink_item(pa_store, match); + k_add_head(paymentaddresses_store, match); + count++; + } + match = next; } - paymentaddresses_root = add_to_ktree(paymentaddresses_root, item, - cmp_paymentaddresses); - k_add_head(paymentaddresses_store, item); } + LOGDEBUG("%s(): Step 3, untouched %d expired %d added %d", __func__, matches, n, count); + K_WUNLOCK(paymentaddresses_free); - if (ok) - return item; - else - return NULL; + // Calling function must clean up anything left in pa_store + return ok; } bool paymentaddresses_fill(PGconn *conn) From 9b1f76e5c45f95566feb8f1e2ceca8fefeec9b1f Mon Sep 17 00:00:00 2001 From: kanoi Date: Thu, 11 Dec 2014 16:50:49 +1100 Subject: [PATCH 11/21] ckdb/php - display account messages at the top of the page --- pool/page.php | 11 ++++++++++- src/ckdb.h | 5 ++++- src/ckdb_cmd.c | 28 +++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/pool/page.php b/pool/page.php index 2f335fcf..1b794671 100644 --- a/pool/page.php +++ b/pool/page.php @@ -88,6 +88,8 @@ function pghead($script_marker, $name) $head .= "$page_title$name"; $head .= ""; + $head .= ""; + $head .= ""; $head .= ""; + + return $pg; +} +# +function doaddrmgt($data, $user) +{ + $err = ''; + $OK = getparam('OK', false); + $count = getparam('rows', false); + $pass = getparam('pass', false); + if ($OK == 'OK' && !nuem($count) && !nuem($pass)) + { + if ($count > 0 && $count < 1000) + { + $addrarr = array(); + for ($i = 0; $i < $count; $i++) + { + $addr = getparam('addr:'.$i, false); + $ratio = getparam('ratio:'.$i, false); + if (!nuem($addr) && !nuem($ratio)) + $addrarr[] = array('addr' => $addr, 'ratio' => $ratio); + } + $ans = userSettings($user, null, $addrarr, $pass); + if ($ans['STATUS'] != 'ok') + $err = $ans['ERROR']; +#$err = print_r($addrarr, true).$pass; + } + } + + $pg = addrmgtuser($data, $user, $err); + + return $pg; +} +# +function show_addrmgt($info, $page, $menu, $name, $user) +{ + gopage($info, NULL, 'doaddrmgt', $page, $menu, $name, $user); +} +# +?> diff --git a/pool/page_allwork.php b/pool/page_allwork.php index af210dfb..d190f398 100644 --- a/pool/page_allwork.php +++ b/pool/page_allwork.php @@ -39,9 +39,9 @@ function doallwork($data, $user) return $pg; } # -function show_allwork($page, $menu, $name, $user) +function show_allwork($info, $page, $menu, $name, $user) { - gopage(NULL, 'doallwork', $page, $menu, $name, $user); + gopage($info, NULL, 'doallwork', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_api.php b/pool/page_api.php index 8b773410..9819b5b7 100644 --- a/pool/page_api.php +++ b/pool/page_api.php @@ -9,7 +9,7 @@ function no_api($json = "") exit(0); } # -function show_api($page, $menu, $name, $user) +function show_api($info, $page, $menu, $name, $user) { global $fld_sep; $u = getparam('username', true); @@ -29,20 +29,21 @@ function show_api($page, $menu, $name, $user) no_api($jfu); if (nuem($work)) { - $ans = homeInfo($u); - if ($ans === false) + if ($info === NULL) + $info = homeInfo($u); + if ($info === false) no_api($jfu); - $rep = fldEncode($ans, 'lastbc', true); - $rep .= fldEncode($ans, 'lastheight', false); - $rep .= fldEncode($ans, 'currndiff', false); - $rep .= fldEncode($ans, 'lastblock', false); - $rep .= fldEncode($ans, 'lastblockheight', false); - $rep .= fldEncode($ans, 'blockacc', false); - $rep .= fldEncode($ans, 'blockerr', false); - $rep .= fldEncode($ans, 'p_hashrate5m', false); - $rep .= fldEncode($ans, 'p_hashrate1hr', false); - $rep .= fldEncode($ans, 'u_hashrate5m', false); - $rep .= fldEncode($ans, 'u_hashrate1hr', false); + $rep = fldEncode($info, 'lastbc', true); + $rep .= fldEncode($info, 'lastheight', false); + $rep .= fldEncode($info, 'currndiff', false); + $rep .= fldEncode($info, 'lastblock', false); + $rep .= fldEncode($info, 'lastblockheight', false); + $rep .= fldEncode($info, 'blockacc', false); + $rep .= fldEncode($info, 'blockerr', false); + $rep .= fldEncode($info, 'p_hashrate5m', false); + $rep .= fldEncode($info, 'p_hashrate1hr', false); + $rep .= fldEncode($info, 'u_hashrate5m', false); + $rep .= fldEncode($info, 'u_hashrate1hr', false); } else { diff --git a/pool/page_blocks.php b/pool/page_blocks.php index 95aeba1e..1bf6b9f3 100644 --- a/pool/page_blocks.php +++ b/pool/page_blocks.php @@ -182,9 +182,9 @@ function doblocks($data, $user) return $pg; } # -function show_blocks($page, $menu, $name, $user) +function show_blocks($info, $page, $menu, $name, $user) { - gopage(NULL, 'doblocks', $page, $menu, $name, $user); + gopage($info, NULL, 'doblocks', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_ckp.php b/pool/page_ckp.php index dad79f4f..aa59fb97 100644 --- a/pool/page_ckp.php +++ b/pool/page_ckp.php @@ -62,9 +62,9 @@ function dockp($data, $user) return $pg; } # -function show_ckp($page, $menu, $name, $user) +function show_ckp($info, $page, $menu, $name, $user) { - gopage(NULL, 'dockp', $page, $menu, $name, $user); + gopage($info, NULL, 'dockp', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_help.php b/pool/page_help.php index 41546dcf..2ed74e87 100644 --- a/pool/page_help.php +++ b/pool/page_help.php @@ -5,9 +5,9 @@ function dohelp($data, $user) return '

Helpless

Helpless'; } # -function show_help($page, $menu, $name, $user) +function show_help($info, $page, $menu, $name, $user) { - gopage(NULL, 'dohelp', $page, $menu, $name, $user); + gopage($info, NULL, 'dohelp', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_index.php b/pool/page_index.php index d9d423fb..106d0402 100644 --- a/pool/page_index.php +++ b/pool/page_index.php @@ -2,9 +2,9 @@ # @include_once('myindex.php'); # -function show_index($page, $menu, $name, $user) +function show_index($info, $page, $menu, $name, $user) { - gopage(NULL, 'doindex', $page, $menu, $name, $user); + gopage($info, NULL, 'doindex', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_payments.php b/pool/page_payments.php index f3ddc309..db6919d4 100644 --- a/pool/page_payments.php +++ b/pool/page_payments.php @@ -34,9 +34,9 @@ function dopayments($data, $user) return $pg; } # -function show_payments($page, $menu, $name, $user) +function show_payments($info, $page, $menu, $name, $user) { - gopage(NULL, 'dopayments', $page, $menu, $name, $user); + gopage($info, NULL, 'dopayments', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_payout.php b/pool/page_payout.php index af87e2e6..cca9ee40 100644 --- a/pool/page_payout.php +++ b/pool/page_payout.php @@ -13,9 +13,9 @@ function dopayout($data, $user) return $pg; } # -function show_payout($page, $menu, $name, $user) +function show_payout($info, $page, $menu, $name, $user) { - gopage(NULL, 'dopayout', $page, $menu, $name, $user); + gopage($info, NULL, 'dopayout', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_pblocks.php b/pool/page_pblocks.php index 86e9aea7..7bedd01d 100644 --- a/pool/page_pblocks.php +++ b/pool/page_pblocks.php @@ -7,9 +7,9 @@ function dopblocks($data, $user) return doblocks($data, null); } # -function show_pblocks($page, $menu, $name, $user) +function show_pblocks($info, $page, $menu, $name, $user) { - gopage(NULL, 'dopblocks', $page, $menu, $name, $user); + gopage($info, NULL, 'dopblocks', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_pplns.php b/pool/page_pplns.php index 116763aa..4f3daff9 100644 --- a/pool/page_pplns.php +++ b/pool/page_pplns.php @@ -281,9 +281,9 @@ Block: return $pg; } # -function show_pplns($page, $menu, $name, $user) +function show_pplns($info, $page, $menu, $name, $user) { - gopage(NULL, 'dopplns', $page, $menu, $name, $user); + gopage($info, NULL, 'dopplns', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_reg.php b/pool/page_reg.php index d1411742..05021106 100644 --- a/pool/page_reg.php +++ b/pool/page_reg.php @@ -79,7 +79,7 @@ function doreg2($data) return $pg; } # -function try_reg($page, $menu, $name, $u) +function try_reg($info, $page, $menu, $name, $u) { $user = getparam('user', false); $mail = trim(getparam('mail', false)); @@ -128,12 +128,12 @@ function try_reg($page, $menu, $name, $u) { $ans = userReg($user, $mail, $pass); if ($ans['STATUS'] == 'ok') - gopage($data, 'doreg2', $page, $menu, $name, $u, true, true, false); + gopage($info, $data, 'doreg2', $page, $menu, $name, $u, true, true, false); else $data['error'] = "Invalid username, password or email address"; } - gopage($data, 'doregres', $page, $menu, $name, $u, true, true, false); + gopage($info, $data, 'doregres', $page, $menu, $name, $u, true, true, false); } # function doreset2($data) @@ -179,7 +179,7 @@ function doreset2($data) return $pg; } # -function try_reset($page, $menu, $name, $u) +function try_reset($info, $page, $menu, $name, $u) { $user = getparam('user', false); $mail = trim(getparam('mail', false)); @@ -199,20 +199,20 @@ function try_reset($page, $menu, $name, $u) { $data = array('user' => $user, 'email' => $mail); - gopage($data, 'doreset2', $page, $menu, $name, $u, true, true, false); + gopage($info, $data, 'doreset2', $page, $menu, $name, $u, true, true, false); } } - gopage($data, 'doregres', $page, $menu, $name, $u, true, true, false); + gopage($info, $data, 'doregres', $page, $menu, $name, $u, true, true, false); } # -function show_reg($page, $menu, $name, $u) +function show_reg($info, $page, $menu, $name, $u) { $reg = getparam('Register', false); if ($reg !== NULL) - try_reg($page, $menu, $name, $u); + try_reg($info, $page, $menu, $name, $u); else - try_reset($page, $menu, $name, $u); + try_reset($info, $page, $menu, $name, $u); } # ?> diff --git a/pool/page_reset.php b/pool/page_reset.php index 7dc584fe..57be33fc 100644 --- a/pool/page_reset.php +++ b/pool/page_reset.php @@ -158,9 +158,9 @@ function doreset($data, $u) return resetfail(); } # -function show_reset($page, $menu, $name, $u) +function show_reset($info, $page, $menu, $name, $u) { - gopage(array(), 'doreset', $page, $menu, $name, $u, true, true, false); + gopage($info, array(), 'doreset', $page, $menu, $name, $u, true, true, false); } # ?> diff --git a/pool/page_settings.php b/pool/page_settings.php index 5838dfdc..c6d30757 100644 --- a/pool/page_settings.php +++ b/pool/page_settings.php @@ -33,28 +33,31 @@ function settings($data, $user, $email, $addr, $err) $pg .= ''; $pg .= '
'; - $pg .= makeForm('settings'); - $pg .= ''; - $pg .= ''; - $pg .= ''; - $pg .= ''; - $pg .= ''; - $pg .= '
'; - $pg .= 'To change your payout address, enter a new address and your password'; - $pg .= '
'; - $pg .= 'BTC Address:'; - $pg .= ''; - $pg .= ""; - $pg .= '
'; - $pg .= 'Password:'; - $pg .= ''; - $pg .= ''; - $pg .= '
'; - $pg .= 'Change: '; - $pg .= '
'; + if (!isset($data['info']['u_multiaddr'])) + { + $pg .= makeForm('settings'); + $pg .= ''; + $pg .= ''; + $pg .= ''; + $pg .= ''; + $pg .= ''; + $pg .= '
'; + $pg .= 'To change your payout address, enter a new address and your password'; + $pg .= '
'; + $pg .= 'BTC Address:'; + $pg .= ''; + $pg .= ""; + $pg .= '
'; + $pg .= 'Password:'; + $pg .= ''; + $pg .= ''; + $pg .= '
'; + $pg .= 'Change: '; + $pg .= '
'; - $pg .= '
'; - $pg .= '
'; + $pg .= '
'; + $pg .= '
'; + } $pg .= makeForm('settings'); $pg .= ''; @@ -101,11 +104,14 @@ function dosettings($data, $user) $check = true; break; case 'Address': - $addr = getparam('baddr', false); - $addrarr = array(array('addr' => $addr)); - $pass = getparam('pass', false); - $ans = userSettings($user, null, $addrarr, $pass); - $check = true; + if (!isset($data['info']['u_multiaddr'])) + { + $addr = getparam('baddr', false); + $addrarr = array(array('addr' => $addr)); + $pass = getparam('pass', false); + $ans = userSettings($user, null, $addrarr, $pass); + $check = true; + } break; case 'Password': $oldpass = getparam('oldpass', false); @@ -149,9 +155,9 @@ function dosettings($data, $user) return $pg; } # -function show_settings($page, $menu, $name, $user) +function show_settings($info, $page, $menu, $name, $user) { - gopage(NULL, 'dosettings', $page, $menu, $name, $user); + gopage($info, NULL, 'dosettings', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_stats.php b/pool/page_stats.php index 203de795..90768813 100644 --- a/pool/page_stats.php +++ b/pool/page_stats.php @@ -99,9 +99,9 @@ function dostats($data, $user) return $pg; } # -function show_stats($page, $menu, $name, $user) +function show_stats($info, $page, $menu, $name, $user) { - gopage(NULL, 'dostats', $page, $menu, $name, $user); + gopage($info, NULL, 'dostats', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_userset.php b/pool/page_userset.php index 9764fa5f..ae4b382c 100644 --- a/pool/page_userset.php +++ b/pool/page_userset.php @@ -92,9 +92,9 @@ function douserset($data, $user) return $pg; } # -function show_userset($page, $menu, $name, $user) +function show_userset($info, $page, $menu, $name, $user) { - gopage(NULL, 'douserset', $page, $menu, $name, $user); + gopage($info, NULL, 'douserset', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_workers.php b/pool/page_workers.php index 938aee9a..dcbfa46c 100644 --- a/pool/page_workers.php +++ b/pool/page_workers.php @@ -188,9 +188,9 @@ function doworkers($data, $user) return $pg; } # -function show_workers($page, $menu, $name, $user) +function show_workers($info, $page, $menu, $name, $user) { - gopage(NULL, 'doworkers', $page, $menu, $name, $user); + gopage($info, NULL, 'doworkers', $page, $menu, $name, $user); } # ?> diff --git a/pool/page_workmgt.php b/pool/page_workmgt.php index 4cea1569..e7ad249a 100644 --- a/pool/page_workmgt.php +++ b/pool/page_workmgt.php @@ -86,9 +86,9 @@ function doworkmgt($data, $user) return $pg; } # -function show_workmgt($page, $menu, $name, $user) +function show_workmgt($info, $page, $menu, $name, $user) { - gopage(NULL, 'doworkmgt', $page, $menu, $name, $user); + gopage($info, NULL, 'doworkmgt', $page, $menu, $name, $user); } # ?> diff --git a/pool/prime.php b/pool/prime.php index c50c69f7..af2bf796 100644 --- a/pool/prime.php +++ b/pool/prime.php @@ -8,6 +8,12 @@ include_once('base.php'); # function process($p, $user, $menu) { + $info = homeInfo($user); + if (is_array($info) && isset($info['u_multiaddr'])) + { + if (isset($menu['Account'])) + $menu['Account']['Addresses'] = 'addrmgt'; + } if ($user == 'Kano' || $user == 'ckolivas' || $user == 'wvr2' || $user == 'aphorise') { $menu['Admin']['ckp'] = 'ckp'; @@ -26,9 +32,9 @@ function process($p, $user, $menu) } if ($page === '') - showPage('index', $menu, '', $user); + showPage($info, 'index', $menu, '', $user); else - showPage($page, $menu, $n, $user); + showPage($info, $page, $menu, $n, $user); } # function def_menu() @@ -80,11 +86,11 @@ function check() { $p = getparam('k', true); if ($p == 'reset') - showPage('reset', $dmenu, '', $who); + showPage(NULL, 'reset', $dmenu, '', $who); else { if (requestRegister() == true) - showPage('reg', $dmenu, '', $who); + showPage(NULL, 'reg', $dmenu, '', $who); else { $p = getparam('k', true); diff --git a/src/ckdb.h b/src/ckdb.h index f656b522..3f28e06d 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.741" +#define CKDB_VERSION DB_VERSION"-0.742" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 4b591683..9d1b1ee4 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -163,11 +163,11 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, { K_ITEM *i_username, *i_passwordhash, *i_rows, *i_address, *i_ratio; K_ITEM *i_email, *u_item, *pa_item; - char *email; + char *email, *address; char reply[1024] = ""; size_t siz = sizeof(reply); char tmp[1024]; - PAYMENTADDRESSES *paymentaddresses; + PAYMENTADDRESSES *row; K_STORE *pa_store = NULL; K_TREE_CTX ctx[1]; USERS *users; @@ -212,26 +212,24 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, K_RLOCK(paymentaddresses_free); pa_item = find_paymentaddresses(users->userid, ctx); + rows = 0; if (pa_item) { - DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); - while (pa_item && CURRENT(&(paymentaddresses->expirydate)) && - paymentaddresses->userid == users->userid) { + DATA_PAYMENTADDRESSES(row, pa_item); + while (pa_item && CURRENT(&(row->expirydate)) && + row->userid == users->userid) { snprintf(tmp, sizeof(tmp), "addr:%d=%s%c", - rows, paymentaddresses->payaddress, FLDSEP); + rows, row->payaddress, FLDSEP); APPEND_REALLOC(answer, off, len, tmp); snprintf(tmp, sizeof(tmp), "ratio:%d=%d%c", - rows, paymentaddresses->payratio, FLDSEP); + rows, row->payratio, FLDSEP); APPEND_REALLOC(answer, off, len, tmp); rows++; pa_item = prev_in_ktree(ctx); - DATA_PAYMENTADDRESSES_NULL(paymentaddresses, pa_item); + DATA_PAYMENTADDRESSES_NULL(row, pa_item); } - K_RUNLOCK(paymentaddresses_free); - } else { - K_RUNLOCK(paymentaddresses_free); - rows = 0; } + K_RUNLOCK(paymentaddresses_free); snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", rows, FLDSEP, @@ -306,12 +304,22 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, reason = "Invalid address"; goto struckout; } + address = transfer_data(i_address); + pa_item = pa_store->head; + while (pa_item) { + DATA_PAYMENTADDRESSES(row, pa_item); + if (strcmp(row->payaddress, address) == 0) { + K_WUNLOCK(paymentaddresses_free); + reason = "Duplicate address"; + goto struckout; + } + pa_item = pa_item->next; + } pa_item = k_unlink_head(paymentaddresses_free); - DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); - bzero(paymentaddresses, sizeof(*paymentaddresses)); - STRNCPY(paymentaddresses->payaddress, - transfer_data(i_address)); - paymentaddresses->payratio = ratio; + DATA_PAYMENTADDRESSES(row, pa_item); + bzero(row, sizeof(*row)); + STRNCPY(row->payaddress, address); + row->payratio = ratio; k_add_head(pa_store, pa_item); } K_WUNLOCK(paymentaddresses_free); @@ -328,8 +336,8 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, if (pa_store && pa_store->count > 0) { pa_item = pa_store->head; while (pa_item) { - DATA_PAYMENTADDRESSES(paymentaddresses, pa_item); - if (!btc_valid_address(paymentaddresses->payaddress)) { + DATA_PAYMENTADDRESSES(row, pa_item); + if (!btc_valid_address(row->payaddress)) { reason = "Invalid BTC address"; goto struckout; } From cf3658d88156b7e29bb51b3d2d83cfa46dbdad83 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 12 Dec 2014 08:46:14 +1100 Subject: [PATCH 14/21] ckdb/php pplns handle user with multiple payout addresses --- pool/page_pplns.php | 5 +-- src/ckdb.h | 2 +- src/ckdb_cmd.c | 79 +++++++++++++++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/pool/page_pplns.php b/pool/page_pplns.php index 4f3daff9..50eb8b4b 100644 --- a/pool/page_pplns.php +++ b/pool/page_pplns.php @@ -42,10 +42,11 @@ function calctx($ans, $count, $miner_sat, $diffacc_total) $payaddress = $ans['payaddress:'.$i]; if ($payaddress == 'none') { - $len = strlen($username); $c0 = substr($username, 0, 1); + $parts = explode('.', $username); + $len = strlen($parts[0]); if (($c0 == '1' || $c0 == '3') && $len > 26 && $len < 37) - $payaddress = $username; + $payaddress = $parts[0]; else { if ($pay_sat > 0) diff --git a/src/ckdb.h b/src/ckdb.h index 3f28e06d..eb86cebc 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.742" +#define CKDB_VERSION DB_VERSION"-0.743" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index 9d1b1ee4..cb02768a 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -3390,42 +3390,71 @@ static char *cmd_pplns(__maybe_unused PGconn *conn, char *cmd, char *id, K_RLOCK(users_free); u_item = find_userid(miningpayouts->userid); K_RUNLOCK(users_free); - if (u_item) { - K_ITEM *pa_item; - PAYMENTADDRESSES *pa; - char *payaddress; + if (!u_item) { + snprintf(reply, siz, + "ERR.unknown userid %"PRId64, + miningpayouts->userid); + goto shazbot; + } - // TODO: handle multiple addresses for one user + DATA_USERS(users, u_item); + + K_ITEM *pa_item; + PAYMENTADDRESSES *pa; + int64_t paytotal; + double amount; + int count; + + K_RLOCK(paymentaddresses_free); + pa_item = find_paymentaddresses(miningpayouts->userid, pay_ctx); + if (pa_item) { + paytotal = 0; + DATA_PAYMENTADDRESSES(pa, pa_item); + while (pa_item && CURRENT(&(pa->expirydate)) && + pa->userid == miningpayouts->userid) { + paytotal += pa->payratio; + pa_item = prev_in_ktree(pay_ctx); + DATA_PAYMENTADDRESSES_NULL(pa, pa_item); + } + count = 0; pa_item = find_paymentaddresses(miningpayouts->userid, pay_ctx); - if (pa_item) { - DATA_PAYMENTADDRESSES(pa, pa_item); - payaddress = pa->payaddress; - } else - payaddress = "none"; + DATA_PAYMENTADDRESSES_NULL(pa, pa_item); + while (pa_item && CURRENT(&(pa->expirydate)) && + pa->userid == miningpayouts->userid) { + amount = (double)(miningpayouts->amount) * + (double)pa->payratio / (double)paytotal; - DATA_USERS(users, u_item); - snprintf(tmp, sizeof(tmp), - "user:%d=%s%cpayaddress:%d=%s%c", - rows, users->username, FLDSEP, - rows, payaddress, FLDSEP); + snprintf(tmp, sizeof(tmp), + "user:%d=%s.%d%cpayaddress:%d=%s%c", + rows, users->username, ++count, FLDSEP, + rows, pa->payaddress, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + snprintf(tmp, sizeof(tmp), + "diffacc_user:%d=%.1f%c", + rows, amount, FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); + rows++; + pa_item = prev_in_ktree(pay_ctx); + DATA_PAYMENTADDRESSES_NULL(pa, pa_item); + } + K_RUNLOCK(paymentaddresses_free); } else { + K_RUNLOCK(paymentaddresses_free); + snprintf(tmp, sizeof(tmp), + "user:%d=%s.0%cpayaddress:%d=%s%c", + rows, users->username, FLDSEP, + rows, "none", FLDSEP); + APPEND_REALLOC(buf, off, len, tmp); snprintf(tmp, sizeof(tmp), - "user:%d=%"PRId64"%cpayaddress:%d=none%c", - rows, miningpayouts->userid, FLDSEP, - rows, FLDSEP); - } - APPEND_REALLOC(buf, off, len, tmp); - - snprintf(tmp, sizeof(tmp), "diffacc_user:%d=%"PRId64"%c", rows, miningpayouts->amount, FLDSEP); - APPEND_REALLOC(buf, off, len, tmp); - + APPEND_REALLOC(buf, off, len, tmp); + rows++; + } mu_item = next_in_ktree(ctx); - rows++; } snprintf(tmp, sizeof(tmp), "rows=%d%cflds=%s%c", From d1cbdc56db6fea1359e7f5bfbb1cbec4a1cc927d Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 12 Dec 2014 08:47:14 +1100 Subject: [PATCH 15/21] ckpmsg - display up to 64k of the reply data --- src/ckpmsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ckpmsg.c b/src/ckpmsg.c index c6f4e387..6f007df7 100644 --- a/src/ckpmsg.c +++ b/src/ckpmsg.c @@ -130,7 +130,7 @@ int main(int argc, char **argv) continue; } mkstamp(stamp, sizeof(stamp)); - LOGNOTICE("%s Received response: %s", stamp, buf); + LOGMSGSIZ(65536, LOG_NOTICE, "%s Received response: %s", stamp, buf); } dealloc(buf); From 1ee6c04722c66ac94348c8e94ce13c4f7f0b4ff3 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 12 Dec 2014 15:29:10 +1100 Subject: [PATCH 16/21] php - add a blockchain link on the payments page to the payout address --- pool/page_payments.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pool/page_payments.php b/pool/page_payments.php index db6919d4..280b0a6c 100644 --- a/pool/page_payments.php +++ b/pool/page_payments.php @@ -2,7 +2,12 @@ # function dopayments($data, $user) { + $bc = 'https://blockchain.info/address/'; + $addr1 = '1KzFJddTvK9TQWsmWFKYJ9fRx9QeSATyrT'; + $pg = '

Payments

'; + $pg .= 'The payout transactions on blockchain are here:'; + $pg .= " BTC

"; $ans = getPayments($user); From 6ae7b58bb042a38cfa58f079ee26cfa390eb2c23 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 12 Dec 2014 15:31:02 +1100 Subject: [PATCH 17/21] php - update payout description to match kano pool --- pool/page_payout.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pool/page_payout.php b/pool/page_payout.php index cca9ee40..e45e70ca 100644 --- a/pool/page_payout.php +++ b/pool/page_payout.php @@ -5,10 +5,16 @@ function dopayout($data, $user) $pg = '

Payouts

'; $pg .= '
'; $pg .= '
'; - $pg .= 'We use PPLNS (pay per last N shares)
'; - $pg .= 'Pool fee is 1.5%
'; + $pg .= 'We use PPLNS (pay per last N shares)

'; $pg .= 'The N value used for PPLNS is the network difficulty'; - $pg .= ' when a block is found.'; + $pg .= ' when a block is found,
'; + $pg .= 'but includes the full shift at the start and end of the range,
'; + $pg .= 'so it usually will be a bit more than N.

'; + $pg .= 'Shifts are ~30s long, however, when a block is found
'; + $pg .= 'the current shift ends at the point the block was found.
'; + $pg .= 'A ckpool restart will also start a new shift.

'; + $pg .= 'Transaction fees are included in the miner payout.
'; + $pg .= 'Pool fee is 0.9% of the total.
'; $pg .= '
'; return $pg; } From 2e00dc5d3b7c992779e9d872d44c06b89270e339 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 12 Dec 2014 21:30:46 +1100 Subject: [PATCH 18/21] php - fix page_api warning --- pool/page_api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pool/page_api.php b/pool/page_api.php index 9819b5b7..82fbf0a6 100644 --- a/pool/page_api.php +++ b/pool/page_api.php @@ -50,7 +50,7 @@ function show_api($info, $page, $menu, $name, $user) $ans = getWorkers($u); if ($ans === false) no_api($jfu); - $rep .= fldEncode($ans, 'rows', true); + $rep = fldEncode($ans, 'rows', true); $rows = $ans['rows']; $flds = explode(',', $ans['flds']); $zeflds = ''; From 6647df49b6a8ba93086d9d5b31ea7d32166cdf31 Mon Sep 17 00:00:00 2001 From: kanoi Date: Fri, 12 Dec 2014 22:43:04 +1100 Subject: [PATCH 19/21] php - addresses page, fix '+' to work with no addresses --- pool/page_addrmgt.php | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/pool/page_addrmgt.php b/pool/page_addrmgt.php index 042ad5dc..0ef12d85 100644 --- a/pool/page_addrmgt.php +++ b/pool/page_addrmgt.php @@ -15,6 +15,19 @@ function addrmgtuser($data, $user, $err) $pg .= '%'; $pg .= ''; + # new row template for '+' + $pg .= ''; + $pg .= ''; + $pg .= ""; + $pg .= ''; + $pg .= ''; + $pg .= ""; + $pg .= ''; + $pg .= ''; + $pg .= "0.00%"; + $pg .= ''; + $pg .= "\n"; + $ans = userSettings($user); $offset = 0; @@ -29,25 +42,18 @@ function addrmgtuser($data, $user, $err) else $row = 'odd'; - $pg .= " 0 && $count < 1000) { @@ -116,7 +122,6 @@ function doaddrmgt($data, $user) $ans = userSettings($user, null, $addrarr, $pass); if ($ans['STATUS'] != 'ok') $err = $ans['ERROR']; -#$err = print_r($addrarr, true).$pass; } } From f2543ca22e8cbe7627739652c65c1cb60b281ac5 Mon Sep 17 00:00:00 2001 From: kanoi Date: Sun, 14 Dec 2014 08:28:55 +1100 Subject: [PATCH 20/21] ckdb - don't revalidate known addresses - it's slow --- src/ckdb.h | 3 ++- src/ckdb_cmd.c | 17 ++++++++++++++--- src/ckdb_data.c | 22 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/ckdb.h b/src/ckdb.h index eb86cebc..1beaf0eb 100644 --- a/src/ckdb.h +++ b/src/ckdb.h @@ -52,7 +52,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.9.6" -#define CKDB_VERSION DB_VERSION"-0.743" +#define CKDB_VERSION DB_VERSION"-0.744" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -1562,6 +1562,7 @@ extern void dsp_paymentaddresses(K_ITEM *item, FILE *stream); extern cmp_t cmp_paymentaddresses(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_paymentaddresses(int64_t userid, K_TREE_CTX *ctx); extern K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx); +extern K_ITEM *find_any_payaddress(char *payaddress); extern cmp_t cmp_payments(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_optioncontrol(K_ITEM *a, K_ITEM *b); extern K_ITEM *find_optioncontrol(char *optionname, tv_t *now); diff --git a/src/ckdb_cmd.c b/src/ckdb_cmd.c index cb02768a..369d7a73 100644 --- a/src/ckdb_cmd.c +++ b/src/ckdb_cmd.c @@ -162,12 +162,12 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, __maybe_unused tv_t *notcd, K_TREE *trf_root) { K_ITEM *i_username, *i_passwordhash, *i_rows, *i_address, *i_ratio; - K_ITEM *i_email, *u_item, *pa_item; + K_ITEM *i_email, *u_item, *pa_item, *old_pa_item; char *email, *address; char reply[1024] = ""; size_t siz = sizeof(reply); char tmp[1024]; - PAYMENTADDRESSES *row; + PAYMENTADDRESSES *row, *pa; K_STORE *pa_store = NULL; K_TREE_CTX ctx[1]; USERS *users; @@ -337,7 +337,18 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id, pa_item = pa_store->head; while (pa_item) { DATA_PAYMENTADDRESSES(row, pa_item); - if (!btc_valid_address(row->payaddress)) { + // Only EVER validate addresses once ... for now + old_pa_item = find_any_payaddress(row->payaddress); + if (old_pa_item) { + /* This test effectively means that + * two users can never add the same + * payout address */ + DATA_PAYMENTADDRESSES(pa, old_pa_item); + if (pa->userid != users->userid) { + reason = "Unavailable BTC address"; + goto struckout; + } + } else if (!btc_valid_address(row->payaddress)) { reason = "Invalid BTC address"; goto struckout; } diff --git a/src/ckdb_data.c b/src/ckdb_data.c index 8c17cd35..5730261b 100644 --- a/src/ckdb_data.c +++ b/src/ckdb_data.c @@ -1164,6 +1164,28 @@ K_ITEM *find_one_payaddress(int64_t userid, char *payaddress, K_TREE_CTX *ctx) return find_in_ktree(paymentaddresses_root, &look, cmp_paymentaddresses, ctx); } +/* This will match any user that has the payaddress + * This avoids the bitcoind delay of rechecking an address + * that has EVER been seen before + * However, also, cmd_userset() that uses it, effectively ensures + * that 2 standard users, that mine to a username rather than + * a bitcoin address, cannot ever use the same bitcoin address */ +K_ITEM *find_any_payaddress(char *payaddress) +{ + PAYMENTADDRESSES *pa; + K_TREE_CTX ctx[1]; + K_ITEM *item; + + item = first_in_ktree(paymentaddresses_root, ctx); + DATA_PAYMENTADDRESSES_NULL(pa, item); + while (item) { + if (strcmp(pa->payaddress, payaddress) == 0) + return item; + item = next_in_ktree(ctx); + } + return NULL; +} + // order by userid asc,paydate asc,payaddress asc,expirydate desc cmp_t cmp_payments(K_ITEM *a, K_ITEM *b) { From 81fa2972736505356acc90251c3ffa27f218f114 Mon Sep 17 00:00:00 2001 From: kanoi Date: Sun, 14 Dec 2014 08:31:12 +1100 Subject: [PATCH 21/21] php - add uncommitted css .hid --- pool/page.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pool/page.php b/pool/page.php index 032205f3..64b095eb 100644 --- a/pool/page.php +++ b/pool/page.php @@ -126,6 +126,7 @@ h1 {margin-top: 20px; float:middle; font-size: 20px;} .title {background-color: #909090;} .even {background-color: #cccccc;} .odd {background-color: #a8a8a8;} +.hid {display: none;} .dl {text-align: left; padding: 2px 8px;} .dr {text-align: right; padding: 2px 8px;} .dc {text-align: center; padding: 2px 8px;}