diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/core.c | 14 | ||||
-rw-r--r-- | src/common/core.h | 13 | ||||
-rw-r--r-- | src/common/db.c | 60 | ||||
-rw-r--r-- | src/common/db.h | 14 | ||||
-rw-r--r-- | src/common/socket.c | 3 | ||||
-rw-r--r-- | src/common/strlib.c | 136 | ||||
-rw-r--r-- | src/common/strlib.h | 21 |
7 files changed, 208 insertions, 53 deletions
diff --git a/src/common/core.c b/src/common/core.c index b89cc3841..bfa563d8c 100644 --- a/src/common/core.c +++ b/src/common/core.c @@ -24,7 +24,12 @@ #include <unistd.h> #endif -int runflag = 1; + +/// Called when a terminate signal is received. +void (*shutdown_callback)(void) = NULL; + + +int runflag = CORE_ST_RUN; int arg_c = 0; char **arg_v = NULL; @@ -78,7 +83,10 @@ static void sig_proc(int sn) case SIGTERM: if (++is_called > 3) exit(EXIT_SUCCESS); - runflag = 0; + if( shutdown_callback != NULL ) + shutdown_callback(); + else + runflag = CORE_ST_STOP;// auto-shutdown break; case SIGSEGV: case SIGFPE: @@ -249,7 +257,7 @@ int main (int argc, char **argv) {// Main runtime cycle int next; - while (runflag) { + while (runflag != CORE_ST_STOP) { next = do_timer(gettick_nocache()); do_sockets(next); } diff --git a/src/common/core.h b/src/common/core.h index fc4af3e3e..beb72d080 100644 --- a/src/common/core.h +++ b/src/common/core.h @@ -7,6 +7,7 @@ extern int arg_c; extern char **arg_v; +/// @see E_CORE_ST extern int runflag; extern char *SERVER_NAME; extern char SERVER_TYPE; @@ -18,4 +19,16 @@ extern void set_server_type(void); extern void do_abort(void); extern void do_final(void); +/// The main loop continues until runflag is CORE_ST_STOP +enum E_CORE_ST +{ + CORE_ST_STOP = 0, + CORE_ST_RUN, + CORE_ST_LAST +}; + +/// Called when a terminate signal is received. (Ctrl+C pressed) +/// If NULL, runflag is set to CORE_ST_STOP instead. +extern void (*shutdown_callback)(void); + #endif /* _CORE_H_ */ diff --git a/src/common/db.c b/src/common/db.c index 595ed241d..935fe472f 100644 --- a/src/common/db.c +++ b/src/common/db.c @@ -271,6 +271,7 @@ static struct db_stats { uint32 dbit_remove; uint32 dbit_destroy; uint32 db_iterator; + uint32 db_exists; uint32 db_get; uint32 db_getall; uint32 db_vgetall; @@ -304,7 +305,7 @@ static struct db_stats { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }; #define DB_COUNTSTAT(token) if (stats. ## token != UINT32_MAX) ++stats. ## token #else /* !defined(DB_ENABLE_STATS) */ @@ -1087,6 +1088,7 @@ static void db_release_both(DBKey key, void *data, DBRelease which) * dbit_obj_destroy - Destroys the iterator, unlocking the database and * * freeing used memory. * * db_obj_iterator - Return a new databse iterator. * + * db_obj_exists - Checks if an entry exists. * * db_obj_get - Get the data identified by the key. * * db_obj_vgetall - Get the data of the matched entries. * * db_obj_getall - Get the data of the matched entries. * @@ -1402,6 +1404,57 @@ static DBIterator* db_obj_iterator(DBMap* self) } /** + * Returns true if the entry exists. + * @param self Interface of the database + * @param key Key that identifies the entry + * @return true is the entry exists + * @protected + * @see DBMap#exists + */ +static bool db_obj_exists(DBMap* self, DBKey key) +{ + DBMap_impl* db = (DBMap_impl*)self; + DBNode node; + int c; + bool found = false; + + DB_COUNTSTAT(db_exists); + if (db == NULL) return false; // nullpo candidate + if (!(db->options&DB_OPT_ALLOW_NULL_KEY) && db_is_key_null(db->type, key)) { + return false; // nullpo candidate + } + + if (db->cache && db->cmp(key, db->cache->key, db->maxlen) == 0) { +#if defined(DEBUG) + if (db->cache->deleted) { + ShowDebug("db_exists: Cache contains a deleted node. Please report this!!!\n"); + return false; + } +#endif + return true; // cache hit + } + + db_free_lock(db); + node = db->ht[db->hash(key, db->maxlen)%HASH_SIZE]; + while (node) { + c = db->cmp(key, node->key, db->maxlen); + if (c == 0) { + if (!(node->deleted)) { + db->cache = node; + found = true; + } + break; + } + if (c < 0) + node = node->left; + else + node = node->right; + } + db_free_unlock(db); + return found; +} + +/** * Get the data of the entry identifid by the key. * @param self Interface of the database * @param key Key that identifies the entry @@ -2351,6 +2404,7 @@ DBMap* db_alloc(const char *file, int line, DBType type, DBOptions options, unsi options = db_fix_options(type, options); /* Interface of the database */ db->vtable.iterator = db_obj_iterator; + db->vtable.exists = db_obj_exists; db->vtable.get = db_obj_get; db->vtable.getall = db_obj_getall; db->vtable.vgetall = db_obj_vgetall; @@ -2493,7 +2547,7 @@ void db_final(void) "dbit_next %10u, dbit_prev %10u,\n" "dbit_exists %10u, dbit_remove %10u,\n" "dbit_destroy %10u, db_iterator %10u,\n" - "db_get %10u,\n" + "db_exits %10u, db_get %10u,\n" "db_getall %10u, db_vgetall %10u,\n" "db_ensure %10u, db_vensure %10u,\n" "db_put %10u, db_remove %10u,\n" @@ -2523,7 +2577,7 @@ void db_final(void) stats.dbit_next, stats.dbit_prev, stats.dbit_exists, stats.dbit_remove, stats.dbit_destroy, stats.db_iterator, - stats.db_get, + stats.db_exists, stats.db_get, stats.db_getall, stats.db_vgetall, stats.db_ensure, stats.db_vensure, stats.db_put, stats.db_remove, diff --git a/src/common/db.h b/src/common/db.h index c1b224bcd..e02de2e08 100644 --- a/src/common/db.h +++ b/src/common/db.h @@ -360,6 +360,15 @@ struct DBMap { DBIterator* (*iterator)(DBMap* self); /** + * Returns true if the entry exists. + * @param self Database + * @param key Key that identifies the entry + * @return true is the entry exists + * @protected + */ + bool (*exists)(DBMap* self, DBKey key); + + /** * Get the data of the entry identifid by the key. * @param self Database * @param key Key that identifies the entry @@ -580,6 +589,11 @@ struct DBMap { # define str2key(k) ((DBKey)(const char *)(k)) #endif /* not DB_MANUAL_CAST_TO_UNION */ +#define db_exists(db,k) ( (db)->exists((db),(k)) ) +#define idb_exists(db,k) ( (db)->exists((db),i2key(k)) ) +#define uidb_exists(db,k) ( (db)->exists((db),ui2key(k)) ) +#define strdb_exists(db,k) ( (db)->exists((db),str2key(k)) ) + #define db_get(db,k) ( (db)->get((db),(k)) ) #define idb_get(db,k) ( (db)->get((db),i2key(k)) ) #define uidb_get(db,k) ( (db)->get((db),ui2key(k)) ) diff --git a/src/common/socket.c b/src/common/socket.c index 262351dcf..8f60f8df5 100644 --- a/src/common/socket.c +++ b/src/common/socket.c @@ -1118,6 +1118,9 @@ void socket_final(void) /// Closes a socket. void do_close(int fd) { + if( fd <= 0 ||fd >= FD_SETSIZE ) + return;// invalid + flush_fifo(fd); // Try to send what's left (although it might not succeed since it's a nonblocking socket) sFD_CLR(fd, &readfds);// this needs to be done before closing the socket sShutdown(fd, SHUT_RDWR); // Disallow further reads/writes diff --git a/src/common/strlib.c b/src/common/strlib.c index 66f281ffc..097f499e6 100644 --- a/src/common/strlib.c +++ b/src/common/strlib.c @@ -441,30 +441,13 @@ bool bin2hex(char* output, unsigned char* input, size_t count) ///////////////////////////////////////////////////////////////////// -/// Parses a delim-separated string. -/// Starts parsing at startoff and fills the pos array with position pairs. -/// out_pos[0] and out_pos[1] are the start and end of line. -/// Other position pairs are the start and end of fields. -/// Returns the number of fields found or -1 if an error occurs. -/// -/// out_pos can be NULL. -/// If a line terminator is found, the end position is placed there. -/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5] -/// for the seconds field and so on. -/// Unfilled positions are set to -1. -/// -/// @param str String to parse -/// @param len Length of the string -/// @param startoff Where to start parsing -/// @param delim Field delimiter -/// @param out_pos Array of resulting positions -/// @param npos Size of the pos array -/// @param opt Options that determine the parsing behaviour -/// @return Number of fields found in the string or -1 if an error occured -int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt) +/// Parses a single field in a delim-separated string. +/// The delimiter after the field is skipped. +/// +/// @param sv Parse state +/// @return 1 if a field was parsed, 0 if already done, -1 on error. +int sv_parse_next(struct s_svstate* sv) { - int i; - int count; enum { START_OF_FIELD, PARSING_FIELD, @@ -473,27 +456,37 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i TERMINATE, END } state; + const char* str; + int len; + enum e_svopt opt; + char delim; + int i; - // check pos/npos - if( out_pos == NULL ) npos = 0; - for( i = 0; i < npos; ++i ) - out_pos[i] = -1; + if( sv == NULL ) + return -1;// error + + str = sv->str; + len = sv->len; + opt = sv->opt; + delim = sv->delim; // check opt if( delim == '\n' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_LF)) ) { - ShowError("sv_parse: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n"); + ShowError("sv_parse_next: delimiter '\\n' is not compatible with options SV_TERMINATE_LF or SV_TERMINATE_CRLF.\n"); return -1;// error } if( delim == '\r' && (opt&(SV_TERMINATE_CRLF|SV_TERMINATE_CR)) ) { - ShowError("sv_parse: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n"); + ShowError("sv_parse_next: delimiter '\\r' is not compatible with options SV_TERMINATE_CR or SV_TERMINATE_CRLF.\n"); return -1;// error } - // check str - if( str == NULL ) + if( sv->done || str == NULL ) + { + sv->done = true; return 0;// nothing to parse + } #define IS_END() ( i >= len ) #define IS_DELIM() ( str[i] == delim ) @@ -502,16 +495,13 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i ((opt&SV_TERMINATE_CR) && str[i] == '\r') || \ ((opt&SV_TERMINATE_CRLF) && i+1 < len && str[i] == '\r' && str[i+1] == '\n') ) #define IS_C_ESCAPE() ( (opt&SV_ESCAPE_C) && str[i] == '\\' ) -#define SET_FIELD_START() if( npos > count*2+2 ) out_pos[count*2+2] = i -#define SET_FIELD_END() if( npos > count*2+3 ) out_pos[count*2+3] = i; ++count +#define SET_FIELD_START() sv->start = i +#define SET_FIELD_END() sv->end = i - i = startoff; - count = 0; + i = sv->off; state = START_OF_FIELD; - if( npos > 0 ) out_pos[0] = startoff;// start while( state != END ) { - if( npos > 1 ) out_pos[1] = i;// end switch( state ) { case START_OF_FIELD:// record start of field and start parsing it @@ -533,7 +523,7 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i ++i;// '\\' if( IS_END() ) { - ShowError("sv_parse: empty escape sequence\n"); + ShowError("sv_parse_next: empty escape sequence\n"); return -1; } if( str[i] == 'x' ) @@ -541,7 +531,7 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i ++i;// 'x' if( IS_END() || !ISXDIGIT(str[i]) ) { - ShowError("sv_parse: \\x with no following hex digits\n"); + ShowError("sv_parse_next: \\x with no following hex digits\n"); return -1; } do{ @@ -562,26 +552,22 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i } else { - ShowError("sv_parse: unknown escape sequence \\%c\n", str[i]); + ShowError("sv_parse_next: unknown escape sequence \\%c\n", str[i]); return -1; } state = PARSING_FIELD; break; } - case END_OF_FIELD:// record end of field and continue + case END_OF_FIELD:// record end of field and stop SET_FIELD_END(); + state = END; if( IS_END() ) - state = END; + ;// nothing else else if( IS_DELIM() ) - { ++i;// delim - state = START_OF_FIELD; - } else if( IS_TERMINATOR() ) state = TERMINATE; - else - state = START_OF_FIELD; break; case TERMINATE: @@ -592,10 +578,14 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i else ++i;// CR or LF #endif + sv->done = true; state = END; break; } } + if( IS_END() ) + sv->done = true; + sv->off = i; #undef IS_END #undef IS_DELIM @@ -604,6 +594,58 @@ int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, i #undef SET_FIELD_START #undef SET_FIELD_END + return 1; +} + + +/// Parses a delim-separated string. +/// Starts parsing at startoff and fills the pos array with position pairs. +/// out_pos[0] and out_pos[1] are the start and end of line. +/// Other position pairs are the start and end of fields. +/// Returns the number of fields found or -1 if an error occurs. +/// +/// out_pos can be NULL. +/// If a line terminator is found, the end position is placed there. +/// out_pos[2] and out_pos[3] for the first field, out_pos[4] and out_pos[5] +/// for the seconds field and so on. +/// Unfilled positions are set to -1. +/// +/// @param str String to parse +/// @param len Length of the string +/// @param startoff Where to start parsing +/// @param delim Field delimiter +/// @param out_pos Array of resulting positions +/// @param npos Size of the pos array +/// @param opt Options that determine the parsing behaviour +/// @return Number of fields found in the string or -1 if an error occured +int sv_parse(const char* str, int len, int startoff, char delim, int* out_pos, int npos, enum e_svopt opt) +{ + struct s_svstate sv; + int count; + + // initialize + if( out_pos == NULL ) npos = 0; + for( count = 0; count < npos; ++count ) + out_pos[count] = -1; + sv.str = str; + sv.len = len; + sv.off = startoff; + sv.opt = opt; + sv.delim = delim; + sv.done = false; + + // parse + count = 0; + if( npos > 0 ) out_pos[0] = startoff; + while( !sv.done ) + { + ++count; + if( sv_parse_next(&sv) <= 0 ) + return -1;// error + if( npos > count*2 ) out_pos[count*2] = sv.start; + if( npos > count*2+1 ) out_pos[count*2+1] = sv.end; + } + if( npos > 1 ) out_pos[1] = sv.off; return count; } diff --git a/src/common/strlib.h b/src/common/strlib.h index 3f4f984cf..f5819bbcb 100644 --- a/src/common/strlib.h +++ b/src/common/strlib.h @@ -78,6 +78,27 @@ typedef enum e_svopt /// Other escape sequences supported by the C compiler. #define SV_ESCAPE_C_SUPPORTED "abtnvfr\?\"'\\" +/// Parse state. +/// The field is [start,end[ +struct s_svstate +{ + const char* str; //< string to parse + int len; //< string length + int off; //< current offset in the string + int start; //< where the field starts + int end; //< where the field ends + enum e_svopt opt; //< parse options + char delim; //< field delimiter + bool done; //< if all the text has been parsed +}; + +/// Parses a single field in a delim-separated string. +/// The delimiter after the field is skipped. +/// +/// @param sv Parse state +/// @return 1 if a field was parsed, 0 if done, -1 on error. +int sv_parse_next(struct s_svstate* sv); + /// Parses a delim-separated string. /// Starts parsing at startoff and fills the pos array with position pairs. /// out_pos[0] and out_pos[1] are the start and end of line. |