diff --git a/pool/page.php b/pool/page.php index b522e9ff..1939977d 100644 --- a/pool/page.php +++ b/pool/page.php @@ -110,6 +110,9 @@ h1 {margin-top: 20px; float:middle; font-size: 20px;} .dl {text-align: left; padding: 2px 8px;} .dr {text-align: right; padding: 2px 8px;} .dc {text-align: center; padding: 2px 8px;} +.dls {text-align: left; padding: 2px 8px; text-decoration:line-through; font-weight:lighter; } +.drs {text-align: right; padding: 2px 8px; text-decoration:line-through; font-weight:lighter; } +.dcs {text-align: center; padding: 2px 8px; text-decoration:line-through; font-weight:lighter; } \n"; $head .= ''; diff --git a/pool/page_blocks.php b/pool/page_blocks.php index f031a96b..8d682fee 100644 --- a/pool/page_blocks.php +++ b/pool/page_blocks.php @@ -12,7 +12,8 @@ function doblocks($data, $user) $pg .= "Height"; $pg .= "Who"; $pg .= "Reward"; - $pg .= "When"; + $pg .= "When"; + $pg .= "Status"; $pg .= "\n"; if ($ans['STATUS'] == 'ok') { @@ -24,11 +25,19 @@ function doblocks($data, $user) else $row = 'odd'; + $ex = ''; + $stat = $ans['status'.$i]; + if ($stat == 'Orphan') + $ex = 's'; + if ($stat == '1-Confirm') + $stat = 'Conf'; + $pg .= ""; - $pg .= ''.$ans['height'.$i].''; - $pg .= ''.$ans['workername'.$i].''; - $pg .= ''.btcfmt($ans['reward'.$i]).''; - $pg .= ''.gmdate('Y-m-d H:i:s+00', $ans['createdate'.$i]).''; + $pg .= "".$ans['height'.$i].''; + $pg .= "".$ans['workername'.$i].''; + $pg .= "".btcfmt($ans['reward'.$i]).''; + $pg .= "".gmdate('Y-m-d H:i:s+00', $ans['createdate'.$i]).''; + $pg .= "".$stat.''; $pg .= "\n"; } } diff --git a/pool/page_reg.php b/pool/page_reg.php index bddfec33..e523db6f 100644 --- a/pool/page_reg.php +++ b/pool/page_reg.php @@ -59,7 +59,7 @@ function safepass($pass) return false; # Invalid characters - $p2 = preg_replace('/[^ -~]/', '', $pass); + $p2 = preg_replace('/[\011]/', '', $pass); if ($p2 != $pass) return false; @@ -109,7 +109,7 @@ function show_reg($menu, $name, $u) { $ok = false; $data['error'] = "Password is unsafe - requires 6 or more characters, including
" . - "at least one of each uppercase, lowercase and digits"; + "at least one of each uppercase, lowercase and digits, but not Tab"; } elseif ($pass2 != $pass) { @@ -118,11 +118,11 @@ function show_reg($menu, $name, $u) } $orig = $user; - $user = preg_replace('/[_\\.]/', '', $orig); + $user = preg_replace('/[\._\/\011]/', '', $orig); if ($user != $orig) { $ok = false; - $data['error'] = "Username cannot include '.' or '_'"; + $data['error'] = "Username cannot include '.', '_', '/' or Tab"; $data['user'] = $user; } } diff --git a/pool/page_workers.php b/pool/page_workers.php index 8e305dfa..275b6567 100644 --- a/pool/page_workers.php +++ b/pool/page_workers.php @@ -19,6 +19,7 @@ function doworker($data, $user) $pg .= "\n"; if ($ans['STATUS'] == 'ok') { + $thr = 0; $count = $ans['rows']; for ($i = 0; $i < $count; $i++) { @@ -89,6 +90,7 @@ function doworker($data, $user) $uhr = '?GHs'; else { + $thr += $uhr; $uhr /= 10000000; if ($uhr < 0.01) $uhr = '0GHs'; @@ -104,6 +106,21 @@ function doworker($data, $user) $pg .= "\n"; } } + $thr /= 10000000; + if ($thr < 0.01) + $thr = '0GHs'; + else + { + if ($thr < 100000) + $thr = number_format(round($thr)/100,2).'GHs'; + else + $thr = number_format(round($thr/1000)/100,2).'THs'; + } + if (($i % 2) == 0) + $row = 'even'; + else + $row = 'odd'; + $pg .= "$thr\n"; $pg .= "\n"; return $pg; diff --git a/src/ckdb.c b/src/ckdb.c index f06c85b5..769577e1 100644 --- a/src/ckdb.c +++ b/src/ckdb.c @@ -47,7 +47,7 @@ #define DB_VLOCK "1" #define DB_VERSION "0.7" -#define CKDB_VERSION DB_VERSION"-0.102" +#define CKDB_VERSION DB_VERSION"-0.106" #define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL_HERE __FILE__, __func__, __LINE__ @@ -774,6 +774,7 @@ enum cmd_values { CMD_USERSTAT, CMD_BLOCK, CMD_BLOCKLIST, + CMD_BLOCKSTATUS, CMD_NEWID, CMD_PAYMENTS, CMD_WORKERS, @@ -1187,10 +1188,18 @@ typedef struct blocks { #define DATA_BLOCKS(_item) ((BLOCKS *)(_item->data)) #define BLOCKS_NEW 'n' +#define BLOCKS_NEW_STR "n" #define BLOCKS_CONFIRM '1' +#define BLOCKS_CONFIRM_STR "1" +#define BLOCKS_42 'F' +#define BLOCKS_42_STR "F" +#define BLOCKS_ORPHAN 'O' +#define BLOCKS_ORPHAN_STR "O" static const char *blocks_new = "New"; static const char *blocks_confirm = "1-Confirm"; +static const char *blocks_42 = "42-Confirm"; +static const char *blocks_orphan = "Orphan"; static const char *blocks_unknown = "?Unknown?"; #define KANO -27972 @@ -3640,6 +3649,7 @@ static void auto_age_older(PGconn *conn, int64_t workinfoid, char *poolinstance, char *by, char *code, char *inet, tv_t *cd) { static int64_t last_attempted_id = -1; + static int64_t prev_found = 0; static int repeat; char min_buf[DATE_BUFSIZ], max_buf[DATE_BUFSIZ]; @@ -3648,37 +3658,51 @@ static void auto_age_older(PGconn *conn, int64_t workinfoid, char *poolinstance, tv_t ss_first_min, ss_last_max; tv_t ss_first, ss_last; int32_t wid_count; + SHARESUMMARY sharesummary; K_TREE_CTX ctx[1]; - K_ITEM *ss_item; + K_ITEM look, *ss_item; int64_t age_id, do_id, to_id; - bool ok; + bool ok, found; + + LOGDEBUG("%s(): workinfoid=%"PRId64" prev=%"PRId64, __func__, workinfoid, prev_found); - LOGDEBUG("%s(): %"PRId64, __func__, workinfoid); + age_id = prev_found; + + // Find the oldest 'unaged' sharesummary < workinfoid and >= prev_found + sharesummary.workinfoid = prev_found; + sharesummary.userid = -1; + sharesummary.workername[0] = '\0'; + look.data = (void *)(&sharesummary); + ss_item = find_after_in_ktree(sharesummary_workinfoid_root, &look, + cmp_sharesummary_workinfoid, ctx); ss_first_min.tv_sec = ss_first_min.tv_usec = ss_last_max.tv_sec = ss_last_max.tv_usec = 0; ss_count_tot = s_count_tot = s_diff_tot = 0; - age_id = 0; - // Find the oldest 'unaged' sharesummary < workinfoid - ss_item = first_in_ktree(sharesummary_workinfoid_root, ctx); + found = false; while (ss_item && DATA_SHARESUMMARY(ss_item)->workinfoid < workinfoid) { if (DATA_SHARESUMMARY(ss_item)->complete[0] == SUMMARY_NEW) { age_id = DATA_SHARESUMMARY(ss_item)->workinfoid; + prev_found = age_id; + found = true; break; } ss_item = next_in_ktree(ctx); } - LOGDEBUG("%s(): age_id=%"PRId64, __func__, age_id); - /* Process all the consecutive sharesummaries that's aren't aged - * This way we find each oldest 'batch' of sharesummaries that have - * been missed and can report the range of data that was aged, - * which would normally just be an approx 10min set of workinfoids - * from the last time ckpool stopped - * Each next group of unaged sharesummaries following this, will be - * picked up by each next aging */ - if (age_id) { + LOGDEBUG("%s(): age_id=%"PRId64" found=%d", __func__, age_id, found); + // Don't repeat searching old items to avoid accessing their ram + if (!found) + prev_found = workinfoid; + else { + /* Process all the consecutive sharesummaries that's aren't aged + * This way we find each oldest 'batch' of sharesummaries that have + * been missed and can report the range of data that was aged, + * which would normally just be an approx 10min set of workinfoids + * from the last time ckpool stopped + * Each next group of unaged sharesummaries following this, will be + * picked up by each next aging */ wid_count = 0; do_id = age_id; to_id = 0; @@ -4851,11 +4875,14 @@ static const char *blocks_confirmed(char *confirmed) return blocks_new; case BLOCKS_CONFIRM: return blocks_confirm; + case BLOCKS_42: + return blocks_42; + case BLOCKS_ORPHAN: + return blocks_orphan; } return blocks_unknown; } -// TODO: determine how to handle orphan blocks after 1 confirm static bool blocks_add(PGconn *conn, char *height, char *blockhash, char *confirmed, char *workinfoid, char *username, char *workername, char *clientid, char *enonce1, @@ -4875,6 +4902,7 @@ static bool blocks_add(PGconn *conn, char *height, char *blockhash, char *params[11 + HISTORYDATECOUNT]; bool ok = false, update_old = false; int par = 0; + char want = '?'; int n; LOGDEBUG("%s(): add", __func__); @@ -4969,32 +4997,46 @@ static bool blocks_add(PGconn *conn, char *height, char *blockhash, goto unparam; } break; + case BLOCKS_ORPHAN: + case BLOCKS_42: + // These shouldn't be possible until startup completes + if (!startup_complete) { + K_WUNLOCK(blocks_free); + tv_to_buf(cd, cd_buf, sizeof(cd_buf)); + LOGERR("%s(): Status: %s invalid during startup. " + "Ignored: Block: %s/...%s/%s", + __func__, + blocks_confirmed(confirmed), + height, blk_dsp, cd_buf); + goto flail; + } + want = BLOCKS_CONFIRM; case BLOCKS_CONFIRM: if (!old_b_item) { - k_add_head(blocks_free, b_item); K_WUNLOCK(blocks_free); tv_to_buf(cd, cd_buf, sizeof(cd_buf)); - LOGERR("%s(): Can't confirm a non-existent block, Status: " - "%s, Block: %s/...%s/%s", + LOGERR("%s(): Can't %s a non-existent Block: %s/...%s/%s", __func__, blocks_confirmed(confirmed), height, blk_dsp, cd_buf); - return false; + goto flail; } - /* This will also treat an unrecognised 'confirmed' as a - * duplicate since they shouldn't exist anyway */ - if (DATA_BLOCKS(old_b_item)->confirmed[0] != BLOCKS_NEW) { + if (confirmed[0] == BLOCKS_CONFIRM) + want = BLOCKS_NEW; + if (DATA_BLOCKS(old_b_item)->confirmed[0] != want) { k_add_head(blocks_free, b_item); K_WUNLOCK(blocks_free); - if (!igndup || DATA_BLOCKS(old_b_item)->confirmed[0] != BLOCKS_CONFIRM) { + // No mismatch messages during startup + if (!startup_complete) { tv_to_buf(cd, cd_buf, sizeof(cd_buf)); - LOGERR("%s(): Duplicate (%s) blocks ignored, Status: " - "%s, Block: %s/...%s/%s", + LOGERR("%s(): Request Status: %s requires Status: %s. " + "Ignored: Status: %s, Block: %s/...%s/%s", __func__, - blocks_confirmed(DATA_BLOCKS(old_b_item)->confirmed), blocks_confirmed(confirmed), + blocks_confirmed(BLOCKS_CONFIRM_STR), + blocks_confirmed(DATA_BLOCKS(old_b_item)->confirmed), height, blk_dsp, cd_buf); } - return true; + goto flail; } K_WUNLOCK(blocks_free); @@ -5102,44 +5144,55 @@ flail: K_WUNLOCK(blocks_free); if (ok) { + char pct[16] = "?"; + char est[16] = ""; + K_ITEM *w_item; char tmp[256]; + bool blk; - if (confirmed[0] != BLOCKS_NEW) - tmp[0] = '\0'; - else { - char pct[16] = "?"; - char est[16] = ""; - K_ITEM *w_item; - - w_item = find_workinfo(DATA_BLOCKS(b_item)->workinfoid); - if (w_item) { - char wdiffbin[TXT_SML+1]; - double wdiff; - hex2bin(wdiffbin, DATA_WORKINFO(w_item)->bits, 4); - wdiff = diff_from_nbits(wdiffbin); - if (wdiff > 0.0) { - snprintf(pct, sizeof(pct), "%.2f", - 100.0 * pool.diffacc / wdiff); + switch (confirmed[0]) { + case BLOCKS_NEW: + blk = true; + tmp[0] = '\0'; + break; + case BLOCKS_CONFIRM: + blk = true; + w_item = find_workinfo(DATA_BLOCKS(b_item)->workinfoid); + if (w_item) { + char wdiffbin[TXT_SML+1]; + double wdiff; + hex2bin(wdiffbin, DATA_WORKINFO(w_item)->bits, 4); + wdiff = diff_from_nbits(wdiffbin); + if (wdiff > 0.0) { + snprintf(pct, sizeof(pct), "%.2f", + 100.0 * pool.diffacc / wdiff); + } } - } - if (pool.diffacc >= 1000000.0) { - suffix_string(pool.diffacc, est, sizeof(est)-1, 1); - strcat(est, " "); - } - tv_to_buf(&(DATA_BLOCKS(b_item)->createdate), cd_buf, sizeof(cd_buf)); - snprintf(tmp, sizeof(tmp), - " Reward: %f, User: %s, Worker: %s, ShareEst: %.1f %s%s%% UTC:%s", - BTC_TO_D(DATA_BLOCKS(b_item)->reward), - username, workername, pool.diffacc, est, pct, cd_buf); - if (pool.workinfoid < DATA_BLOCKS(b_item)->workinfoid) { - pool.workinfoid = DATA_BLOCKS(b_item)->workinfoid; - pool.diffacc = pool.differr = - pool.best_sdiff = 0.0; - } + if (pool.diffacc >= 1000000.0) { + suffix_string(pool.diffacc, est, sizeof(est)-1, 1); + strcat(est, " "); + } + tv_to_buf(&(DATA_BLOCKS(b_item)->createdate), cd_buf, sizeof(cd_buf)); + snprintf(tmp, sizeof(tmp), + " Reward: %f, User: %s, Worker: %s, ShareEst: %.1f %s%s%% UTC:%s", + BTC_TO_D(DATA_BLOCKS(b_item)->reward), + username, workername, pool.diffacc, est, pct, cd_buf); + if (pool.workinfoid < DATA_BLOCKS(b_item)->workinfoid) { + pool.workinfoid = DATA_BLOCKS(b_item)->workinfoid; + pool.diffacc = pool.differr = + pool.best_sdiff = 0.0; + } + break; + case BLOCKS_ORPHAN: + case BLOCKS_42: + default: + blk = false; + tmp[0] = '\0'; + break; } - LOGWARNING("%s(): BLOCK! Status: %s, Block: %s/...%s%s", - __func__, + LOGWARNING("%s(): %sStatus: %s, Block: %s/...%s%s", + __func__, blk ? "BLOCK! " : "", blocks_confirmed(confirmed), height, blk_dsp, tmp); } @@ -7220,20 +7273,18 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, char *buf; size_t len, off; int rows; - int32_t height; LOGDEBUG("%s(): cmd '%s'", __func__, cmd); - b_item = last_in_ktree(blocks_root, ctx); APPEND_REALLOC_INIT(buf, off, len); APPEND_REALLOC(buf, off, len, "ok."); rows = 0; - height = -1; + K_RLOCK(blocks_free); + b_item = last_in_ktree(blocks_root, ctx); while (b_item && rows < 42) { - if (height != DATA_BLOCKS(b_item)->height) { - height = DATA_BLOCKS(b_item)->height; - - snprintf(tmp, sizeof(tmp), "height%d=%d%c", rows, height, FLDSEP); + if (CURRENT(&(DATA_BLOCKS(b_item)->expirydate))) { + int_to_buf(DATA_BLOCKS(b_item)->height, reply, sizeof(reply)); + snprintf(tmp, sizeof(tmp), "height%d=%s%c", rows, reply, FLDSEP); APPEND_REALLOC(buf, off, len, tmp); str_to_buf(DATA_BLOCKS(b_item)->blockhash, reply, sizeof(reply)); @@ -7266,6 +7317,7 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, } b_item = prev_in_ktree(ctx); } + K_RUNLOCK(blocks_free); snprintf(tmp, sizeof(tmp), "rows=%d", rows); APPEND_REALLOC(buf, off, len, tmp); @@ -7273,6 +7325,90 @@ static char *cmd_blocklist(__maybe_unused PGconn *conn, char *cmd, char *id, return buf; } +static char *cmd_blockstatus(__maybe_unused PGconn *conn, char *cmd, char *id, + tv_t *now, char *by, char *code, char *inet, + __maybe_unused tv_t *cd, K_TREE *trf_root) +{ + K_ITEM *i_height, *i_blockhash, *i_action; + char reply[1024] = ""; + size_t siz = sizeof(reply); + K_ITEM *b_item; + BLOCKS *blocks; + int32_t height; + char *action; + bool ok = false; + + LOGDEBUG("%s(): cmd '%s'", __func__, cmd); + + i_height = require_name(trf_root, "height", 1, NULL, reply, siz); + if (!i_height) + return strdup(reply); + + TXT_TO_INT("height", DATA_TRANSFER(i_height)->data, height); + + i_blockhash = require_name(trf_root, "blockhash", 1, NULL, reply, siz); + if (!i_blockhash) + return strdup(reply); + + i_action = require_name(trf_root, "action", 1, NULL, reply, siz); + if (!i_action) + return strdup(reply); + + action = DATA_TRANSFER(i_action)->data; + + K_RLOCK(blocks_free); + b_item = find_blocks(height, DATA_TRANSFER(i_blockhash)->data); + K_RUNLOCK(blocks_free); + + if (!b_item) { + snprintf(reply, siz, "ERR.unknown block"); + LOGERR("%s.%s", id, reply); + return strdup(reply); + } + + blocks = DATA_BLOCKS(b_item); + + if (strcasecmp(action, "orphan") == 0) { + switch (blocks->confirmed[0]) { + case BLOCKS_NEW: + case BLOCKS_CONFIRM: + ok = blocks_add(conn, DATA_TRANSFER(i_height)->data, + blocks->blockhash, + BLOCKS_ORPHAN_STR, + EMPTY, EMPTY, EMPTY, EMPTY, + EMPTY, EMPTY, EMPTY, EMPTY, + by, code, inet, now, false, id, + trf_root); + if (!ok) { + snprintf(reply, siz, + "DBE.action '%s'", + action); + LOGERR("%s.%s", id, reply); + return strdup(reply); + } + // TODO: reset the share counter? + break; + default: + snprintf(reply, siz, + "ERR.invalid action '%.*s%s' for block state '%s'", + CMD_SIZ, action, + (strlen(action) > CMD_SIZ) ? "..." : "", + blocks_confirmed(blocks->confirmed)); + LOGERR("%s.%s", id, reply); + return strdup(reply); + } + } else { + snprintf(reply, siz, "ERR.unknown action '%s'", + DATA_TRANSFER(i_action)->data); + LOGERR("%s.%s", id, reply); + return strdup(reply); + } + + snprintf(reply, siz, "ok.%s %d", DATA_TRANSFER(i_action)->data, height); + LOGDEBUG("%s.%s", id, reply); + return strdup(reply); +} + static char *cmd_newid(PGconn *conn, char *cmd, char *id, tv_t *now, char *by, char *code, char *inet, __maybe_unused tv_t *cd, K_TREE *trf_root) @@ -8835,6 +8971,7 @@ static struct CMDS { { CMD_USERSTAT, "userstats", false, true, cmd_userstats, ACCESS_POOL }, { CMD_BLOCK, "block", false, true, cmd_blocks, ACCESS_POOL }, { CMD_BLOCKLIST,"blocklist", false, false, cmd_blocklist, ACCESS_WEB }, + { CMD_BLOCKSTATUS,"blockstatus",false, false, cmd_blockstatus,ACCESS_WEB }, { CMD_NEWID, "newid", false, false, cmd_newid, ACCESS_SYSTEM }, { CMD_PAYMENTS, "payments", false, false, cmd_payments, ACCESS_WEB }, { CMD_WORKERS, "workers", false, false, cmd_workers, ACCESS_WEB }, @@ -9581,6 +9718,7 @@ static void *socketer(__maybe_unused void *arg) case CMD_PAYMENTS: case CMD_PPLNS: case CMD_DSP: + case CMD_BLOCKSTATUS: if (!startup_complete) { snprintf(reply, sizeof(reply), "%s.%ld.loading.%s", @@ -9685,8 +9823,11 @@ static void *socketer(__maybe_unused void *arg) } K_WLOCK(transfer_free); k_list_transfer_to_head(trf_store, transfer_free); - K_WUNLOCK(transfer_free); trf_store = k_free_store(trf_store); + if (transfer_free->count == transfer_free->total && + transfer_free->total > ALLOC_TRANSFER * 64) + k_cull_list(transfer_free); + K_WUNLOCK(transfer_free); } } @@ -9750,6 +9891,7 @@ static bool reload_line(PGconn *conn, char *filename, uint64_t count, char *buf) case CMD_NEWPASS: case CMD_CHKPASS: case CMD_BLOCKLIST: + case CMD_BLOCKSTATUS: case CMD_NEWID: case CMD_PAYMENTS: case CMD_WORKERS: @@ -10035,7 +10177,8 @@ static void *listener(void *arg) startup_complete = true; } - conn = dbconnect(); + if (!everyone_die) + conn = dbconnect(); // Process queued work while (!everyone_die) { @@ -10060,7 +10203,8 @@ static void *listener(void *arg) } } - PQfinish(conn); + if (conn) + PQfinish(conn); return NULL; } @@ -10601,6 +10745,7 @@ static struct option long_options[] = { { "name", required_argument, 0, 'n' }, { "dbpass", required_argument, 0, 'p' }, { "ckpool-logdir", required_argument, 0, 'r' }, + { "logdir", required_argument, 0, 'R' }, { "sockdir", required_argument, 0, 's' }, { "dbuser", required_argument, 0, 'u' }, { "version", no_argument, 0, 'v' }, @@ -10634,7 +10779,7 @@ int main(int argc, char **argv) memset(&ckp, 0, sizeof(ckp)); ckp.loglevel = LOG_NOTICE; - while ((c = getopt_long(argc, argv, "c:d:hkl:n:p:r:s:u:vyY:", long_options, &i)) != -1) { + while ((c = getopt_long(argc, argv, "c:d:hkl:n:p:r:R:s:u:vyY:", long_options, &i)) != -1) { switch(c) { case 'c': ckp.config = strdup(optarg); @@ -10686,6 +10831,9 @@ int main(int argc, char **argv) case 'r': restorefrom = strdup(optarg); break; + case 'R': + ckp.logdir = strdup(optarg); + break; case 's': ckp.socket_dir = strdup(optarg); break; diff --git a/src/klist.c b/src/klist.c index adca979e..aef01127 100644 --- a/src/klist.c +++ b/src/klist.c @@ -378,3 +378,36 @@ K_STORE *_k_free_store(K_STORE *store, KLIST_FFL_ARGS) return NULL; } + +// Must be locked and none in use and/or unlinked +void _k_cull_list(K_LIST *list, KLIST_FFL_ARGS) +{ + int i; + + if (list->is_store) { + quithere(1, "List %s can't %s() a store" KLIST_FFL, + list->name, __func__, KLIST_FFL_PASS); + } + + if (list->count != list->total) { + quithere(1, "List %s can't %s() a list in use" KLIST_FFL, + list->name, __func__, KLIST_FFL_PASS); + } + + for (i = 0; i < list->item_mem_count; i++) + free(list->item_memory[i]); + free(list->item_memory); + list->item_memory = NULL; + list->item_mem_count = 0; + + for (i = 0; i < list->data_mem_count; i++) + free(list->data_memory[i]); + free(list->data_memory); + list->data_memory = NULL; + list->data_mem_count = 0; + + list->total = list->count = list->count_up = 0; + list->head = list->tail = NULL; + + k_alloc_items(list, KLIST_FFL_PASS); +} diff --git a/src/klist.h b/src/klist.h index 4621b487..51f63fd6 100644 --- a/src/klist.h +++ b/src/klist.h @@ -94,5 +94,7 @@ extern K_LIST *_k_free_list(K_LIST *list, KLIST_FFL_ARGS); #define k_free_list(_list) _k_free_list(_list, KLIST_FFL_HERE) extern K_STORE *_k_free_store(K_STORE *store, KLIST_FFL_ARGS); #define k_free_store(_store) _k_free_store(_store, KLIST_FFL_HERE) +extern void _k_cull_list(K_LIST *list, KLIST_FFL_ARGS); +#define k_cull_list(_list) _k_cull_list(_list, KLIST_FFL_HERE) #endif