const char *test_zlib_vmbuf() { ssize_t data_size; for (data_size = DATA_SIZE - 5; data_size < DATA_SIZE + 5; ++data_size) { int data[data_size]; int i; for (i = 0; i < data_size; ++i) { data[i] = i; } size_t expected_size = compressBound(sizeof(data)); uint8_t *expected = malloc(expected_size); compress(expected, &expected_size, (uint8_t *)data, sizeof(data)); struct vmbuf compressed = VMBUF_INITIALIZER; vmbuf_init(&compressed, 128); if (0 > vmbuf_deflate_ptr(data, sizeof(data), &compressed)) mu_fail("vmbuf_deflate_ptr() failed"); // printf("%zu ==> %zu (%zu)\n", sizeof(data), vmbuf_wlocpos(&compressed), expected_size); mu_assert_eqi(expected_size + 12 /* gzip header and trailer */, vmbuf_wlocpos(&compressed)); struct vmbuf decompressed = VMBUF_INITIALIZER; vmbuf_init(&decompressed, 128); vmbuf_inflate2(&compressed, &decompressed); mu_assert_eqi(vmbuf_wlocpos(&decompressed), sizeof(data)); typeof(data[0]) *decompressed_buf = vmbuf_mem(&decompressed); for (i = 0; i < data_size; ++i) mu_assert_eqi_idx(i, data[i], decompressed_buf[i]); vmbuf_free(&compressed); vmbuf_free(&decompressed); free(expected); } return NULL; }
static inline void _init_alloc(z_stream *strm) { static struct vmbuf zalloc_buf = VMBUF_INITIALIZER; vmbuf_init(&zalloc_buf, 1024*1024*2); strm->zalloc = _zalloc; strm->zfree = _zfree; strm->opaque = &zalloc_buf; }
int mysql_pool_init() { if (0 > hashtable_init(&ht_idle_connections, 128)) return -1; if (0 > vmbuf_init(&misc, 4096)) return -1; return 0; }
int vmbuf_deflate3(struct vmbuf *buf, int level) { static struct vmbuf outbuf = VMBUF_INITIALIZER; vmbuf_init(&outbuf, vmbuf_ravail(buf)); if (0 > vmbuf_deflate4(buf, &outbuf, level)) return -1; vmbuf_swap(&outbuf, buf); return 0; }
int vmbuf_inflate(struct vmbuf *buf) { static struct vmbuf outbuf = VMBUF_INITIALIZER; vmbuf_init(&outbuf, vmbuf_ravail(buf)); if (0 > vmbuf_inflate2(buf, &outbuf)) return -1; vmbuf_swap(&outbuf, buf); return 0; }
struct http_client_context *http_client_pool_create_client(struct http_client_pool *http_client_pool, struct in_addr addr, uint16_t port, struct ribs_context *rctx) { int cfd; struct http_client_key key = { .addr = addr, .port = port }; uint32_t ofs = hashtable_lookup(&ht_persistent_clients, &key, sizeof(struct http_client_key)); struct list *head; if (ofs > 0 && !list_empty(head = client_heads + *(uint32_t *)hashtable_get_val(&ht_persistent_clients, ofs))) { struct list *client = list_pop_head(head); cfd = client - client_chains; } else { cfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (0 > cfd) return LOGGER_PERROR("socket"), NULL; const int option = 1; if (0 > setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))) return LOGGER_PERROR("setsockopt SO_REUSEADDR"), close(cfd), NULL; if (0 > setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option))) return LOGGER_PERROR("setsockopt TCP_NODELAY"), close(cfd), NULL; struct sockaddr_in saddr = { .sin_family = AF_INET, .sin_port = htons(port), .sin_addr = addr }; if (0 > connect(cfd, (struct sockaddr *)&saddr, sizeof(saddr)) && EINPROGRESS != errno) return LOGGER_PERROR("connect"), close(cfd), NULL; struct epoll_event ev; ev.events = EPOLLIN | EPOLLOUT | EPOLLET; ev.data.fd = cfd; if (0 > epoll_ctl(ribs_epoll_fd, EPOLL_CTL_ADD, cfd, &ev)) return LOGGER_PERROR("epoll_ctl"), close(cfd), NULL; } struct ribs_context *new_ctx = ctx_pool_get(&http_client_pool->ctx_pool); new_ctx->fd = cfd; new_ctx->data.ptr = http_client_pool; struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + cfd; fd_data->ctx = new_ctx; ribs_makecontext(new_ctx, rctx ? rctx : current_ctx, http_client_fiber_main); struct http_client_context *cctx = (struct http_client_context *)new_ctx->reserved; cctx->key = (struct http_client_key) { .addr = addr, .port = port }; vmbuf_init(&cctx->request, 4096); vmbuf_init(&cctx->response, 4096); timeout_handler_add_fd_data(&http_client_pool->timeout_handler, fd_data); return cctx; }
static void begin_log_line(const char *msg_class) { struct tm tm, *tmp; struct timeval tv; gettimeofday(&tv, NULL); tmp = localtime_r(&tv.tv_sec, &tm); vmbuf_init(&log_buf, 4096); vmbuf_strftime(&log_buf, "%Y-%m-%d %H:%M:%S", tmp); vmbuf_sprintf(&log_buf, ".%03d.%03d %d %s ", tv.tv_usec / 1000, tv.tv_usec % 1000, getpid(), msg_class); }
int ds_var_field_writer_init2(struct ds_var_field_writer *dsvfw, const char *filename, int64_t type, int64_t element_type) { unlink(filename); if (0 > vmbuf_init(&dsvfw->ofs_table, 4096) || 0 > file_writer_init(&dsvfw->data, filename)) return -1; struct ds_var_field_header header = { -type, -1 , element_type }; /* set type to negative value so load fails if not finalized */ if (0 > file_writer_write(&dsvfw->data, &header, sizeof(header))) return -1; return 0; }
int mysql_helper_real_connect(struct mysql_helper *mysql_helper, struct mysql_login_info *login_info){ if (NULL == mysql_real_connect(&mysql_helper->mysql, login_info->host, login_info->user, login_info->pass, login_info->db, login_info->port, NULL, 0)) return report_error(mysql_helper); my_bool b_flag = 1; if (0 != mysql_options(&mysql_helper->mysql, MYSQL_REPORT_DATA_TRUNCATION, (const char *)&b_flag)) return report_error(mysql_helper); b_flag = 1; if (0 != mysql_options(&mysql_helper->mysql, MYSQL_OPT_RECONNECT, (const char *)&b_flag)) return report_error(mysql_helper); VMBUF_INIT(mysql_helper->buf); vmbuf_init(&mysql_helper->buf, 16384); VMBUF_INIT(mysql_helper->time_buf); vmbuf_init(&mysql_helper->time_buf, 4096); /* maximum of 102 timestamp bindings */ return 0; }
/* the main */ int main(void){ /* the base directory to be used/created */ const char *base_dir = "data"; /* the default test db connection string */ char DEFAULT_TEST_DB_CONN_STR[] = "@localhost"; char *test_db = DEFAULT_TEST_DB_CONN_STR; /* create the target directory where the dump(s) will be stored */ if( 0 > mkdir_recursive(base_dir)) { LOGGER_ERROR("Failed to create target directory: [%s]", base_dir); exit(EXIT_FAILURE); } LOGGER_INFO("target directory: [%s]", base_dir); /* the mysql_login_info struct will be used to access the database */ struct mysql_login_info login_db_test; /* parse the test_db connection string in case of any errors */ /* not necessarily used in this example unless you use a database connection string other than '@localhost' */ if (0 > ribs_mysql_parse_db_conn_str(test_db, &login_db_test)) { LOGGER_ERROR("failed to parse DB connection string: [%s]", test_db); exit(EXIT_FAILURE); } login_db_test.db = DB_TEST; /* initialize the event loop */ if( 0 > epoll_worker_init()) { LOGGER_ERROR("epoll_worker_init failed"); exit(EXIT_FAILURE); } /* initialize the client pool */ http_client_pool_init(&client_pool, 10, 10); /* initialize the query buffer used in dump_test_data() */ struct vmbuf query = VMBUF_INITIALIZER; vmbuf_init(&query, 65536); /* dump the test db data table into its target directory */ if( 0 > dump_test_data(base_dir, login_db_test, &query)) { LOGGER_ERROR("Faied to dump data from test db"); exit(EXIT_FAILURE); } /* return when successful */ LOGGER_INFO("completed successfully"); exit(EXIT_SUCCESS); return 0; }
void http_server_fiber_main(void) { struct http_server_context *ctx = http_server_get_context(); struct http_server *server = ctx->server; int fd = ctx->fd; char *URI; char *headers; char *content; size_t content_length; int res; ctx->persistent = 0; vmbuf_init(&ctx->request, server->init_request_size); vmbuf_init(&ctx->header, server->init_header_size); vmbuf_init(&ctx->payload, server->init_payload_size); size_t max_req_size = server->max_req_size; for (;; http_server_yield()) { READ_FROM_SOCKET(); if (vmbuf_wlocpos(&ctx->request) > MIN_HTTP_REQ_SIZE) break; } do { if (0 == SSTRNCMP(GET, vmbuf_data(&ctx->request)) || 0 == SSTRNCMP(HEAD, vmbuf_data(&ctx->request))) { /* GET or HEAD */ while (0 != SSTRNCMP(CRLFCRLF, vmbuf_wloc(&ctx->request) - SSTRLEN(CRLFCRLF))) { http_server_yield(); READ_FROM_SOCKET(); } /* make sure the string is \0 terminated */ /* this will overwrite the first CR */ *(vmbuf_wloc(&ctx->request) - SSTRLEN(CRLFCRLF)) = 0; char *p = vmbuf_data(&ctx->request); ctx->persistent = check_persistent(p); URI = strchrnul(p, ' '); /* can't be NULL GET and HEAD constants have space at the end */ *URI = 0; ++URI; // skip the space p = strchrnul(URI, '\r'); /* HTTP/1.0 */ headers = p; if (0 != *headers) /* are headers present? */ headers += SSTRLEN(CRLF); /* skip the new line */ *p = 0; p = strchrnul(URI, ' '); /* truncate the version part */ *p = 0; /* \0 at the end of URI */ ctx->content = NULL; ctx->content_len = 0; /* minimal parsing and call user function */ http_server_process_request(URI, headers); } else if (0 == SSTRNCMP(POST, vmbuf_data(&ctx->request)) || 0 == SSTRNCMP(PUT, vmbuf_data(&ctx->request))) { /* POST or PUT */ for (;;) { *vmbuf_wloc(&ctx->request) = 0; /* wait until we have the header */ if (NULL != (content = strstr(vmbuf_data(&ctx->request), CRLFCRLF))) break; http_server_yield(); READ_FROM_SOCKET(); } *content = 0; /* terminate at the first CR like in GET */ content += SSTRLEN(CRLFCRLF); size_t content_ofs = content - vmbuf_data(&ctx->request); if (strstr(vmbuf_data(&ctx->request), EXPECT_100)) { vmbuf_sprintf(&ctx->header, "%s %s\r\n\r\n", HTTP_SERVER_VER, HTTP_STATUS_100); if (0 > vmbuf_write(&ctx->header, fd)) { close(fd); return; } vmbuf_reset(&ctx->header); } ctx->persistent = check_persistent(vmbuf_data(&ctx->request)); /* parse the content length */ char *p = strcasestr(vmbuf_data(&ctx->request), CONTENT_LENGTH); if (NULL == p) { http_server_response(HTTP_STATUS_411, HTTP_CONTENT_TYPE_TEXT_PLAIN); break; } p += SSTRLEN(CONTENT_LENGTH); content_length = atoi(p); for (;;) { if (content_ofs + content_length <= vmbuf_wlocpos(&ctx->request)) break; http_server_yield(); READ_FROM_SOCKET(); } p = vmbuf_data(&ctx->request); URI = strchrnul(p, ' '); /* can't be NULL PUT and POST constants have space at the end */ *URI = 0; ++URI; /* skip the space */ p = strchrnul(URI, '\r'); /* HTTP/1.0 */ headers = p; if (0 != *headers) /* are headers present? */ headers += SSTRLEN(CRLF); /* skip the new line */ *p = 0; p = strchrnul(URI, ' '); /* truncate http version */ *p = 0; /* \0 at the end of URI */ ctx->content = vmbuf_data_ofs(&ctx->request, content_ofs); *(ctx->content + content_length) = 0; ctx->content_len = content_length; /* minimal parsing and call user function */ http_server_process_request(URI, headers); } else { http_server_response(HTTP_STATUS_501, HTTP_CONTENT_TYPE_TEXT_PLAIN); break; } } while(0); if (vmbuf_wlocpos(&ctx->header) > 0) { epoll_worker_resume_events(fd); http_server_write(); } if (ctx->persistent) { struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + fd; fd_data->ctx = server->idle_ctx; timeout_handler_add_fd_data(&server->timeout_handler, fd_data); } else close(fd); }
int mysql_dumper_dump(struct mysql_login_info *mysql_login_info, const char *outputdir, const char *dbname, const char *tablename, const char *query, size_t query_len, struct mysql_dumper_type *types) { MYSQL mysql; MYSQL_STMT *stmt = NULL; mysql_init(&mysql); my_bool b_flag = 1; if (0 != mysql_options(&mysql, MYSQL_OPT_RECONNECT, (const char *)&b_flag)) return report_error(&mysql); if (NULL == mysql_real_connect(&mysql, mysql_login_info->host, mysql_login_info->user, mysql_login_info->pass, mysql_login_info->db, mysql_login_info->port, NULL, CLIENT_COMPRESS)) return report_error(&mysql); b_flag = 0; if (0 != mysql_options(&mysql, MYSQL_REPORT_DATA_TRUNCATION, (const char *)&b_flag)) return report_error(&mysql); stmt = mysql_stmt_init(&mysql); if (!stmt) return report_error(&mysql); if (0 != mysql_stmt_prepare(stmt, query, query_len)) return report_stmt_error(&mysql, stmt); MYSQL_RES *rs = mysql_stmt_result_metadata(stmt); if (!rs) return report_stmt_error(&mysql, stmt); unsigned int n = mysql_num_fields(rs); MYSQL_FIELD *fields = mysql_fetch_fields(rs); int field_types[n]; MYSQL_BIND bind[n]; my_bool is_null[n]; unsigned long length[n]; my_bool error[n]; memset(bind, 0, sizeof(MYSQL_BIND) * n); int null_terminate_str[n]; memset(null_terminate_str, 0, sizeof(int) * n); struct file_writer ffields[n]; struct file_writer vfields[2][n]; struct vmbuf buf = VMBUF_INITIALIZER; vmbuf_init(&buf, 4096); vmbuf_sprintf(&buf, "%s/%s/%s/schema.txt", outputdir, dbname, tablename); mkdir_for_file_recursive(vmbuf_data(&buf)); int fdschema = creat(vmbuf_data(&buf), 0644); struct vmfile ds_txt = VMFILE_INITIALIZER; vmbuf_reset(&buf); vmbuf_sprintf(&buf, "%s/%s/%s/ds.txt", outputdir, dbname, tablename); if (0 > vmfile_init(&ds_txt, vmbuf_data(&buf), 4096)) return LOGGER_ERROR("failed to create: %s", vmbuf_data(&buf)), vmbuf_free(&buf), -1; vmfile_sprintf(&ds_txt, "DS_LOADER_BEGIN()\n"); vmfile_sprintf(&ds_txt, "/*\n * DB: %s\n */\n", dbname); vmfile_sprintf(&ds_txt, "#undef DB_NAME\n#define DB_NAME %s\n", dbname); ssize_t len = 80 - strlen(tablename); if (len < 0) len = 4; char header[len]; memset(header, '=', len); vmfile_sprintf(&ds_txt, "/* %.*s[ %s ]%.*s */\n", (int)len / 2, header, tablename, (int)(len - (len / 2)), header); vmfile_sprintf(&ds_txt, "# undef TABLE_NAME\n# define TABLE_NAME %s\n", tablename); /* * initialize output files */ unsigned int i; for (i = 0; i < n; ++i) { file_writer_make(&ffields[i]); file_writer_make(&vfields[0][i]); file_writer_make(&vfields[1][i]); } struct hashtable ht_types = HASHTABLE_INITIALIZER; hashtable_init(&ht_types, 32); if (NULL != types) { struct mysql_dumper_type *t = types; for (; t->name; ++t) { /* storing ptr here which points to static str */ hashtable_insert(&ht_types, t->name, strlen(t->name), t, sizeof(struct mysql_dumper_type)); } } /* * parse meta data and construct bind array */ int err = 0; for (i = 0; i < n; ++i) { field_types[i] = fields[i].type; bind[i].is_unsigned = IS_UNSIGNED(fields[i].flags); int64_t ds_type = -1; const char *ds_type_str = "VAR"; /* * handle overrides */ while (NULL != types) { uint32_t ofs = hashtable_lookup(&ht_types, fields[i].name, strlen(fields[i].name)); if (!ofs) break; struct mysql_dumper_type *type = (struct mysql_dumper_type *)hashtable_get_val(&ht_types, ofs); null_terminate_str[i] = MYSQL_DUMPER_CSTR & type->flags; if (type->mysql_type) field_types[i] = type->mysql_type; bind[i].is_unsigned = (type->flags & MYSQL_DUMPER_UNSIGNED) > 0 ? 1 : 0; break; } vmbuf_reset(&buf); vmbuf_sprintf(&buf, "%s/%s/%s/%s", outputdir, dbname, tablename, fields[i].name); mkdir_for_file_recursive(vmbuf_data(&buf)); if (is_var_length_field(field_types[i])) { size_t ofs = vmbuf_wlocpos(&buf); vmbuf_sprintf(&buf, ".ofs"); if (0 > (err = file_writer_init(&vfields[0][i], vmbuf_data(&buf)))) break; vmbuf_wlocset(&buf, ofs); vmbuf_sprintf(&buf, ".dat"); if (0 > (err = file_writer_init(&vfields[1][i], vmbuf_data(&buf)))) break; } else { ds_type = get_ds_type(field_types[i], bind[i].is_unsigned); const char *s = get_ds_type_str(ds_type); if (*s) ds_type_str = s; if (0 > (err = file_writer_init(&ffields[i], vmbuf_data(&buf))) || 0 > (err = file_writer_write(&ffields[i], &ds_type, sizeof(ds_type)))) break;; } len = ribs_mysql_get_storage_size(field_types[i], fields[i].length); if (fdschema > 0) dprintf(fdschema, "%03d name = %s, size=%zu, length=%lu, type=%s (%s), is_prikey=%d, ds_type=%s\n", i, fields[i].name, len, fields[i].length, ribs_mysql_get_type_name(field_types[i]), bind[i].is_unsigned ? "unsigned" : "signed", IS_PRI_KEY(fields[i].flags), ds_type_str); if (is_var_length_field(field_types[i])) { vmfile_sprintf(&ds_txt, " DS_VAR_FIELD_LOADER(%s)\n", fields[i].name); } else { vmfile_sprintf(&ds_txt, " DS_FIELD_LOADER(%s, %s)\n", ds_type_str, fields[i].name); } bind[i].buffer_type = field_types[i]; bind[i].buffer_length = len; bind[i].buffer = malloc(len); bind[i].is_null = &is_null[i]; bind[i].length = &length[i]; bind[i].error = &error[i]; } hashtable_free(&ht_types); mysql_free_result(rs); close(fdschema); //vmfile_sprintf(&ds_txt, "/*\n * TABLE END: %s\n */\n", tablename); vmfile_sprintf(&ds_txt, "DS_LOADER_END()\n"); vmfile_close(&ds_txt); /* * execute & bind */ if (0 != err || 0 != mysql_stmt_execute(stmt) || 0 != mysql_stmt_bind_result(stmt, bind)) { err = -1; report_stmt_error(&mysql, stmt); goto dumper_close_writer; } char zeros[4096]; memset(zeros, 0, sizeof(zeros)); int mysql_err = 0; size_t count = 0, num_rows_errors = 0; /* * write all rows to output files */ while (0 == (mysql_err = mysql_stmt_fetch(stmt))) { int b = 0; for (i = 0; i < n && !b; ++i) b = b || error[i]; if (b) { ++num_rows_errors; continue; } for (i = 0; i < n; ++i) { if (is_var_length_field(field_types[i])) { size_t ofs = file_writer_wlocpos(&vfields[1][i]); if (0 > (err = file_writer_write(&vfields[0][i], &ofs, sizeof(ofs))) || 0 > (err = file_writer_write(&vfields[1][i], is_null[i] ? NULL : bind[i].buffer, is_null[i] ? 0 : length[i]))) goto dumper_error; if (null_terminate_str[i]) { const char c = '\0'; if (0 > (err = file_writer_write(&vfields[1][i], &c, sizeof(c)))) goto dumper_error; } } else { if (0 > (err = file_writer_write(&ffields[i], is_null[i] ? zeros : bind[i].buffer, bind[i].buffer_length))) goto dumper_error; } } ++count; } /* no dumper errors */ goto dumper_ok; dumper_error: LOGGER_ERROR("failed to write data, aborting"); dumper_ok: /* we are done with mysql, close it */ mysql_stmt_close(stmt); mysql_close(&mysql); LOGGER_INFO("%s: %zu records, %zu skipped", tablename, count, num_rows_errors); /* check for mysql errors */ if (mysql_err != MYSQL_NO_DATA) { LOGGER_ERROR("mysql_stmt_fetch returned an error (code=%d)\n", mysql_err); err = -1; } dumper_close_writer: /* * finalize & free memory */ for (i = 0; i < n; ++i) { if (is_var_length_field(field_types[i])) { size_t ofs = file_writer_wlocpos(&vfields[1][i]); if (0 > (err = file_writer_write(&vfields[0][i], &ofs, sizeof(ofs)))) LOGGER_ERROR("failed to write offset"); file_writer_close(&vfields[0][i]); file_writer_close(&vfields[1][i]); } else { file_writer_close(&ffields[i]); } free(bind[i].buffer); } vmbuf_free(&buf); return err; }
int sendemail2(struct sendemail_mta *mta, struct email *email) { if (NULL == mta) { mta = global_mta; } int cfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (0 > cfd) return LOGGER_PERROR("sendemail: socket"), -1; const int option = 1; if (0 > setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))) return LOGGER_PERROR("sendemail: setsockopt SO_REUSEADDR"), close(cfd), -1; if (0 > setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option))) return LOGGER_PERROR("sendemail: setsockopt TCP_NODELAY"), close(cfd), -1; if (0 > connect(cfd, (struct sockaddr *)&mta->saddr, sizeof(mta->saddr)) && EINPROGRESS != errno) return LOGGER_PERROR("sendemail: connect"), close(cfd), -1; if (0 > ribs_epoll_add(cfd, EPOLLIN | EPOLLOUT | EPOLLET, current_ctx)) return close(cfd), -1; struct vmbuf response = VMBUF_INITIALIZER; struct vmbuf request = VMBUF_INITIALIZER; vmbuf_init(&response, 4096); vmbuf_init(&request, 4096); int code = -1; size_t ofs = 0; READ_AND_CHECK(220); vmbuf_sprintf(&request, "EHLO %s\r\n", mta->myhost); SEND_DATA; READ_AND_CHECK(250); vmbuf_sprintf(&request, "MAIL FROM:<%s>\r\n", email->from); SEND_DATA; READ_AND_CHECK(250); struct rcptlist *rcpt = &email->rcpt; while (rcpt) { vmbuf_sprintf(&request, "RCPT TO:<%s>\r\n", rcpt->to); SEND_DATA; READ_AND_CHECK(250); rcpt = rcpt->next; } vmbuf_strcpy(&request, "DATA\r\n"); SEND_DATA; READ_AND_CHECK(354); struct iovec iov[2]= { [0] = { .iov_base = email->data, .iov_len = strlen(email->data) },
int main(int argc, char *argv[]) { size_t num_files = 0; char **files = (char **) calloc(MAX_FILE_SUPPORT, MAX_FILE_SUPPORT * sizeof(char *)); if (0 > init_log_config(&logconf, argc, argv)) { exit(EXIT_FAILURE); } if (!SSTRISEMPTY(logconf.target) && !SSTRISEMPTY(logconf.interface)) { LOGGER_ERROR("%s", "cannot write to target and interface together. please choose one"); exit(EXIT_FAILURE); } char *f = logconf.watch_files; if (!SSTRISEMPTY(f)) { while (f != NULL) { char *fprime = strsep(&f, ","); if (fprime != NULL) { files[num_files] = strdup(fprime); ++num_files; } } } else { LOGGER_ERROR("%s", "no files..no watch!"); exit(EXIT_FAILURE); } if (0 > epoll_worker_init()) { LOGGER_ERROR("%s", "epoll_worker_init"); exit(EXIT_FAILURE); } ribs_timer(60*1000, dump_stats); tab_event_fds = thashtable_create(); delta_push = thashtable_create(); vmbuf_init(&write_buffer, 4096); vmbuf_init(&mb, 4096); if (SSTRISEMPTY(logconf.interface) && !SSTRISEMPTY(logconf.target)) { file_writer_make(&fw); if (0 > file_writer_init(&fw, logconf.target)) { LOGGER_ERROR("%s", "flie_writer"); exit(EXIT_FAILURE); } write_to_file = true; } else if (!SSTRISEMPTY(logconf.interface)) { if (0 > http_client_pool_init(&client_pool, 20, 20)) { LOGGER_ERROR("http_client_pool_init"); exit(EXIT_FAILURE); } memset(&eserv, 0, sizeof(eserv)); vmbuf_reset(&write_buffer); _replace(logconf.interface, &write_buffer, "http://www.", ""); _replace(logconf.interface, &write_buffer, "http://", ""); char *interface = vmbuf_data(&write_buffer); eserv.context = ribs_strdup(strstr(interface, "/")); char *ln = strchr(interface, '/'); ln = ribs_malloc_sprintf("%.*s", ((int)strlen(interface) - (int)strlen(ln)), interface); if (0 > parse_host_to_inet(ln, eserv.hostname, &eserv.server, &eserv.port)) { LOGGER_ERROR("%s", "server details invalid. cannot parse server"); exit(EXIT_FAILURE); } } else { LOGGER_ERROR("%s", "no target defined. please use target or interface"); exit(EXIT_FAILURE); } char _hostname[1024]; gethostname(_hostname, 1024); hostname = ribs_strdup(_hostname); int wd = inotify_init1(IN_NONBLOCK); if (0 >= wd) { LOGGER_ERROR("%s", "failed to init inotify. cannot proceed. make sure you've inotify and is accessible to this user."); exit(EXIT_FAILURE); } if (!recursive_flush_events(wd, files, num_files)) { LOGGER_ERROR("%s", "collection failed"); abort(); } return 0; }
static bool recursive_flush_events ( int inotify_wd, char **files, uint32_t num_files) { struct logz_file_def *filedef = ribs_malloc(num_files * sizeof(struct logz_file_def)); int prev_wd; size_t evlen = 0; // size_t evbuf_off = 0; bool found_unwatchable_dir = false; bool no_inotify_resources = false; int inserted = 0; size_t i; for (i = 0; i < num_files; i++) { filedef[i].name = files[i]; size_t fnlen = strlen (filedef[i].name); if (evlen < fnlen) evlen = fnlen; filedef[i].wd = -1; char *file_fullname = ribs_strdup(filedef[i].name); char *dir_name = dirname(file_fullname); size_t dirlen = strlen(dir_name);; char prev = filedef[i].name[dirlen]; filedef[i].basename_start = basename (file_fullname) - filedef[i].name; filedef[i].name[dirlen] = '\0'; filedef[i].parent_wd = inotify_add_watch(inotify_wd, dir_name, (IN_CREATE | IN_MOVED_TO)); filedef[i].name[dirlen] = prev; if(filedef[i].parent_wd < 0) { if (errno != ENOSPC) LOGGER_ERROR("cannot watch parent directory of file %s", filedef[i].name); else { no_inotify_resources = true; LOGGER_ERROR("%s", "inotify resources exhausted"); } found_unwatchable_dir = true; break; } filedef[i].wd = inotify_add_watch(inotify_wd, filedef[i].name, inotify_file_watch_mask); if (filedef[i].wd < 0) { if (errno == ENOSPC) { no_inotify_resources = true; LOGGER_ERROR("%s", "inotify resources exhausted"); } else if(errno != filedef[i].errnum) LOGGER_ERROR("cannot watch %s", filedef[i].name); continue; } filedef[i].fd = open(filedef[i].name, O_RDONLY | O_NONBLOCK); if (0 >= filedef[i].fd) { LOGGER_ERROR("skipping file %s. cannot open to read", filedef[i].name); continue; } struct stat stats; if (fstat (filedef[i].fd, &stats) != 0) { LOGGER_ERROR("skipping file %s.cannot stat", filedef[i].name); filedef->errnum = errno; logz_close_fd (filedef[i].fd, filedef[i].name); filedef->fd = -1; continue; } filedef[i].size = stats.st_size; lseek (filedef[i].fd, 0, SEEK_END); // no offset enforced thashtable_insert(tab_event_fds, &filedef[i].wd, sizeof(filedef[i].wd), &filedef[i], sizeof(filedef[i]), &inserted); struct vmbuf kdelta = VMBUF_INITIALIZER; vmbuf_init(&kdelta, 4096); thashtable_insert(delta_push, &filedef[i].fd, sizeof(filedef[i].fd), &kdelta, sizeof(kdelta), &inserted); } if(no_inotify_resources || found_unwatchable_dir) { LOGGER_ERROR("%s", "running low on inotify resources / got an unwatchable directory. Aborting!!"); abort(); } prev_wd = filedef[num_files -1].wd; evlen += sizeof (struct inotify_event) + 1; struct vmbuf evbuf = VMBUF_INITIALIZER; vmbuf_init(&evbuf, evlen); ssize_t res = 0; struct timeval delay; /* how long to wait for file changes. */ delay.tv_sec = (time_t) 0.50; delay.tv_usec = 1000000 * (0.50 - delay.tv_sec); fd_set rfd; FD_ZERO (&rfd); FD_SET (inotify_wd, &rfd); while(1) { if (thashtable_get_size(tab_event_fds) == 0) { LOGGER_INFO("%s", "no file to read"); return true; } { int file_change = select(inotify_wd + 1, &rfd, NULL, NULL, NULL); if (file_change == 0) continue; else if (file_change == -1) { LOGGER_ERROR("%s", "error monitoring inotify event"); exit(EXIT_FAILURE); } vmbuf_reset(&evbuf); while (0 < (res = read(inotify_wd, vmbuf_wloc(&evbuf), evlen))) { if (0 > vmbuf_wseek(&evbuf, res)) return false; else if (errno == EINTR) continue; else if (0 < res) break; } if (errno == EAGAIN && res < 0) continue; // res might be 0, or could have overrun memory if (res == 0) { LOGGER_ERROR("%s", "error reading inotify event|bad buffer size. aborting to investigate"); abort(); } // another case when res == 0 or could have overrun memory or got EINVAL // -- ignored -- // what to do if? .. realloc buffer. but the deal is we're on vmbuf which will grow if world is that bad. we're mostly safe here. hence ignored. } struct logz_file_def *tmp; struct inotify_event *event = (struct inotify_event *)vmbuf_data(&evbuf); if (event->len) { // events of lower interest?. these are from watched directory. we'll drop those which we're not watching for and will set watch on those of interest. size_t x; for (x = 0; x < num_files; x++) { if (filedef[x].parent_wd == event->wd && strcmp (event->name, filedef[x].name + filedef[x].basename_start)) break; } if (x == num_files) continue; int wdx = inotify_add_watch (inotify_wd, filedef[x].name, inotify_file_watch_mask); if (0 > wdx) { LOGGER_ERROR("cannot watch %s", filedef[x].name); continue; } tmp = &(filedef[x]); thashtable_remove(tab_event_fds, &tmp->wd, sizeof (tmp->wd)); tmp->wd = wdx; thashtable_insert(tab_event_fds, &tmp->wd, sizeof(tmp->wd), &tmp, sizeof(struct logz_file_def), &inserted); // rebalance new found file | make all assertions | we'll read from this as well. UNUSED(tmp); } else { thashtable_rec_t *rec = thashtable_lookup(tab_event_fds, &event->wd, sizeof(event->wd)); tmp = (struct logz_file_def *)thashtable_get_val(rec); } if (!tmp) { continue; } if (event->mask & (IN_ATTRIB | IN_DELETE_SELF | IN_MOVE_SELF)) { if (event->mask & IN_DELETE_SELF) { inotify_rm_watch(inotify_wd, tmp->wd); thashtable_remove(tab_event_fds, &tmp->wd, sizeof(tmp->wd)); } continue; } _flush(tmp, event->wd, &prev_wd); } return true; }