diff --git a/log_manager/liblog_manager.so.1.0.1 b/log_manager/liblog_manager.so.1.0.1 new file mode 100755 index 000000000..fcd0555b4 Binary files /dev/null and b/log_manager/liblog_manager.so.1.0.1 differ diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc new file mode 100644 index 000000000..48f9febd9 --- /dev/null +++ b/log_manager/log_manager.cc @@ -0,0 +1,1191 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_PREFIXLEN 250 +#define MAX_SUFFIXLEN 250 +#define MAX_PATHLEN 512 + +/** Write buffer structure */ +typedef struct logfile_writebuf_st { + skygw_chk_t wb_chk_top; + size_t wb_bufsize; + char wb_buf[1]; +} logfile_writebuf_t; + +/** Writer thread structure */ +struct filewriter_st { + skygw_chk_t fwr_chk_top; + logmanager_t* fwr_logmgr; + filewriter_state_t fwr_state; + /** Physical files */ + skygw_file_t* fwr_file[LOGFILE_LAST+1]; + /** fwr_logmes is for messages from log clients */ + skygw_message_t* fwr_logmes; + /** fwr_clientmes is for messages to log clients */ + skygw_message_t* fwr_clientmes; + skygw_thread_t* fwr_thread; + skygw_chk_t fwr_chk_tail; +}; + +/** logfile object corresponds to physical file(s) where + * certain log is written. + */ +struct logfile_st { + skygw_chk_t lf_chk_top; + logmanager_t* lf_lmgr; + /** fwr_logmes is for messages from log clients */ + skygw_message_t* lf_logmes; + logfile_state_t lf_state; + logfile_id_t lf_id; + char* lf_logpath; + char* lf_name_prefix; + char* lf_name_suffix; + int lf_name_sequence; + char* lf_full_name; + int lf_nfiles_max; + size_t lf_file_size; + size_t lf_writebuf_size; + /** Flat list for write buffers ready for disk writing */ + mlist_t lf_writebuf_list; + int lf_npending_writes; + skygw_chk_t lf_chk_tail; +}; + + +struct fnames_conf_st { + skygw_chk_t fn_chk_top; + char* fn_trace_prefix; + char* fn_trace_suffix; + char* fn_msg_prefix; + char* fn_msg_suffix; + char* fn_err_prefix; + char* fn_err_suffix; + char* fn_logpath; + size_t fn_bufsize; + skygw_chk_t fn_chk_tail; +}; + +struct logmanager_st { + skygw_chk_t lm_chk_top; + bool lm_enabled; + simple_mutex_t lm_mutex; + size_t lm_nlinks; + /** fwr_logmes is for messages from log clients */ + skygw_message_t* lm_logmes; + /** fwr_clientmes is for messages to log clients */ + skygw_message_t* lm_clientmes; + fnames_conf_t lm_fnames_conf; + logfile_t lm_logfile[LOGFILE_LAST+1]; + filewriter_t lm_filewriter; + skygw_chk_t lm_chk_tail; +}; + + +/** Static function declarations */ +static bool logfiles_init(logmanager_t* lmgr); +static bool logfile_init( + logfile_t* logfile, + logfile_id_t logfile_id, + logmanager_t* logmanager); +static void logfile_done(logfile_t* logfile); +static void logfile_free_memory(logfile_t* lf); +static bool filewriter_init( + logmanager_t* logmanager, + filewriter_t* fw, + skygw_message_t* clientmes, + skygw_message_t* logmes); +static void filewriter_done(filewriter_t* filewriter); +static bool fnames_conf_init(fnames_conf_t* fn, int argc, char* argv[]); +static void fnames_conf_done(fnames_conf_t* fn); +static char* fname_conf_get_prefix(fnames_conf_t* fn, logfile_id_t id); +static char* fname_conf_get_suffix(fnames_conf_t* fn, logfile_id_t id); +static size_t fname_conf_get_bufsize(fnames_conf_t* fn, logfile_id_t id); +static void* thr_filewriter_fun(void* data); +static logfile_writebuf_t** get_or_create_writebuffers( + void* ctx, + size_t len, + size_t bufsize); +static int logmanager_write( + void* ctx, + logmanager_t* lmgr, + logfile_id_t id, + char* str, + bool flush); +static bool logmanager_register(logmanager_t* lmgr); +static bool logmanager_unregister(logmanager_t* lmgr); +static void logfile_write_buffers( + logfile_t* lf, + logfile_writebuf_t** p_wb, + char* str); + + +const char* get_suffix_default(void) +{ + return ".log"; +} + +const char* get_trace_prefix_default(void) +{ + return "skygw_trace"; +} + +const char* get_trace_suffix_default(void) +{ + return get_suffix_default(); +} + +const char* get_msg_prefix_default(void) +{ + return "skygw_msg"; +} + +const char* get_msg_suffix_default(void) +{ + return get_suffix_default(); +} + +const char* get_err_prefix_default(void) +{ + return "skygw_err"; +} + +const char* get_err_suffix_default(void) +{ + return get_suffix_default(); +} + +const char* get_logpath_default(void) +{ + return "/tmp"; +} + +const size_t get_bufsize_default(void) +{ + return (size_t)256; +} + + +/** + * @node Initializes log managing routines in SkySQL Gateway. + * + * Parameters: + * @param p_ctx - in, give + * pointer to memory location where logmanager stores private write + * buffer. + * + * @param argc - in, use + * number of arguments in argv array + * + * @param argv - in, use + * arguments array + * + * @return + * + * + * @details (write detailed description here) + * + */ +logmanager_t* skygw_logmanager_init( + void** p_ctx, + int argc, + char* argv[]) +{ + logmanager_t* lmgr = NULL; + fnames_conf_t* fn; + filewriter_t* fw; + int err; + + ss_dfprintf(stderr, ">> skygw_logmanager_init\n"); + + lmgr = (logmanager_t *)calloc(1, sizeof(logmanager_t)); + lmgr->lm_chk_top = CHK_NUM_LOGMANAGER; + lmgr->lm_chk_tail = CHK_NUM_LOGMANAGER; + + if (simple_mutex_init( + &lmgr->lm_mutex, + strdup("Logmanager mutex")) == NULL) + { + goto return_err; + } + lmgr->lm_clientmes = skygw_message_init(); + lmgr->lm_logmes = skygw_message_init(); + fn = &lmgr->lm_fnames_conf; + fw = &lmgr->lm_filewriter; + + /** Initialize configuration including log file naming info */ + if (!fnames_conf_init(fn, argc, argv)) { + goto return_err; + } + + /** Initialize logfiles */ + if(!logfiles_init(lmgr)) { + goto return_err; + } + + /** Initialize filewriter data and open the (first) log file(s) + * for each log file type. */ + if (!filewriter_init(lmgr, fw, lmgr->lm_clientmes, lmgr->lm_logmes)) { + goto return_err; + } + /** Initialize and start filewriter thread */ + fw->fwr_thread = + skygw_thread_init( + strdup("filewriter thr"), + thr_filewriter_fun, + (void *)fw); + + if ((err = skygw_thread_start(fw->fwr_thread)) != 0) { + goto return_err; + } + /** Wait message from filewriter_thr */ + skygw_message_wait(fw->fwr_clientmes); + +return_mgr: + lmgr->lm_enabled = TRUE; + ss_dfprintf(stderr, "<< skygw_logmanager_init\n"); + return lmgr; + +return_err: + /** This includes clear-up for all created objects */ + skygw_logmanager_done(NULL, &lmgr); + fprintf(stderr, "Initializing logmanager failed.\n"); + goto return_mgr; +} + + +/** + * @node End execution of log manager + * + * Parameters: + * @param p_ctx - in, take + * pointer to memory location including context pointer. Context will + * be freed in this function. + * + * @param logmanager - in, use + * pointer to logmanager. + * + * @return void + * + * + * @details Stops file writing thread, releases filewriter, and logfiles. + * + */ +void skygw_logmanager_done( + void** p_ctx, + logmanager_t** logmanager) +{ + int i; + filewriter_t* fwr; + logfile_t* lf; + logmanager_t* lmgr; + + ss_dfprintf(stderr, ">> skygw_logmanager_done\n"); + lmgr = *logmanager; + + if (lmgr == NULL) { + fprintf(stderr, "Error. Logmanager is not initialized.\n"); + return; + } + CHK_LOGMANAGER(lmgr); + lmgr->lm_enabled = FALSE; + + while (TRUE) { + simple_mutex_lock(&lmgr->lm_mutex, TRUE); + + if (lmgr->lm_nlinks == 0) { + simple_mutex_unlock(&lmgr->lm_mutex); + break; + } + simple_mutex_unlock(&lmgr->lm_mutex); + usleep(100*MSEC_USEC); + } + + fwr = &lmgr->lm_filewriter; + CHK_FILEWRITER(fwr); + + /** Inform filewriter thread and wait until it has stopped. */ + skygw_thread_set_exitflag(fwr->fwr_thread, + fwr->fwr_logmes, + fwr->fwr_clientmes); + /** Free thread memory */ + skygw_thread_done(fwr->fwr_thread); + /** Free filewriter memory. */ + filewriter_done(fwr); + + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) { + lf = &lmgr->lm_logfile[i]; + CHK_LOGFILE(lf); + + /** Release logfile memory */ + logfile_done(lf); + } + + fnames_conf_done(&lmgr->lm_fnames_conf); + skygw_message_done(lmgr->lm_clientmes); + skygw_message_done(lmgr->lm_logmes); + simple_mutex_done(&lmgr->lm_mutex); + free(lmgr); + *logmanager = NULL; + + ss_dfprintf(stderr, "<< skygw_logmanager_done\n"); +} + +static logfile_t* logmanager_get_logfile( + logmanager_t* lmgr, + logfile_id_t id) +{ + CHK_LOGMANAGER(lmgr); + ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); + CHK_LOGFILE(lmgr->lm_logfile); + + return &lmgr->lm_logfile[id]; +} + +static int logmanager_write( + void* ctx, + logmanager_t* lmgr, + logfile_id_t id, + char* str, + bool flush) +{ + logfile_t* lf; + /** array of constan-size buffers */ + logfile_writebuf_t** wb_arr; + int err = 0; + + CHK_LOGMANAGER(lmgr); + ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); + + if (id < LOGFILE_FIRST || id > LOGFILE_LAST) { + /** invalid id, since we don't have logfile yet, + * recall logmanager_write. */ + err = logmanager_write(NULL, + lmgr, + LOGFILE_ERROR, + strdup("Invalid logfile id argument."), + TRUE); + if (err != 0) { + fprintf(stderr, + "Writing to logfile %s failed.\n", + STRLOGID(LOGFILE_ERROR)); + } + err = -1; + goto return_err; + } + /** Get logfile and check its correctness. */ + lf = logmanager_get_logfile(lmgr, id); + CHK_LOGFILE(lf); + + if (lf == NULL) { + fprintf(stderr, "Could find valid logfile from the log manager.\n"); + err = -1; + goto return_err; + } + /** skygw_log_flush doesn't pass any string */ + if (str != NULL) { + /** Get or create array of constant-size buffers. */ + wb_arr = get_or_create_writebuffers( + ctx, + strlen(str), + lf->lf_writebuf_size); + + if (wb_arr == NULL) { + fprintf(stderr, "Getting or creating write buffers failed.\n"); + err = -1; + goto return_err; + } + /** Split loginfo to buffers, if necessary, and add buffers + * to logfile. + * Free write buffer pointer array, and original string. */ + logfile_write_buffers(lf, wb_arr, str); + } else { + ss_dassert(flush); + } + /** Send notification to filewriter thread */ + if (flush) { + skygw_message_send(lf->lf_logmes); + } + +return_err: + return err; +} + + +/** + * @node Get or allocate new buffers for log writing. + * + * Parameters: + * @param ctx - + * + * + * @param len - + * + * + * @param bufsize - + * + * + * @return + * + * + * @details Pointers to created buffers are stored to pointer array p_str. + * + */ +static logfile_writebuf_t** get_or_create_writebuffers( + void* ctx, + size_t len, + size_t bufsize) +{ + int i; + logfile_writebuf_t** p_wb; + size_t allocsize; + int nbufs; + size_t llen; + /** Additional pointer is left to NULL to mark the end of array. */ + + /** Allocate pointer array for buffer pointers. */ + llen = len+strlen("\n"); + nbufs = llen/bufsize; + nbufs = (llen%bufsize)>0 ? nbufs+1 : nbufs; + p_wb = (logfile_writebuf_t **)calloc(nbufs+1, sizeof(char *)); + + if (p_wb == NULL) { + fprintf(stderr, + "Allocating memory for write buffer " + "pointer array failed.\n"); + goto return_p_str; + } + /** Allocate memory for all write buffers. + * Real allocation size includes logfile_writebuf_t and bufsize. + * -1 is the one byte defined in logfile_writebuf_st. + */ + allocsize = sizeof(logfile_writebuf_t)+bufsize-1; + *p_wb = (logfile_writebuf_t *)calloc(nbufs, allocsize); + + if (*p_wb == NULL) { + fprintf(stderr, + "Allocating memory for write buffer " + "pointer array failed.\n"); + free(*p_wb); + free(p_wb); + *p_wb = NULL; + goto return_p_str; + } + + /** Store pointers of each buffer from continuous memory chunk + * to p_str. Initialize each write buffer. + */ + for (i=0; iwb_chk_top = CHK_NUM_WRITEBUF; + p_wb[i]->wb_bufsize = bufsize; + } + ss_dassert(p_wb[i] == NULL); + +return_p_str: + return p_wb; +} + +static void logfile_write_buffers( + logfile_t* lf, + logfile_writebuf_t** p_wb, + char* str) +{ + mlist_t* wblist; + logfile_writebuf_t** p_data; + logfile_writebuf_t* wb; + char* p; + size_t slen; + size_t copylen; + + CHK_LOGFILE(lf); + slen = strlen(str); + p_data = p_wb; + p = str; + + /** Copy log string to write buffer(s) */ + while(slen > 0) { + wb = *p_wb; + copylen = MIN(wb->wb_bufsize, slen); + ss_dassert(*p_data != NULL); + memcpy(wb->wb_buf, p, copylen); + /** force adding terminating '\0' */ + memset(&wb->wb_buf[copylen], 0, 1); + /** If this is last buffer, add line feed */ + if (copylen < wb->wb_bufsize) { + strcpy(&wb->wb_buf[copylen], "\n"); + } + p_wb += 1; + p += copylen; + slen -= copylen; + } + ss_dassert(slen == 0); + ss_dassert(*p_wb == NULL); + p_wb = p_data; + + wblist = &lf->lf_writebuf_list; + simple_mutex_lock(&wblist->mlist_mutex, TRUE); + + /** Add write buffers to write buffers list from where + * filewriter reads them. */ + while(*p_wb != NULL) { + mlist_add_data_nomutex(wblist, *p_wb); + p_wb += 1; + } + simple_mutex_unlock(&wblist->mlist_mutex); + + ss_dassert(*p_wb == NULL); + /** Free pointer array memory */ + free(p_data); + +} + +int skygw_log_write_flush( + void* ctx, + logmanager_t* lmgr, + logfile_id_t id, + char* str) +{ + int err = 0; + + if (lmgr == NULL) { + fprintf(stderr, "Error. Logmanager is not initialized.\n"); + err = -1; + goto return_err; + } + CHK_LOGMANAGER(lmgr); + + if (!logmanager_register(lmgr)) { + fprintf(stderr, "Logmanager is not available\n"); + err = -1; + goto return_err; + } + ss_dfprintf(stderr, + "skygw_log_write_flush writes to %s :\n\t%s.\n", + STRLOGID(id), + str); + err = logmanager_write(ctx, lmgr, id, str, TRUE); + + if (err != 0) { + fprintf(stderr, "skygw_log_write_flush failed.\n"); + goto return_unregister; + } + ss_dfprintf(stderr, "skygw_log_write_flush succeeed.\n"); + +return_unregister: + logmanager_unregister(lmgr); +return_err: + return err; +} + +int skygw_log_write( + void* ctx, + logmanager_t* lmgr, + logfile_id_t id, + char* str) +{ + int err = 0; + + if (lmgr == NULL) { + fprintf(stderr, "Error. Logmanager is not initialized.\n"); + err = -1; + goto return_err; + } + CHK_LOGMANAGER(lmgr); + + if (!logmanager_register(lmgr)) { + fprintf(stderr, "Logmanager is not available\n"); + err = -1; + goto return_err; + } + ss_dfprintf(stderr, + "skygw_log_write writes to %s :\n\t%s.\n", + STRLOGID(id), + str); + err = logmanager_write(ctx, lmgr, id, str, FALSE); + + if (err != 0) { + fprintf(stderr, "skygw_log_write failed.\n"); + goto return_unregister; + } + + ss_dfprintf(stderr, "skygw_log_write succeeed.\n"); + +return_unregister: + logmanager_unregister(lmgr); +return_err: + return err; +} + +int skygw_log_flush( + logmanager_t* lmgr, + logfile_id_t id) +{ + int err = 0; + CHK_LOGMANAGER(lmgr); + + if (!logmanager_register(lmgr)) { + fprintf(stderr, "Logmanager is not available\n"); + err = -1; + goto return_err; + } + err = logmanager_write(NULL, lmgr, id, NULL, TRUE); + + if (err != 0) { + fprintf(stderr, "skygw_log_flush failed.\n"); + goto return_unregister; + } + ss_dfprintf(stderr, + "skygw_log_flush : flushed %s successfully.\n", + STRLOGID(id)); +return_unregister: + logmanager_unregister(lmgr); +return_err: + return err; +} + +/** + * @node Increase link count of logmanager. + * + * Parameters: + * @param lmgr - + * + * + * @return + * + * + * @details Link count modify is protected by mutex. + * + */ +static bool logmanager_register( + logmanager_t* lmgr) +{ + bool succp = FALSE; + int err; + + err = simple_mutex_lock(&lmgr->lm_mutex, TRUE); + + if (err != 0) { + goto return_succp; + } + succp = lmgr->lm_enabled; + + if (succp) { + lmgr->lm_nlinks += 1; + } + simple_mutex_unlock(&lmgr->lm_mutex); +return_succp: + return succp; +} + +/** + * @node Decrease link count of logmanager. + * + * Parameters: + * @param lmgr - + * + * + * @return + * + * + * @details Link count modify is protected by mutex. + * + */ +static bool logmanager_unregister( + logmanager_t* lmgr) +{ + bool succp = FALSE; + int err; + + err = simple_mutex_lock(&lmgr->lm_mutex, TRUE); + + if (err != 0) { + goto return_succp; + } + succp = lmgr->lm_enabled; + ss_dassert(succp); + + if (succp) { + lmgr->lm_nlinks -= 1; + ss_dassert(lmgr->lm_nlinks >= 0); + } + simple_mutex_unlock(&lmgr->lm_mutex); +return_succp: + return succp; +} + + +/** + * @node Initialize log file naming parameters from call arguments + * or from default functions in cases where arguments are not provided. + * + * Parameters: + * @param fn - + * + * + * @param argc - + * + * + * @param argv - + * + * + * @return + * + * + * @details (write detailed description here) + * + */ +static bool fnames_conf_init( + fnames_conf_t* fn, + int argc, + char* argv[]) +{ + int opt; + int i; + bool succp = FALSE; + const char* argstr = + "* a - trace prefix, default \"skygw_trace\"\n" + "* b - trace suffix, default \".log\"\n" + "* c - message prefix, default \"skygw_msg\"\n" + "* d - message suffix, default \".log\"\n" + "* e - error prefix, default \"skygw_err\"\n" + "* f - error suffix, default \".log\"\n" + "* g - log path, default \"/tmp\"\n" + "* h - write buffer size in bytes, default 256\n"; + + fn->fn_chk_top = CHK_NUM_FNAMES; + fn->fn_chk_tail = CHK_NUM_FNAMES; + + while ((opt = getopt(argc, argv, "+a:b:c:d:e:f:g:h:")) != -1) { + switch (opt) { + case 'a': + fn->fn_trace_prefix = strndup(optarg, MAX_PREFIXLEN); + break; + + case 'b': + fn->fn_trace_suffix = strndup(optarg, MAX_SUFFIXLEN); + break; + + case 'c': + fn->fn_msg_prefix = strndup(optarg, MAX_PREFIXLEN); + break; + + case 'd': + fn->fn_msg_suffix = strndup(optarg, MAX_SUFFIXLEN); + break; + + case 'e': + fn->fn_err_prefix = strndup(optarg, MAX_PREFIXLEN); + break; + + case 'f': + fn->fn_err_suffix = strndup(optarg, MAX_SUFFIXLEN); + break; + + case 'g': + fn->fn_logpath = strndup(optarg, MAX_PATHLEN); + break; + + case 'h': + fn->fn_bufsize = strtoul(optarg, NULL, 10); + break; + + default: + fprintf(stderr, + "\nSupported arguments are\n%s\n", + argstr); + goto return_conf_init; + } /** switch (opt) */ + } + + fn->fn_trace_prefix = (fn->fn_trace_prefix == NULL) ? + strdup(get_trace_prefix_default()) : NULL; + fn->fn_trace_suffix = (fn->fn_trace_suffix == NULL) ? + strdup(get_trace_suffix_default()) : NULL; + fn->fn_msg_prefix = (fn->fn_msg_prefix == NULL) ? + strdup(get_msg_prefix_default()) : NULL; + fn->fn_msg_suffix = (fn->fn_msg_suffix == NULL) ? + strdup(get_msg_suffix_default()) : NULL; + fn->fn_err_prefix = (fn->fn_err_prefix == NULL) ? + strdup(get_err_prefix_default()) : NULL; + fn->fn_err_suffix = (fn->fn_err_suffix == NULL) ? + strdup(get_err_suffix_default()) : NULL; + fn->fn_logpath = (fn->fn_logpath == NULL) ? + strdup(get_logpath_default()) : NULL; + fn->fn_bufsize = (fn->fn_bufsize == 0) ? + get_bufsize_default() : 0; + + ss_dfprintf(stderr, "Command line : "); + for (i=0; i= LOGFILE_FIRST && id <= LOGFILE_LAST); + + switch (id) { + case LOGFILE_TRACE: + return fn->fn_trace_prefix; + break; + + case LOGFILE_MESSAGE: + return fn->fn_msg_prefix; + break; + + case LOGFILE_ERROR: + return fn->fn_err_prefix; + break; + + default: + return NULL; + } +} + +static char* fname_conf_get_suffix( + fnames_conf_t* fn, + logfile_id_t id) +{ + CHK_FNAMES_CONF(fn); + ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); + + switch (id) { + case LOGFILE_TRACE: + return fn->fn_trace_suffix; + break; + + case LOGFILE_MESSAGE: + return fn->fn_msg_suffix; + break; + + case LOGFILE_ERROR: + return fn->fn_err_suffix; + break; + + default: + return NULL; + } +} + +static size_t fname_conf_get_bufsize( + fnames_conf_t* fn, + logfile_id_t id) +{ + CHK_FNAMES_CONF(fn); + ss_dassert(id >= LOGFILE_FIRST && id <= LOGFILE_LAST); + + switch (id) { + case LOGFILE_TRACE: + case LOGFILE_MESSAGE: + case LOGFILE_ERROR: + return fn->fn_bufsize; + break; + + default: + return 0; + } +} + +static bool logfiles_init( + logmanager_t* lmgr) +{ + bool succp = TRUE; + int i = LOGFILE_FIRST; + + while(i<=LOGFILE_LAST && succp) { + succp = logfile_init(&lmgr->lm_logfile[i], (logfile_id_t)i, lmgr); + + if (!succp) { + fprintf(stderr, "Initializing logfiles failed\n"); + break; + } + i++; + } + return succp; +} + + +static bool logfile_init( + logfile_t* logfile, + logfile_id_t logfile_id, + logmanager_t* logmanager) +{ + bool succp = FALSE; + size_t namelen; + fnames_conf_t* fn = &logmanager->lm_fnames_conf; + + logfile->lf_chk_top = CHK_NUM_LOGFILE; + logfile->lf_chk_tail = CHK_NUM_LOGFILE; + logfile->lf_logmes = logmanager->lm_logmes; + logfile->lf_state = LOGFILE_INIT; + logfile->lf_id = logfile_id; + logfile->lf_logpath = strdup(fn->fn_logpath); + logfile->lf_name_prefix = fname_conf_get_prefix(fn, logfile_id); + logfile->lf_name_suffix = fname_conf_get_suffix(fn, logfile_id); + logfile->lf_npending_writes = 0; + logfile->lf_name_sequence = 1; + logfile->lf_lmgr = logmanager; + /** Read existing files to logfile->lf_files_list and create + * new file for log named after / + */ + namelen = strlen(logfile->lf_logpath) + + sizeof('/') + + strlen(logfile->lf_name_prefix) + + printf("%d",logfile->lf_name_sequence) + + strlen(logfile->lf_name_suffix) + + sizeof('\0'); + + logfile->lf_full_name = (char *)malloc(namelen); + + if (logfile->lf_full_name == NULL) { + fprintf(stderr, "Memory allocation for full logname failed\n"); + goto return_with_succp; + } + ss_dassert(logfile->lf_full_name != NULL); + + snprintf(logfile->lf_full_name, + namelen, + "%s/%s%d%s", + logfile->lf_logpath, + logfile->lf_name_prefix, + logfile->lf_name_sequence, + logfile->lf_name_suffix); + + logfile->lf_writebuf_size = fname_conf_get_bufsize(fn, logfile_id); + + if (mlist_init(&logfile->lf_writebuf_list, + NULL, + strdup("logfile writebuf list")) == NULL) + { + ss_dfprintf(stderr, "Initializing logfile writebuf list failed\n"); + logfile_free_memory(logfile); + goto return_with_succp; + } + CHK_LOGFILE(logfile); + succp = TRUE; + +return_with_succp: + return succp; +} + +/** + * @node Flush logfile and free memory allocated for it. + * + * Parameters: + * @param lf - + * + * + * @return void + * + * + * @details Operation is not protected. it is assumed that no one tries + * to call logfile functions when logfile_done is called. + * + * It is also assumed that filewriter doesn't exist anymore. + * + * Logfile access could be protected by using exit flag and rwlock to protect + * flag read/write. Lock would be held during log write operation by clients. + * + */ +static void logfile_done( + logfile_t* lf) +{ + CHK_LOGFILE(lf); + ss_dassert(lf->lf_lmgr->lm_filewriter.fwr_state == FILEWRITER_DONE); + + if (lf->lf_npending_writes != 0) { + /** Not implemented yet */ + //logfile_write_flush(lf); + } + mlist_done(&lf->lf_writebuf_list); + logfile_free_memory(lf); +} + +static void logfile_free_memory( + logfile_t* lf) +{ + CHK_LOGFILE(lf); + free(lf->lf_logpath); + free(lf->lf_name_prefix); + free(lf->lf_name_suffix); + free(lf->lf_full_name); + lf->lf_state = LOGFILE_DONE; +} + + + +/** + * @node Initialize filewriter struct to a given address + * + * Parameters: + * @param fw - + * + * + * @return + * + * + * @details (write detailed description here) + * + */ +static bool filewriter_init( + logmanager_t* logmanager, + filewriter_t* fw, + skygw_message_t* clientmes, + skygw_message_t* logmes) +{ + bool succp = FALSE; + logfile_t* lf; + logfile_id_t id; + int i; + CHK_LOGMANAGER(logmanager); + + fw->fwr_chk_top = CHK_NUM_FILEWRITER; + fw->fwr_chk_tail = CHK_NUM_FILEWRITER; + fw->fwr_logmgr = logmanager; + fw->fwr_state = FILEWRITER_INIT; + /** Message from filewriter to clients */ + fw->fwr_logmes = logmes; + /** Message from clients to filewriter */ + fw->fwr_clientmes = clientmes; + + if (fw->fwr_logmes == NULL || fw->fwr_clientmes == NULL) { + goto return_succp; + } + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) { + id = (logfile_id_t)i; + lf = logmanager_get_logfile(logmanager, id); + fw->fwr_file[id] = skygw_file_init(lf->lf_full_name); + } + fw->fwr_state = FILEWRITER_RUN; + succp = TRUE; +return_succp: + return succp; +} + + +static void filewriter_done( + filewriter_t* fw) +{ + int i; + logfile_id_t id; + + CHK_FILEWRITER(fw); + + fw->fwr_logmes = NULL; + fw->fwr_clientmes = NULL; + fw->fwr_state = FILEWRITER_DONE; + + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) { + id = (logfile_id_t)i; + skygw_file_done(fw->fwr_file[id]); + } +} + + +/** + * @node File writer thread routine + * + * Parameters: + * @param data - + * + * + * @return + * + * + * @details (write detailed description here) + * + */ +static void* thr_filewriter_fun( + void* data) +{ + skygw_thread_t* thr; + filewriter_t* fwr; + logfile_t* lf; + skygw_file_t* file; + + logfile_writebuf_t* wb; + char* writep; + size_t nbytes; + logfile_id_t id; + mlist_t* wblist; + mlist_node_t* node; + mlist_node_t* anchor; + int i; + bool succp; + + thr = (skygw_thread_t *)data; + fwr = (filewriter_t *)skygw_thread_get_data(thr); + CHK_FILEWRITER(fwr); + skygw_thread_set_state(thr, THR_RUNNING); + skygw_message_send(fwr->fwr_clientmes); + + while(!skygw_thread_must_exit(thr)) { + /** Wait until new log arrival message appears. + * Reset message to avoid redundant calls. */ + skygw_message_wait(fwr->fwr_logmes); + skygw_message_reset(fwr->fwr_logmes); + + /** Test _all_ logfiles if they have buffered writes. */ + for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) { + id = (logfile_id_t)i; + lf = logmanager_get_logfile(fwr->fwr_logmgr, id); + CHK_LOGFILE(lf); + file = fwr->fwr_file[id]; + wblist = &lf->lf_writebuf_list; + + simple_mutex_lock(&wblist->mlist_mutex, TRUE); + + /** Process non-empty write buffer lists. */ + if (wblist->mlist_nodecount != 0) { + /** Detach nodes from the list and release lock */ + node = wblist->mlist_first; + wblist->mlist_first = NULL; + wblist->mlist_last = NULL; + wblist->mlist_nodecount = 0; + simple_mutex_unlock(&wblist->mlist_mutex); + + /** Read data from write buf list and write it to file. */ + while(node != NULL) { + wb = (logfile_writebuf_t*)node->mlnode_data; + writep = wb->wb_buf; + nbytes = strlen(writep); + succp = skygw_file_write(file, (void *)writep, nbytes); + ss_dassert(succp); + anchor = node; + node = node->mlnode_next; + mlist_node_done(anchor); + } + } else { + simple_mutex_unlock(&wblist->mlist_mutex); + } + } /* for */ + } /* while */ + /** This informs client thread that file writer thread is stopped. */ + skygw_thread_set_state(thr, THR_STOPPED); + skygw_message_send(fwr->fwr_clientmes); + return NULL; +} + +static void fnames_conf_done( + fnames_conf_t* fn) +{ + free(fn->fn_logpath); + memset(fn, 0, sizeof(fnames_conf_t)); +} diff --git a/log_manager/log_manager.h b/log_manager/log_manager.h new file mode 100644 index 000000000..79b0018b1 --- /dev/null +++ b/log_manager/log_manager.h @@ -0,0 +1,65 @@ + + +typedef struct filewriter_st filewriter_t; +typedef struct logfile_st logfile_t; +typedef struct fnames_conf_st fnames_conf_t; +typedef struct logmanager_st logmanager_t; + +typedef enum { + LOGFILE_FIRST = 0, + LOGFILE_TRACE = LOGFILE_FIRST, + LOGFILE_MESSAGE, + LOGFILE_ERROR, + LOGFILE_LAST = LOGFILE_ERROR +} logfile_id_t; + +typedef enum { FILEWRITER_INIT, FILEWRITER_RUN, FILEWRITER_DONE } + filewriter_state_t; +typedef enum { LOGFILE_INIT, LOGFILE_OPENED, LOGFILE_DONE } logfile_state_t; + +EXTERN_C_BLOCK_BEGIN + +logmanager_t* skygw_logmanager_init(void** ctx, int argc, char* argv[]); +void skygw_logmanager_done(void** ctx, logmanager_t** lm); +int skygw_log_write(void* ctx, logmanager_t* lmgr, logfile_id_t id, char* str); +int skygw_log_flush(logmanager_t* lmgr, logfile_id_t id); +int skygw_log_write_flush(void* ctx, + logmanager_t* lmgr, + logfile_id_t id, + char* str); + +EXTERN_C_BLOCK_END + +const char* get_trace_prefix_default(void); +const char* get_trace_suffix_default(void); +const char* get_msg_prefix_default(void); +const char* get_msg_suffix_default(void); +const char* get_err_prefix_default(void); +const char* get_err_suffix_default(void); +const char* get_logpath_default(void); + +/* +bool logfile_write( + skygw_ctx_t* ctx, + logmgr_t* mgr, + logfile_id_t id, + char* msg); + +bool logfile_write_flush( + skygw_ctx_t* ctx, + logmgr_t* mgr, + logfile_id_t id, + char* msg); + +bool logfile_flush( + logmgr_t* mgr, + logfile_id_t id); + +bool logfile_init( + logmgr_t* mgr, + logfile_id_t id); + +void logfile_done( + logmgr_t* mgr, + logfile_id_t id); +*/ diff --git a/log_manager/makefile b/log_manager/makefile new file mode 100644 index 000000000..86cd77a80 --- /dev/null +++ b/log_manager/makefile @@ -0,0 +1,38 @@ +include ../build_gateway.inc +include ../makefile.inc + +CC = gcc +CPP = g++ + +LOG_WRITER_PATH := $(shell pwd) + +makeall: clean all + +clean: + make -C ../utils clean + - $(DEL) *.o + - $(DEL) *.so + - $(DEL) *.so.1.0.1 + - $(DEL) *~ + +all: utils lib + +utils: + make -C $(ROOT_PATH)/utils clean all + $(COPY) $(ROOT_PATH)/utils/skygw_utils.o ./ + +lib: libcomp liblink + +libcomp: + $(CPP) -c $(CFLAGS) \ + -I$(MARIADB_SRC_PATH)/include/ \ + -I../utils/ -I./ \ + -fPIC ./log_manager.cc -o log_manager.o $(LDLIBS) + +liblink: + $(CPP) -shared \ + -Wl,-soname,liblog_manager.so \ + -o liblog_manager.so.1.0.1 log_manager.o \ + $(LDLIBS) $(CPP_LDLIBS) + $(DEL) ./liblog_manager.so + $(LINK) ./liblog_manager.so.1.0.1 ./liblog_manager.so diff --git a/log_manager/test/core b/log_manager/test/core new file mode 100644 index 000000000..99c0105e8 Binary files /dev/null and b/log_manager/test/core differ diff --git a/log_manager/test/makefile b/log_manager/test/makefile new file mode 100644 index 000000000..db1bfa306 --- /dev/null +++ b/log_manager/test/makefile @@ -0,0 +1,34 @@ +include ../../build_gateway.inc +include ../../makefile.inc + +CC = gcc +CPP = g++ + +TESTPATH := $(shell pwd) +LOG_MANAGER_PATH := $(ROOT_PATH)/log_manager +TESTAPP = $(TESTPATH)/testlog + +runtest: makeall testall + +makeall: clean all + +clean: + - $(DEL) *.o + - $(DEL) testlog + - $(DEL) *~ + +all: testcomp testall + +testcomp: + $(CC) $(CFLAGS) \ + -L$(LOG_MANAGER_PATH) \ + -Wl,-rpath,$(LOG_MANAGER_PATH)/ \ + -o testlog -DSS_DEBUG \ + -I$(SOLIDDB_SRC_PATH)/include \ + -I$(MARIADB_SRC_PATH)/include \ + -I$(LOG_MANAGER_PATH) -I$(ROOT_PATH)/utils testlog.c \ + -llog_manager $(LDLIBS) \ + $(LOG_MANAGER_PATH)/skygw_utils.o + +testall: + - $(LAUNCH_DEBUGGER) $(TESTAPP) $(BACKGR) diff --git a/log_manager/test/nohup.out b/log_manager/test/nohup.out new file mode 100644 index 000000000..06d0a070f --- /dev/null +++ b/log_manager/test/nohup.out @@ -0,0 +1,65 @@ +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +Warning: + Name: menubar + Class: XmRowColumn + XtGrabPointer failed. + +(Annoyed? Try 'Edit->Preferences->General->Suppress X Warnings'!) +/bin/sh -c 'exec gdb -q -fullname '\''/home/raatikka/src/bazaar/shared/skygateway/log_manager/test/testlog'\''' [26040]: Agent wouldn't die (Terminated) +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +/bin/sh -c 'exec gdb -q -fullname '\''/home/raatikka/src/bazaar/shared/skygateway/log_manager/test/testlog'\''' [26458]: Agent wouldn't die (Terminated) +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. +WARNING: +UTF-8 charmap detected. Switching off UTF-8 as ddd has problems with it. See +README.SUSE for futher information. Use -x parameter to bypass this wrapper. diff --git a/log_manager/test/testlog b/log_manager/test/testlog new file mode 100755 index 000000000..d65090582 Binary files /dev/null and b/log_manager/test/testlog differ diff --git a/log_manager/test/testlog.c b/log_manager/test/testlog.c new file mode 100644 index 000000000..027a814ac --- /dev/null +++ b/log_manager/test/testlog.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + int err; + logmanager_t* lmgr; + char* logstr; + + lmgr = skygw_logmanager_init(NULL, argc, argv); + + logstr = strdup("My name is Tracey"); + err = skygw_log_write(NULL, lmgr, LOGFILE_TRACE, logstr); + free(logstr); + + logstr = strdup("My name is Stacey"); + err = skygw_log_write_flush(NULL, lmgr, LOGFILE_TRACE, logstr); + free(logstr); + + skygw_logmanager_done(NULL, &lmgr); + + logstr = strdup("My name is Philip"); + err = skygw_log_write(NULL, lmgr, LOGFILE_TRACE, logstr); + free(logstr); + + lmgr = skygw_logmanager_init(NULL, argc, argv); + + logstr = strdup("A terrible error has occurred!"); + err = skygw_log_write_flush(NULL, lmgr, LOGFILE_ERROR, logstr); + free(logstr); + + logstr = strdup("Hi, how are you?"); + err = skygw_log_write(NULL, lmgr, LOGFILE_MESSAGE, logstr); + free(logstr); + + logstr = strdup("I'm doing fine!"); + err = skygw_log_write(NULL, lmgr, LOGFILE_MESSAGE, logstr); + free(logstr); + +return_err: + skygw_logmanager_done(NULL, &lmgr); + return err; +} diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index 048811b4f..66659cf4d 100644 --- a/utils/skygw_debug.h +++ b/utils/skygw_debug.h @@ -1,4 +1,6 @@ #include +#include +#include #if !defined(SKYGW_DEBUG_H) #define SKYGW_DEBUG_H @@ -21,6 +23,10 @@ ((t) == QUERY_TYPE_READ ? "QUERY_TYPE_READ" : \ ((t) == QUERY_TYPE_SESSION_WRITE ? "QUERY_TYPE_SESSION_WRITE" : \ "QUERY_TYPE_UNKNOWN"))) +#define STRLOGID(i) ((i) == LOGFILE_TRACE ? "LOGFILE_TRACE" : \ + ((i) == LOGFILE_MESSAGE ? "LOGFILE_MESSAGE" : \ + ((i) == LOGFILE_ERROR ? "LOGFILE_ERROR" : \ + "Unknown logfile type"))) # define ss_dfprintf fprintf # define ss_dfflush fflush # define ss_dfwrite fwrite @@ -72,15 +78,68 @@ typedef enum skygw_chk_t { CHK_NUM_SLIST = CHK_NUM_BASE, CHK_NUM_SLIST_NODE, CHK_NUM_SLIST_CURSOR, + CHK_NUM_MLIST, + CHK_NUM_MLIST_NODE, + CHK_NUM_MLIST_CURSOR, CHK_NUM_QUERY_TEST, CHK_NUM_LOGFILE, CHK_NUM_FILEWRITER, CHK_NUM_THREAD, CHK_NUM_SIMPLE_MUTEX, - CHK_NUM_MESSAGE + CHK_NUM_MESSAGE, + CHK_NUM_RWLOCK, + CHK_NUM_FNAMES, + CHK_NUM_LOGMANAGER, + CHK_NUM_FILE, + CHK_NUM_WRITEBUF } skygw_chk_t; -#define CHK_SLIST(l) { \ + +#define CHK_MLIST(l) { \ + ss_info_dassert((l->mlist_chk_top == CHK_NUM_MLIST && \ + l->mlist_chk_tail == CHK_NUM_MLIST), \ + "Single-linked list structure under- or overflow"); \ + if (l->mlist_first == NULL) { \ + ss_info_dassert(l->mlist_nodecount == 0, \ + "List head is NULL but element counter is not zero."); \ + ss_info_dassert(l->mlist_last == NULL, \ + "List head is NULL but tail has node"); \ + } else { \ + ss_info_dassert(l->mlist_nodecount > 0, \ + "List head has node but element counter is not " \ + "positive."); \ + CHK_MLIST_NODE(l->mlist_first); \ + CHK_MLIST_NODE(l->mlist_last); \ + } \ + if (l->mlist_nodecount == 0) { \ + ss_info_dassert(l->mlist_first == NULL, \ + "Element counter is zero but head has node"); \ + ss_info_dassert(l->mlist_last == NULL, \ + "Element counter is zero but tail has node"); \ + } \ + } + + + +#define CHK_MLIST_NODE(n) { \ + ss_info_dassert((n->mlnode_chk_top == CHK_NUM_MLIST_NODE && \ + n->mlnode_chk_tail == CHK_NUM_MLIST_NODE), \ + "Single-linked list node under- or overflow"); \ + } + +#define CHK_MLIST_CURSOR(c) { \ + ss_info_dassert(c->mlcursor_chk_top == CHK_NUM_MLIST_CURSOR && \ + c->mlcursor_chk_tail == CHK_NUM_MLIST_CURSOR, \ + "List cursor under- or overflow"); \ + ss_info_dassert(c->mlcursor_list != NULL, \ + "List cursor doesn't have list"); \ + ss_info_dassert(c->mlcursor_pos != NULL || \ + (c->mlcursor_pos == NULL && \ + c->mlcursor_list->mlist_first == NULL), \ + "List cursor doesn't have position"); \ + } + +#define CHK_SLIST(l) { \ ss_info_dassert((l->slist_chk_top == CHK_NUM_SLIST && \ l->slist_chk_tail == CHK_NUM_SLIST), \ "Single-linked list structure under- or overflow"); \ @@ -104,6 +163,8 @@ typedef enum skygw_chk_t { } \ } + + #define CHK_SLIST_NODE(n) { \ ss_info_dassert((n->slnode_chk_top == CHK_NUM_SLIST_NODE && \ n->slnode_chk_tail == CHK_NUM_SLIST_NODE), \ @@ -128,10 +189,29 @@ typedef enum skygw_chk_t { "Query test under- or overflow."); \ } -#define CHK_LOGFILE(lf) { \ - ss_info_dassert(lf->lf_chk_top == CHK_NUM_LOGFILE && \ - lf->lf_chk_tail == CHK_NUM_LOGFILE, \ - "Logfile struct under- or overflow"); \ +#define CHK_LOGFILE(lf) { \ + ss_info_dassert(lf->lf_chk_top == CHK_NUM_LOGFILE && \ + lf->lf_chk_tail == CHK_NUM_LOGFILE, \ + "Logfile struct under- or overflow"); \ + ss_info_dassert(lf->lf_logpath != NULL && \ + lf->lf_name_prefix != NULL && \ + lf->lf_name_suffix != NULL && \ + lf->lf_full_name != NULL, \ + "NULL in name variable\n"); \ + ss_info_dassert(lf->lf_id >= LOGFILE_FIRST && \ + lf->lf_id <= LOGFILE_LAST, \ + "Invalid logfile id\n"); \ + ss_info_dassert(lf->lf_writebuf_size > 0, \ + "Error, logfile's writebuf size is zero " \ + "or negative\n"); \ + (lf->lf_chk_top != CHK_NUM_LOGFILE || \ + lf->lf_chk_tail != CHK_NUM_LOGFILE ? \ + FALSE : \ + (lf->lf_logpath == NULL || \ + lf->lf_name_prefix == NULL || \ + lf->lf_name_suffix == NULL || \ + lf->lf_writebuf_size == 0 || \ + lf->lf_full_name == NULL ? FALSE : TRUE)); \ } #define CHK_FILEWRITER(fwr) { \ @@ -150,11 +230,43 @@ typedef enum skygw_chk_t { ss_info_dassert(sm->sm_chk_top == CHK_NUM_SIMPLE_MUTEX && \ sm->sm_chk_tail == CHK_NUM_SIMPLE_MUTEX, \ "Simple mutex struct under- or overflow"); \ - } + } #define CHK_MESSAGE(mes) { \ - ss_info_dassert(mes->mes_chk_top == CHK_NUM_MESSAGE && \ - mes->mes_chk_tail == CHK_NUM_MESSAGE, \ - "Message struct under- or overflow"); \ - } + ss_info_dassert(mes->mes_chk_top == CHK_NUM_MESSAGE && \ + mes->mes_chk_tail == CHK_NUM_MESSAGE, \ + "Message struct under- or overflow"); \ + } + + +#define CHK_MLIST_ISLOCKED(l) { \ + ss_info_dassert((l.mlist_uselock && l.mlist_islocked) || \ + !(l.mlist_uselock || l.mlist_islocked), \ + ("mlist is not locked although it should.")); \ + CHK_MUTEXED_FOR_THR(l.mlist_uselock,l.mlist_rwlock); \ + } + +#define CHK_MUTEXED_FOR_THR(b,l) { \ + ss_info_dassert(!b || \ + (b && (l->srw_rwlock_thr == pthread_self())), \ + "rwlock is not acquired although it should be."); \ + } + +#define CHK_FNAMES_CONF(fn) { \ + ss_info_dassert(fn->fn_chk_top == CHK_NUM_FNAMES && \ + fn->fn_chk_tail == CHK_NUM_FNAMES, \ + "File names confs struct under- or overflow"); \ + } + +#define CHK_LOGMANAGER(lm) { \ + ss_info_dassert(lm->lm_chk_top == CHK_NUM_LOGMANAGER && \ + lm->lm_chk_tail == CHK_NUM_LOGMANAGER, \ + "Log manager struct under- or overflow"); \ + } + +#define CHK_FILE(f) { \ + ss_info_dassert(f->sf_chk_top == CHK_NUM_FILE && \ + f->sf_chk_tail == CHK_NUM_FILE, \ + "File struct under- or overflow"); \ + } #endif /* SKYGW_DEBUG_H */ diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 7dc2f686e..c04a42685 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include "skygw_debug.h" #include "skygw_types.h" @@ -31,25 +32,16 @@ struct slist_st { }; struct slist_cursor_st { - skygw_chk_t slcursor_chk_top; - slist_t* slcursor_list; - slist_node_t* slcursor_pos; - skygw_chk_t slcursor_chk_tail; + skygw_chk_t slcursor_chk_top; + slist_t* slcursor_list; + slist_node_t* slcursor_pos; + skygw_chk_t slcursor_chk_tail; }; -struct simple_mutex_st { - skygw_chk_t sm_chk_top; - pthread_mutex_t sm_mutex; - pthread_t sm_owner; - bool sm_locked; - bool sm_enabled; - char* sm_name; - skygw_chk_t sm_chk_tail; -}; - struct skygw_thread_st { skygw_chk_t sth_chk_top; bool sth_must_exit; + simple_mutex_t* sth_mutex; pthread_t sth_parent; pthread_t sth_thr; int sth_errno; @@ -68,8 +60,33 @@ struct skygw_message_st { skygw_chk_t mes_chk_tail; }; +struct skygw_file_st { + skygw_chk_t sf_chk_top; + char* sf_fname; + FILE* sf_file; + skygw_chk_t sf_chk_tail; +}; + /** End of structs and types */ +#if defined(MLIST) + + +static mlist_node_t* mlist_node_init(void* data, mlist_cursor_t* cursor); + +static mlist_node_t* mlist_node_get_next( + mlist_node_t* curr_node); +static mlist_node_t* mlist_get_first( + mlist_t* list); + +static mlist_cursor_t* mlist_get_cursor( + mlist_t* list); + +static void mlist_add_node_nomutex( + mlist_t* list, + mlist_node_t* newnode); + +#endif /* MLIST */ static slist_cursor_t* slist_cursor_init( slist_t* list); @@ -94,8 +111,391 @@ static slist_node_t* slist_get_first( static slist_cursor_t* slist_get_cursor( slist_t* list); +static bool file_write_header(skygw_file_t* file); +static void simple_mutex_free_memory(simple_mutex_t* sm); +static void mlist_free_memory(mlist_t* ml, char* name); +static void thread_free_memory(skygw_thread_t* th, char* name); + + /** End of static function declarations */ +/** mutexed list, mlist */ + +#if defined(MLIST) + +int skygw_rwlock_rdlock( + skygw_rwlock_t* rwlock) +{ + int err = pthread_rwlock_rdlock(rwlock->srw_rwlock); + + if (err == 0) { + rwlock->srw_rwlock_thr = pthread_self(); + } else { + rwlock->srw_rwlock_thr = 0; + ss_dfprintf(stderr, "pthread_rwlock_rdlock : %s\n", strerror(err)); + } + return err; +} + +int skygw_rwlock_wrlock( + skygw_rwlock_t* rwlock) +{ + int err = pthread_rwlock_wrlock(rwlock->srw_rwlock); + + if (err == 0) { + rwlock->srw_rwlock_thr = pthread_self(); + } else { + rwlock->srw_rwlock_thr = 0; + ss_dfprintf(stderr, "pthread_rwlock_wrlock : %s\n", strerror(err)); + } + return err; +} + +int skygw_rwlock_unlock( + skygw_rwlock_t* rwlock) +{ + int err = pthread_rwlock_rdlock(rwlock->srw_rwlock); + + if (err == 0) { + rwlock->srw_rwlock_thr = 0; + } else { + ss_dfprintf(stderr, "pthread_rwlock_unlock : %s\n", strerror(err)); + } + return err; +} + + + +int skygw_rwlock_destroy( + skygw_rwlock_t* rwlock) +{ + int err = pthread_rwlock_destroy(rwlock->srw_rwlock); + + if (err == 0) { + rwlock->srw_rwlock_thr = 0; + rwlock->srw_rwlock = NULL; + } else { + ss_dfprintf(stderr, "pthread_rwlock_destroy : %s\n", strerror(err)); + } + return err; +} + +int skygw_rwlock_init( + skygw_rwlock_t** rwlock) +{ + skygw_rwlock_t* rwl; + int err; + + rwl = (skygw_rwlock_t *)calloc(1, sizeof(skygw_rwlock_t)); + rwl->srw_chk_top = CHK_NUM_RWLOCK; + rwl->srw_chk_tail = CHK_NUM_RWLOCK; + err = pthread_rwlock_init(rwl->srw_rwlock, NULL); + ss_dassert(err == 0); + + if (err != 0) { + ss_dfprintf(stderr, + "Creating pthread_rwlock failed : %s\n", + strerror(err)); + goto return_err; + } +return_err: + return err; +} + + +/** + * @node Create a list with rwlock and optional read-only cursor + * + * Parameters: + * @param listp - + * + * + * @param cursor - + * + * + * @param name - + * + * + * @return Address of mlist_t struct. + * + * + * @details Cursor must protect its reads with read lock, and after acquiring + * read lock reader must check whether the list is deleted (mlist_deleted). + * + */ +mlist_t* mlist_init( + mlist_t* listp, + mlist_cursor_t** cursor, + char* name) +{ + mlist_cursor_t* c; + mlist_t* list; + + if (cursor != NULL) { + ss_dassert(*cursor == NULL); + } + /** listp is not NULL if caller wants flat list */ + if (listp == NULL) { + list = (mlist_t*)calloc(1, sizeof(mlist_t)); + } else { + /** Caller wants list flat, memory won't be freed */ + list = listp; + list->mlist_flat = TRUE; + } + ss_dassert(list != NULL); + + if (list == NULL) { + fprintf(stderr, "Allocating memory for mlist failed\n"); + mlist_free_memory(list, name); + goto return_list; + } + list->mlist_chk_top = CHK_NUM_MLIST; + list->mlist_chk_tail = CHK_NUM_MLIST; + + if (name != NULL) { + list->mlist_name = name; + } + /** Create mutex, return NULL if fails. */ + if (simple_mutex_init( + &list->mlist_mutex, + strdup("writebuf mutex")) == NULL) + { + ss_dfprintf(stderr, "Creating rwlock for mlist failed\n"); + mlist_free_memory(list, name); + list = NULL; + goto return_list; + } + + /** Create cursor for reading the list */ + if (cursor != NULL) { + c = mlist_cursor_init(list); + + if (c == NULL) { + simple_mutex_done(&list->mlist_mutex); + mlist_free_memory(list, name); + goto return_list; + } + CHK_MLIST_CURSOR(c); + *cursor = c; + } + CHK_MLIST(list); + +return_list: + return list; +} + +/** + * @node Free mlist memory allocations. name must be explicitly + * set if mlist has one. + * + * Parameters: + * @param ml - + * + * + * @param name - + * + * + * @return void + * + * + * @details (write detailed description here) + * + */ +static void mlist_free_memory( + mlist_t* ml, + char* name) +{ + mlist_node_t* node; + + /** name */ + if (name != NULL) { + free(name); + } + if (ml != NULL) { + /** list data */ + while(ml->mlist_first != NULL) { + /** Scan list and free nodes and data inside nodes */ + node = ml->mlist_first->mlnode_next; + mlist_node_done(ml->mlist_first); + ml->mlist_first = node; + } + + /** list structure */ + if (!ml->mlist_flat) { + free(ml); + } + } +} + +void mlist_node_done( + mlist_node_t* n) +{ + CHK_MLIST_NODE(n); + if (n->mlnode_data != NULL) { + free(n->mlnode_data); + } + free(n); +} + +void* mlist_node_get_data( + mlist_node_t* node) +{ + CHK_MLIST_NODE(node); + return node->mlnode_data; +} + +mlist_cursor_t* mlist_cursor_init( + mlist_t* list) +{ + CHK_MLIST(list); + mlist_cursor_t* c; + + /** acquire shared lock to the list */ + simple_mutex_lock(&list->mlist_mutex, TRUE); + + c = (mlist_cursor_t *)calloc(1, sizeof(mlist_cursor_t)); + + if (c == NULL) { + goto return_cursor; + } + c->mlcursor_chk_top = CHK_NUM_MLIST_CURSOR; + c->mlcursor_chk_tail = CHK_NUM_MLIST_CURSOR; + c->mlcursor_list = list; + + /** Set cursor position if list is not empty */ + if (list->mlist_first != NULL) { + c->mlcursor_pos = list->mlist_first; + } + simple_mutex_unlock(&list->mlist_mutex); + + CHK_MLIST_CURSOR(c); + +return_cursor: + return c; +} + + +/** + * @node Mark list as deleted and free the memory. + * + * Parameters: + * @param list - + * + * + * @return void + * + * + * @details (write detailed description here) + * + */ +void mlist_done( + mlist_t* list) +{ + CHK_MLIST(list); + simple_mutex_lock(&list->mlist_mutex, TRUE); + list->mlist_deleted = TRUE; + simple_mutex_unlock(&list->mlist_mutex); + + simple_mutex_done(&list->mlist_mutex); + mlist_free_memory(list, list->mlist_name); +} + + +void* mlist_cursor_get_data_nomutex( + mlist_cursor_t* mc) +{ + CHK_MLIST_CURSOR(mc); + return (mc->mlcursor_pos->mlnode_data); +} + +void mlist_add_data_nomutex( + mlist_t* list, + void* data) +{ + mlist_add_node_nomutex(list, mlist_node_init(data, NULL)); +} + + +static mlist_node_t* mlist_node_init( + void* data, + mlist_cursor_t* cursor) +{ + mlist_node_t* node; + + node = (mlist_node_t*)calloc(1, sizeof(mlist_node_t)); + node->mlnode_chk_top = CHK_NUM_MLIST_NODE; + node->mlnode_chk_tail = CHK_NUM_MLIST_NODE; + node->mlnode_data = data; + CHK_MLIST_NODE(node); + + if (cursor != NULL) { + cursor->mlcursor_pos = node; + } + + return node; +} + +static void mlist_add_node_nomutex( + mlist_t* list, + mlist_node_t* newnode) +{ + + CHK_MLIST(list); +// CHK_MLIST_ISLOCKED(list); + CHK_MLIST_NODE(newnode); + ss_dassert(!list->mlist_deleted); + + /** Find location for new node */ + if (list->mlist_last != NULL) { + ss_dassert(!list->mlist_last->mlnode_deleted); + CHK_MLIST_NODE(list->mlist_last); + CHK_MLIST_NODE(list->mlist_first); + ss_dassert(list->mlist_last->mlnode_next == NULL); + list->mlist_last->mlnode_next = newnode; + } else { + list->mlist_first = newnode; + } + list->mlist_last = newnode; + newnode->mlnode_list = list; + list->mlist_nodecount += 1; + CHK_MLIST(list); +} + + + +bool mlist_cursor_move_to_first( + mlist_cursor_t* mc) +{ + bool succp = FALSE; + mlist_t* list; + + CHK_MLIST_CURSOR(mc); + list = mc->mlcursor_list; + CHK_MLIST(list); + simple_mutex_lock(&list->mlist_mutex, TRUE); + + if (mc->mlcursor_list->mlist_deleted) { + return FALSE; + } + /** Set position point to first node */ + mc->mlcursor_pos = list->mlist_first; + + if (mc->mlcursor_pos != NULL) { + CHK_MLIST_NODE(mc->mlcursor_pos); + succp = TRUE; + } + simple_mutex_unlock(&list->mlist_mutex); + return succp; +} + + + +#endif /* MLIST */ + + +/** End of mlist */ + + static slist_t* slist_init_ex( bool create_cursors) @@ -113,6 +513,7 @@ static slist_t* slist_init_ex( return list; } + static slist_node_t* slist_node_init( void* data, slist_cursor_t* cursor) @@ -147,7 +548,7 @@ static void slist_add_node( list->slist_tail->slnode_next = node; } else { list->slist_head = node; - } + } list->slist_tail = node; node->slnode_list = list; list->slist_nelems += 1; @@ -155,6 +556,8 @@ static void slist_add_node( } + + static slist_node_t* slist_node_get_next( slist_node_t* curr_node) { @@ -180,28 +583,6 @@ static slist_node_t* slist_get_first( return NULL; } -static slist_cursor_t* slist_cursor_init( - slist_t* list) -{ - CHK_SLIST(list); - slist_cursor_t* c; - - c = (slist_cursor_t *)calloc(1, sizeof(slist_cursor_t)); - c->slcursor_chk_top = CHK_NUM_SLIST_CURSOR; - c->slcursor_chk_tail = CHK_NUM_SLIST_CURSOR; - c->slcursor_list = list; - - /** Set cursor position is list is not empty */ - if (list->slist_head != NULL) { - list->slist_head->slnode_cursor_refcount += 1; - c->slcursor_pos = list->slist_head; - } - /** Add cursor to cursor list */ - slist_add_node(list->slist_cursors_list, slist_node_init(c, NULL)); - - CHK_SLIST_CURSOR(c); - return c; -} static slist_cursor_t* slist_get_cursor( slist_t* list) @@ -215,9 +596,32 @@ static slist_cursor_t* slist_get_cursor( } +static slist_cursor_t* slist_cursor_init( + slist_t* list) +{ + CHK_SLIST(list); + slist_cursor_t* c; + + c = (slist_cursor_t *)calloc(1, sizeof(slist_cursor_t)); + c->slcursor_chk_top = CHK_NUM_SLIST_CURSOR; + c->slcursor_chk_tail = CHK_NUM_SLIST_CURSOR; + c->slcursor_list = list; + /** Set cursor position is list is not empty */ + if (list->slist_head != NULL) { + list->slist_head->slnode_cursor_refcount += 1; + c->slcursor_pos = list->slist_head; + } + /** Add cursor to cursor list */ + slist_add_node(list->slist_cursors_list, slist_node_init(c, NULL)); + + CHK_SLIST_CURSOR(c); + return c; +} + /** - * @node Create a cursor and a list with cursors supported + * @node Create a cursor and a list with cursors supported. 19.6.2013 : + * supports only cursor per list. * * Parameters: * @param void - @@ -245,6 +649,7 @@ slist_cursor_t* slist_init(void) + /** * @node moves cursor to the first node of list. * @@ -362,7 +767,8 @@ void slcursor_add_data( CHK_SLIST(list); CHK_SLIST_CURSOR(c); } - + + void slist_done( slist_cursor_t* c) { @@ -379,6 +785,8 @@ void slist_done( free(c->slcursor_list); free(c); } + + /** End of list implementation */ /** @@ -404,11 +812,21 @@ skygw_thread_t* skygw_thread_init( { skygw_thread_t* th = (skygw_thread_t *)calloc(1, sizeof(skygw_thread_t)); + + if (th == NULL) { + fprintf(stderr, "FATAL: memory allocation for thread failed\n"); + } + ss_dassert(th != NULL); th->sth_chk_top = CHK_NUM_THREAD; th->sth_chk_tail = CHK_NUM_THREAD; th->sth_parent = pthread_self(); th->sth_state = THR_INIT; th->sth_name = name; + th->sth_mutex = simple_mutex_init(NULL, strdup(name)); + + if (th->sth_mutex == NULL) { + thread_free_memory(th, th->sth_name); + } th->sth_thrfun = sth_thrfun; th->sth_data = data; CHK_THREAD(th); @@ -416,8 +834,42 @@ skygw_thread_t* skygw_thread_init( return th; } -void skygw_thread_start( - skygw_thread_t* thr) +static void thread_free_memory( + skygw_thread_t* th, + char* name) +{ + if (name != NULL) { + free(name); + } + free(th); +} + +/** + * @node Release skygw_thread data except filewriter. + * + * Parameters: + * @param th - + * + * + * @return void + * + * + * @details (write detailed description here) + * + */ +void skygw_thread_done( + skygw_thread_t* th) +{ + CHK_THREAD(th); + ss_dassert(th->sth_state == THR_STOPPED); + th->sth_state = THR_DONE; + simple_mutex_done(th->sth_mutex); + thread_free_memory(th, th->sth_name); +} + + +int skygw_thread_start( + skygw_thread_t* thr) { int err; @@ -426,6 +878,7 @@ void skygw_thread_start( NULL, thr->sth_thrfun, thr); + ss_dassert(err == 0); if (err != 0) { fprintf(stderr, @@ -433,11 +886,15 @@ void skygw_thread_start( "errno %d : %s\n", err, strerror(errno)); - perror("file writer thread : "); + goto return_err; } ss_dfprintf(stderr, "Started %s thread\n", thr->sth_name); + +return_err: + return err; } + skygw_thr_state_t skygw_thread_get_state( skygw_thread_t* thr) { @@ -446,13 +903,68 @@ skygw_thr_state_t skygw_thread_get_state( } +/** + * @node Update thread state + * + * Parameters: + * @param thr - + * + * + * @param state - + * + * + * @return void + * + * + * @details Thread must check state with mutex. + * + */ void skygw_thread_set_state( skygw_thread_t* thr, skygw_thr_state_t state) { CHK_THREAD(thr); - ss_dassert(!thr->sth_must_exit); + simple_mutex_lock(thr->sth_mutex, TRUE); thr->sth_state = state; + simple_mutex_unlock(thr->sth_mutex); +} + +/** + * @node Set exit flag for thread from other thread + * + * Parameters: + * @param thr - + * + * + * @return + * + * + * @details This call informs thread about exit flag and waits the response. + * + */ +bool skygw_thread_set_exitflag( + skygw_thread_t* thr, + skygw_message_t* sendmes, + skygw_message_t* recmes) +{ + bool succp = FALSE; + + CHK_THREAD(thr); + CHK_MESSAGE(sendmes); + CHK_MESSAGE(recmes); + + simple_mutex_lock(thr->sth_mutex, TRUE); + succp = !thr->sth_must_exit; + thr->sth_must_exit = TRUE; + simple_mutex_unlock(thr->sth_mutex); + + /** Inform thread and wait for response */ + if (succp) { + skygw_message_send(sendmes); + skygw_message_wait(recmes); + } + ss_dassert(thr->sth_state == THR_STOPPED); + return succp; } void* skygw_thread_get_data( @@ -465,18 +977,46 @@ void* skygw_thread_get_data( bool skygw_thread_must_exit( skygw_thread_t* thr) { + CHK_THREAD(thr); return thr->sth_must_exit; } +/** + * @node Create a simple_mutex structure which encapsulates pthread_mutex. + * + * Parameters: + * @param name - + * + * + * @return + * + * + * @details If mutex is flat, sm_enabled can be read if the memory is not freed. + * If flat mutex exists, sm_enabled is TRUE. + * If mutex allocates its own memory, the pointer is NULL if mutex doesn't + * exist. + * + */ simple_mutex_t* simple_mutex_init( - char* name) + simple_mutex_t* mutexptr, + char* name) { - int err; - + int err; simple_mutex_t* sm; - sm = (simple_mutex_t *)calloc(1, sizeof(simple_mutex_t)); + /** Copy pointer only if flat, allocate memory otherwise. */ + if (mutexptr != NULL) { + sm = mutexptr; + sm->sm_flat = TRUE; + } else { + sm = (simple_mutex_t *)calloc(1, sizeof(simple_mutex_t)); + } ss_dassert(sm != NULL); + sm->sm_chk_top = CHK_NUM_SIMPLE_MUTEX; + sm->sm_chk_tail = CHK_NUM_SIMPLE_MUTEX; + sm->sm_name = name; + + /** Create pthread mutex */ err = pthread_mutex_init(&sm->sm_mutex, NULL); if (err != 0) { @@ -487,14 +1027,21 @@ simple_mutex_t* simple_mutex_init( err, strerror(errno)); perror("simple_mutex : "); - sm = NULL; + + /** Write zeroes if flat, free otherwise. */ + if (sm->sm_flat) { + memset(sm, 0, sizeof(sm)); + } else { + simple_mutex_free_memory(sm); + sm = NULL; + } + goto return_sm; } - sm->sm_chk_top = CHK_NUM_SIMPLE_MUTEX; - sm->sm_chk_tail = CHK_NUM_SIMPLE_MUTEX; - sm->sm_name = strdup(name); sm->sm_enabled = TRUE; CHK_SIMPLE_MUTEX(sm); ss_dfprintf(stderr, "Initialized simple mutex %s.\n", name); + +return_sm: return sm; } @@ -517,6 +1064,12 @@ int simple_mutex_done( } err = pthread_mutex_destroy(&sm->sm_mutex); + if (err != 0) { + goto return_err; + } + simple_mutex_free_memory(sm); + + return_err: if (err != 0) { fprintf(stderr, @@ -530,6 +1083,17 @@ return_err: return err; } +static void simple_mutex_free_memory( + simple_mutex_t* sm) +{ + if (sm->sm_name != NULL) { + free(sm->sm_name); + } + if (!sm->sm_flat) { + free(sm); + } +} + int simple_mutex_lock( simple_mutex_t* sm, bool block) @@ -550,6 +1114,9 @@ int simple_mutex_lock( err, strerror(errno)); perror("simple_mutex : "); + } else { + sm->sm_locked = TRUE; + sm->sm_lock_thr = pthread_self(); } return err; } @@ -569,6 +1136,9 @@ int simple_mutex_unlock( err, strerror(errno)); perror("simple_mutex : "); + } else { + sm->sm_locked = FALSE; + sm->sm_lock_thr = 0; } return err; } @@ -754,3 +1324,137 @@ void skygw_message_reset( return_mes_rc: ss_dassert(err == 0); } + +static bool file_write_header( + skygw_file_t* file) +{ + bool succp = FALSE; + size_t wbytes1; + size_t wbytes2; + size_t len1; + size_t len2; + const char* header_buf1; + char* header_buf2 = NULL; + time_t* t; + struct tm* tm; + + t = (time_t *)malloc(sizeof(time_t)); + tm = (struct tm *)malloc(sizeof(struct tm)); + *t = time(NULL); + *tm = *localtime(t); + + CHK_FILE(file); + header_buf1 = "\n----------\nSkySQL Gateway "; + + header_buf2 = strdup(asctime(tm)); + + if (header_buf2 == NULL) { + goto return_succp; + } + len1 = strlen(header_buf1); + len2 = strlen(header_buf2); + + wbytes1=fwrite((void*)header_buf1, len1, 1, file->sf_file); + wbytes2=fwrite((void*)header_buf2, len2, 1, file->sf_file); + + if (wbytes1 != 1 || wbytes2 != 1) { + fprintf(stderr, + "Writing header %s %s to %s failed.\n", + header_buf1, + header_buf2, + file->sf_fname); + perror("Logfile header write.\n"); + goto return_succp; + } + + CHK_FILE(file); + + succp = TRUE; +return_succp: + free(header_buf2); + free(t); + free(tm); + return succp; +} + +bool skygw_file_write( + skygw_file_t* file, + void* data, + size_t nbytes) +{ + size_t nwritten; + bool succp = FALSE; + + CHK_FILE(file); + nwritten = fwrite(data, nbytes, 1, file->sf_file); + + if (nwritten != 1) { + fprintf(stderr, + "Writing header %s to %s failed.\n", + (char *)data, + file->sf_fname); + perror("Logfile write.\n"); + goto return_succp; + } + succp = TRUE; + CHK_FILE(file); +return_succp: + return succp; +} + +skygw_file_t* skygw_file_init( + char* fname) + +{ + skygw_file_t* file; + file = (skygw_file_t *)calloc(1, sizeof(skygw_file_t)); + + if (file == NULL) { + fprintf(stderr, "Memory allocation for skygw file failed.\n"); + perror("SkyGW file allocation\n"); + } + ss_dassert(file != NULL); + file->sf_chk_top = CHK_NUM_FILE; + file->sf_chk_tail = CHK_NUM_FILE; + file->sf_fname = strdup(fname); + file->sf_file = fopen(file->sf_fname, "a"); + + if (file->sf_file == NULL) { + fprintf(stderr, "Opening file %s failed.\n", file->sf_fname); + perror("SkyGW file open\n"); + free(file); + file = NULL; + goto return_file; + } + + file_write_header(file); + CHK_FILE(file); + fprintf(stderr, "Opened %s\n", file->sf_fname); +return_file: + ss_dassert(file->sf_file != NULL); + + return file; +} + + +void skygw_file_done( + skygw_file_t* file) +{ + int fd; + int err; + CHK_FILE(file); + + fd = fileno(file->sf_file); + fsync(fd); + err = fclose(file->sf_file); + + if (err != 0) { + fprintf(stderr, + "Closing file %s failed : %s.\n", + file->sf_fname, + strerror(err)); + } + ss_dassert(err == 0); + free(file->sf_fname); + free(file); +} diff --git a/utils/skygw_utils.h b/utils/skygw_utils.h index 7bbdebb96..757d37873 100644 --- a/utils/skygw_utils.h +++ b/utils/skygw_utils.h @@ -1,21 +1,77 @@ #if !defined(SKYGW_UTILS_H) #define SKYGW_UTILS_H +#define MLIST +#define MIN(a,b) (a