Browse Source

ckdb - add triggers for updating global varaibles from optioncontrol

master
kanoi 9 years ago
parent
commit
224de3c5fe
  1. 43
      src/ckdb.c
  2. 16
      src/ckdb.h
  3. 16
      src/ckdb_data.c
  4. 163
      src/ckdb_dbio.c

43
src/ckdb.c

@ -492,33 +492,40 @@ K_TREE *events_ipc_root;
K_TREE *events_hash_root; K_TREE *events_hash_root;
K_LIST *events_free; K_LIST *events_free;
K_STORE *events_store; K_STORE *events_store;
// Emulate a list for lock checking
K_LIST *event_limits_free;
/* N.B. these limits are not production quality
* They'll block anyone who makes a mistake 2 or 3 times :)
* Use optioncontrol OC_LIMITS to set/store them in the database */
EVENT_LIMITS e_limits[] = { EVENT_LIMITS e_limits[] = {
{ EVENTID_PASSFAIL, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_PASSFAIL, "PASSFAIL", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// It's only possible to create an address account once, so user_lo/hi can never trigger // It's only possible to create an address account once, so user_lo/hi can never trigger
{ EVENTID_CREADDR, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_CREADDR, "CREADDR", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// It's only possible to create an account once, so user_lo/hi can never trigger // It's only possible to create an account once, so user_lo/hi can never trigger
{ EVENTID_CREACC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_CREACC, "CREACC", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// page_api.php with an invalid username // page_api.php with an invalid username
{ EVENTID_UNKATTS, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_UNKATTS, "UNKATTS", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// 2fa missing/invalid format // 2fa missing/invalid format
{ EVENTID_INV2FA, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_INV2FA, "INV2FA", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Wrong 2fa value // Wrong 2fa value
{ EVENTID_WRONG2FA, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_WRONG2FA, "WRONG2FA", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Invalid address according to btcd // Invalid address according to btcd
{ EVENTID_INVBTC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_INVBTC, "INVBTC", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Incorrect format/length address // Incorrect format/length address
{ EVENTID_INCBTC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_INCBTC, "INCBTC", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Address belongs to some other account // Address belongs to some other account
{ EVENTID_BTCUSED, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_BTCUSED, "BTCUSED", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// It's only possible to create an account once, so user_lo/hi can never trigger // It's only possible to create an account once, so user_lo/hi can never trigger
{ EVENTID_AUTOACC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_AUTOACC, "AUTOACC", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Invalid user on auth, CKPool will throttle these // Invalid user on auth, CKPool will throttle these
{ EVENTID_INVAUTH, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }, { EVENTID_INVAUTH, "INVAUTH", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Invalid user on chkpass // Invalid user on chkpass
{ EVENTID_INVUSER, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 } { EVENTID_INVUSER, "INVUSER", 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Terminated by NULL name
{ -1, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
}; };
// All access to above and below limits requires the event_limits_free lock
int event_limits_hash_lifetime = 24*60*60; int event_limits_hash_lifetime = 24*60*60;
// AUTHS authorise.id.json={...} // AUTHS authorise.id.json={...}
@ -1300,7 +1307,7 @@ static void alloc_storage()
DLPRIO(userinfo, 50); DLPRIO(userinfo, 50);
// Needs to check users and ips // Needs to check users and ips and uses limits
DLPRIO(events, 47); DLPRIO(events, 47);
DLPRIO(auths, 44); DLPRIO(auths, 44);
@ -1320,10 +1327,12 @@ static void alloc_storage()
DLPRIO(poolstats, 11); DLPRIO(poolstats, 11);
DLPRIO(userstats, 10); DLPRIO(userstats, 10);
// Uses limits lock for events_limits
DLPRIO(optioncontrol, 5);
// Don't currently nest any locks in these: // Don't currently nest any locks in these:
DLPRIO(workers, PRIO_TERMINAL); DLPRIO(workers, PRIO_TERMINAL);
DLPRIO(idcontrol, PRIO_TERMINAL); DLPRIO(idcontrol, PRIO_TERMINAL);
DLPRIO(optioncontrol, PRIO_TERMINAL);
DLPRIO(paymentaddresses, PRIO_TERMINAL); DLPRIO(paymentaddresses, PRIO_TERMINAL);
DLPRIO(ips, PRIO_TERMINAL); DLPRIO(ips, PRIO_TERMINAL);
@ -4274,7 +4283,7 @@ static void *socketer(__maybe_unused void *arg)
"%s.%ld.failed.ERR", "%s.%ld.failed.ERR",
msgline->id, msgline->id,
now.tv_sec); now.tv_sec);
tmp = reply_event(EVENTID_MAX, reply); tmp = reply_event(EVENTID_NONE, reply);
send_unix_msg(sockd, tmp); send_unix_msg(sockd, tmp);
FREENULL(tmp); FREENULL(tmp);
break; break;
@ -6043,10 +6052,12 @@ int main(int argc, char **argv)
// Emulate a list for lock checking // Emulate a list for lock checking
process_pplns_free = k_lock_only_list("ProcessPPLNS"); process_pplns_free = k_lock_only_list("ProcessPPLNS");
workers_db_free = k_lock_only_list("WorkersDB"); workers_db_free = k_lock_only_list("WorkersDB");
event_limits_free = k_lock_only_list("EventLimits");
#if LOCK_CHECK #if LOCK_CHECK
DLPRIO(process_pplns, 99); DLPRIO(process_pplns, 99);
DLPRIO(workers_db, 98); DLPRIO(workers_db, 98);
DLPRIO(event_limits, PRIO_TERMINAL);
#endif #endif
if (confirm_sharesummary) { if (confirm_sharesummary) {

16
src/ckdb.h

@ -51,7 +51,7 @@
#define DB_VLOCK "1" #define DB_VLOCK "1"
#define DB_VERSION "1.0.4" #define DB_VERSION "1.0.4"
#define CKDB_VERSION DB_VERSION"-1.940" #define CKDB_VERSION DB_VERSION"-1.950"
#define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__ #define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -1599,6 +1599,12 @@ extern K_TREE *optioncontrol_root;
extern K_LIST *optioncontrol_free; extern K_LIST *optioncontrol_free;
extern K_STORE *optioncontrol_store; extern K_STORE *optioncontrol_store;
typedef struct oc_trigger {
char *match;
bool exact;
void (*func)(OPTIONCONTROL *, const char *);
} OC_TRIGGER;
// TODO: discarding workinfo,shares // TODO: discarding workinfo,shares
// WORKINFO workinfo.id.json={...} // WORKINFO workinfo.id.json={...}
typedef struct workinfo { typedef struct workinfo {
@ -1997,6 +2003,10 @@ extern K_TREE *events_ipc_root;
extern K_TREE *events_hash_root; extern K_TREE *events_hash_root;
extern K_LIST *events_free; extern K_LIST *events_free;
extern K_STORE *events_store; extern K_STORE *events_store;
// Emulate a list for lock checking
extern K_LIST *event_limits_free;
#define OC_LIMITS "event_limits_"
// Any password failure // Any password failure
#define EVENTID_PASSFAIL 0 #define EVENTID_PASSFAIL 0
@ -2022,7 +2032,7 @@ extern K_STORE *events_store;
#define EVENTID_INVAUTH 10 #define EVENTID_INVAUTH 10
// Unknown chkpass username // Unknown chkpass username
#define EVENTID_INVUSER 11 #define EVENTID_INVUSER 11
#define EVENTID_MAX 11 #define EVENTID_NONE 12
#define EVENT_OK -1 #define EVENT_OK -1
@ -2032,6 +2042,8 @@ extern K_STORE *events_store;
* i.e. a single user will not fail the test result for ip */ * i.e. a single user will not fail the test result for ip */
typedef struct event_limits { typedef struct event_limits {
int id; int id;
// optioncontrol/display name
char *name;
int user_low_time; int user_low_time;
// how many in above limit = ok (+1 = alert) // how many in above limit = ok (+1 = alert)
int user_low_time_limit; int user_low_time_limit;

16
src/ckdb_data.c

@ -4509,6 +4509,7 @@ int check_events(EVENTS *events)
EVENTS *e = NULL; EVENTS *e = NULL;
int count, secs; int count, secs;
int tyme, limit, lifetime; int tyme, limit, lifetime;
char name[TXT_SML+1];
pid_t pid; pid_t pid;
tv_t now; tv_t now;
@ -4527,6 +4528,7 @@ int check_events(EVENTS *events)
// All tests below always run all full checks to clean up old events // All tests below always run all full checks to clean up old events
setnow(&now); setnow(&now);
K_WLOCK(events_free); K_WLOCK(events_free);
K_RLOCK(event_limits_free);
// Check hash - same hash passfail on more than one valid User // Check hash - same hash passfail on more than one valid User
if (events->id == EVENTID_PASSFAIL && *(events->hash)) { if (events->id == EVENTID_PASSFAIL && *(events->hash)) {
e_item = last_events_hash(events->id, events->hash, ctx); e_item = last_events_hash(events->id, events->hash, ctx);
@ -4573,6 +4575,7 @@ int check_events(EVENTS *events)
tyme = 0; tyme = 0;
limit = 1; limit = 1;
lifetime = event_limits_hash_lifetime; lifetime = event_limits_hash_lifetime;
STRNCPY(name, "HASH");
} }
} }
} }
@ -4613,6 +4616,7 @@ int check_events(EVENTS *events)
tyme = e_limits[events->id].user_low_time; tyme = e_limits[events->id].user_low_time;
limit = e_limits[events->id].user_low_time_limit; limit = e_limits[events->id].user_low_time_limit;
lifetime = e_limits[events->id].lifetime; lifetime = e_limits[events->id].lifetime;
STRNCPY(name, e_limits[events->id].name);
} }
if (alert == false && if (alert == false &&
secs <= e_limits[events->id].user_hi_time && secs <= e_limits[events->id].user_hi_time &&
@ -4622,6 +4626,7 @@ int check_events(EVENTS *events)
tyme = e_limits[events->id].user_hi_time; tyme = e_limits[events->id].user_hi_time;
limit = e_limits[events->id].user_hi_time_limit; limit = e_limits[events->id].user_hi_time_limit;
lifetime = e_limits[events->id].lifetime; lifetime = e_limits[events->id].lifetime;
STRNCPY(name, e_limits[events->id].name);
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
} }
@ -4664,6 +4669,7 @@ int check_events(EVENTS *events)
tyme = e_limits[events->id].ip_low_time; tyme = e_limits[events->id].ip_low_time;
limit = e_limits[events->id].ip_low_time_limit; limit = e_limits[events->id].ip_low_time_limit;
lifetime = e_limits[events->id].lifetime; lifetime = e_limits[events->id].lifetime;
STRNCPY(name, e_limits[events->id].name);
} }
if (alert == false && if (alert == false &&
secs <= e_limits[events->id].ip_hi_time && secs <= e_limits[events->id].ip_hi_time &&
@ -4673,6 +4679,7 @@ int check_events(EVENTS *events)
tyme = e_limits[events->id].ip_hi_time; tyme = e_limits[events->id].ip_hi_time;
limit = e_limits[events->id].ip_hi_time_limit; limit = e_limits[events->id].ip_hi_time_limit;
lifetime = e_limits[events->id].lifetime; lifetime = e_limits[events->id].lifetime;
STRNCPY(name, e_limits[events->id].name);
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
} }
@ -4719,6 +4726,7 @@ int check_events(EVENTS *events)
tyme = e_limits[events->id].ip_low_time; tyme = e_limits[events->id].ip_low_time;
limit = e_limits[events->id].ip_low_time_limit; limit = e_limits[events->id].ip_low_time_limit;
lifetime = e_limits[events->id].lifetime; lifetime = e_limits[events->id].lifetime;
STRNCPY(name, e_limits[events->id].name);
} }
if (alert == false && if (alert == false &&
secs <= e_limits[events->id].ip_hi_time && secs <= e_limits[events->id].ip_hi_time &&
@ -4728,6 +4736,7 @@ int check_events(EVENTS *events)
tyme = e_limits[events->id].ip_hi_time; tyme = e_limits[events->id].ip_hi_time;
limit = e_limits[events->id].ip_hi_time_limit; limit = e_limits[events->id].ip_hi_time_limit;
lifetime = e_limits[events->id].lifetime; lifetime = e_limits[events->id].lifetime;
STRNCPY(name, e_limits[events->id].name);
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
} }
@ -4737,11 +4746,12 @@ int check_events(EVENTS *events)
user2 == false) user2 == false)
alert = false; alert = false;
} }
K_RUNLOCK(event_limits_free);
K_WUNLOCK(events_free); K_WUNLOCK(events_free);
if (alert) { if (alert) {
LOGERR("%s() ALERT ID:%d Lim:%d Time:%d Life:%d %s '%s' '%s'", LOGERR("%s() ALERT ID:%d %s Lim:%d Time:%d Life:%d %s '%s' '%s'",
__func__, __func__,
events->id, limit, tyme, lifetime, events->id, name, limit, tyme, lifetime,
events->createinet, events->createinet,
st = safe_text_nonull(events->createby), st = safe_text_nonull(events->createby),
cause_str(cause)); cause_str(cause));
@ -4760,7 +4770,7 @@ int check_events(EVENTS *events)
snprintf(buf3, sizeof(buf3), "%d", tyme); snprintf(buf3, sizeof(buf3), "%d", tyme);
snprintf(buf4, sizeof(buf4), "%d", lifetime); snprintf(buf4, sizeof(buf4), "%d", lifetime);
st = safe_text_nonull(events->createby); st = safe_text_nonull(events->createby);
execl(ckdb_alert_cmd, ckdb_alert_cmd, buf1, execl(ckdb_alert_cmd, ckdb_alert_cmd, buf1, name,
buf2, buf3, buf4, events->createinet, buf2, buf3, buf4, events->createinet,
st, cause_str(cause), NULL); st, cause_str(cause), NULL);
LOGERR("%s() ALERT fork failed to execute (%d)", LOGERR("%s() ALERT fork failed to execute (%d)",

163
src/ckdb_dbio.c

@ -2438,6 +2438,137 @@ foil:
return ok; return ok;
} }
void oc_switch_state(OPTIONCONTROL *oc, const char *from)
{
switch_state = atoi(oc->optionvalue);
LOGWARNING("%s(%s) set switch_state to %d",
from, __func__, switch_state);
}
void oc_diff_percent(OPTIONCONTROL *oc, __maybe_unused const char *from)
{
diff_percent = DIFF_VAL(strtod(oc->optionvalue, NULL));
if (errno == ERANGE)
diff_percent = DIFF_VAL(DIFF_PERCENT_DEFAULT);
}
/* An event limit setting looks like:
* OC_LIMITS + events_limits.name + '_' + Item
* Item is one of the field names in event_limits
* e.g. user_low_time, user_low_time_limit etc
* as below in the if tests */
void oc_event_limits(OPTIONCONTROL *oc, const char *from)
{
bool processed = false;
size_t len;
char *ptr, *ptr2;
int val, i;
K_WLOCK(event_limits_free);
val = atoi(oc->optionvalue);
ptr = oc->optionname + strlen(OC_LIMITS);
i = -1;
while (e_limits[++i].name) {
len = strlen(e_limits[i].name);
if (strncmp(ptr, e_limits[i].name, len) == 0 &&
ptr[len] == '_') {
ptr2 = ptr + len + 1;
if (strcmp(ptr2, "user_low_time") == 0) {
e_limits[i].user_low_time = val;
} else if (strcmp(ptr2, "user_low_time_limit") == 0) {
e_limits[i].user_low_time_limit = val;
} else if (strcmp(ptr2, "user_hi_time") == 0) {
e_limits[i].user_hi_time = val;
} else if (strcmp(ptr2, "user_hi_time_limit") == 0) {
e_limits[i].user_hi_time_limit = val;
} else if (strcmp(ptr2, "ip_low_time") == 0) {
e_limits[i].ip_low_time = val;
} else if (strcmp(ptr2, "ip_low_time_limit") == 0) {
e_limits[i].ip_low_time_limit = val;
} else if (strcmp(ptr2, "ip_hi_time") == 0) {
e_limits[i].ip_hi_time = val;
} else if (strcmp(ptr2, "ip_hi_time_limit") == 0) {
e_limits[i].ip_hi_time_limit = val;
} else if (strcmp(ptr2, "lifetime") == 0) {
e_limits[i].lifetime = val;
} else {
LOGERR("%s(%s): ERR: Unknown %s item '%s' "
"in '%s'",
from, __func__, OC_LIMITS, ptr2,
oc->optionname);
}
processed = true;
break;
}
}
if (!processed) {
if (strcmp(ptr, "hash_lifetime") == 0) {
event_limits_hash_lifetime = val;
processed = true;
}
}
K_WUNLOCK(event_limits_free);
if (!processed) {
LOGERR("%s(%s): ERR: Unknown %s name '%s'",
from, __func__, OC_LIMITS, oc->optionname);
}
}
OC_TRIGGER oc_trigger[] = {
{ SWITCH_STATE_NAME, true, oc_switch_state },
{ DIFF_PERCENT_NAME, true, oc_diff_percent },
{ OC_LIMITS, false, oc_event_limits },
{ NULL, 0, NULL }
};
/* For oc items that aren't date/height controlled, and use global variables
* rather than having to look up the value every time it's needed
* Called from within the write lock that loaded/added the oc_item */
static void optioncontrol_trigger(K_ITEM *oc_item, const char *from)
{
char cd_buf[DATE_BUFSIZ], cd2_buf[DATE_BUFSIZ];
OPTIONCONTROL *oc;
int got, i;
DATA_OPTIONCONTROL(oc, oc_item);
if (CURRENT(&(oc->expirydate))) {
got = -1;
for (i = 0; oc_trigger[i].match; i++) {
if (oc_trigger[i].exact) {
if (strcmp(oc->optionname,
oc_trigger[i].match) == 0) {
got = i;
break;
}
} else {
if (strncmp(oc->optionname, oc_trigger[i].match,
strlen(oc_trigger[i].match)) == 0) {
got = i;
break;
}
}
}
if (got > -1) {
// If it's date/height controlled, display an ERR
if (oc->activationheight != OPTIONCONTROL_HEIGHT ||
tv_newer(&date_begin, &(oc->activationdate)))
{
tv_to_buf(&(oc->activationdate), cd_buf,
sizeof(cd_buf));
tv_to_buf((tv_t *)&date_begin, cd2_buf,
sizeof(cd_buf));
LOGERR("%s(%s) ERR: ignored '%s' - has "
"height %"PRId32" & date '%s' - "
"expect height %d & date <= '%s'",
from, __func__, oc->optionname,
oc->activationheight, cd_buf,
OPTIONCONTROL_HEIGHT, cd2_buf);
} else
oc_trigger[i].func(oc, from);
}
}
}
K_ITEM *optioncontrol_item_add(PGconn *conn, K_ITEM *oc_item, tv_t *cd, bool begun) K_ITEM *optioncontrol_item_add(PGconn *conn, K_ITEM *oc_item, tv_t *cd, bool begun)
{ {
ExecStatusType rescode; ExecStatusType rescode;
@ -2462,13 +2593,6 @@ K_ITEM *optioncontrol_item_add(PGconn *conn, K_ITEM *oc_item, tv_t *cd, bool beg
row->activationheight = OPTIONCONTROL_HEIGHT; row->activationheight = OPTIONCONTROL_HEIGHT;
} }
// Update if it's changed
if (strcmp(row->optionname, DIFF_PERCENT_NAME) == 0) {
diff_percent = DIFF_VAL(strtod(row->optionvalue, NULL));
if (errno == ERANGE)
diff_percent = DIFF_VAL(DIFF_PERCENT_DEFAULT);
}
INIT_OPTIONCONTROL(&look); INIT_OPTIONCONTROL(&look);
look.data = (void *)row; look.data = (void *)row;
K_RLOCK(optioncontrol_free); K_RLOCK(optioncontrol_free);
@ -2552,6 +2676,8 @@ nostart:
for (n = 0; n < par; n++) for (n = 0; n < par; n++)
free(params[n]); free(params[n]);
/* N.B. if the DB update fails,
* optioncontrol_trigger() will not be called */
K_WLOCK(optioncontrol_free); K_WLOCK(optioncontrol_free);
if (!ok) { if (!ok) {
// Cleanup item passed in // Cleanup item passed in
@ -2567,11 +2693,8 @@ nostart:
} }
add_to_ktree(optioncontrol_root, oc_item); add_to_ktree(optioncontrol_root, oc_item);
k_add_head(optioncontrol_store, oc_item); k_add_head(optioncontrol_store, oc_item);
if (strcmp(row->optionname, SWITCH_STATE_NAME) == 0) {
switch_state = atoi(row->optionvalue); optioncontrol_trigger(oc_item, __func__);
LOGWARNING("%s() set switch_state to %d",
__func__, switch_state);
}
} }
K_WUNLOCK(optioncontrol_free); K_WUNLOCK(optioncontrol_free);
@ -2700,21 +2823,7 @@ bool optioncontrol_fill(PGconn *conn)
add_to_ktree(optioncontrol_root, item); add_to_ktree(optioncontrol_root, item);
k_add_head(optioncontrol_store, item); k_add_head(optioncontrol_store, item);
// There should only be one CURRENT version of switch_state optioncontrol_trigger(item, __func__);
if (CURRENT(&(row->expirydate)) &&
strcmp(row->optionname, SWITCH_STATE_NAME) == 0) {
switch_state = atoi(row->optionvalue);
LOGWARNING("%s() set switch_state to %d",
__func__, switch_state);
}
// The last loaded CURRENT value will be used
if (CURRENT(&(row->expirydate)) &&
strcmp(row->optionname, DIFF_PERCENT_NAME) == 0) {
diff_percent = DIFF_VAL(strtod(row->optionvalue, NULL));
if (errno == ERANGE)
diff_percent = DIFF_VAL(DIFF_PERCENT_DEFAULT);
}
} }
if (!ok) { if (!ok) {
free_optioncontrol_data(item); free_optioncontrol_data(item);

Loading…
Cancel
Save