/* Public function for logging an entry. * Tries to encapsulate as much of the formatting as possible to simplify the * caller's code. */ enum logger_ret_type logger_log(logger *l, const enum log_entry_type event, const void *entry, ...) { bipbuf_t *buf = l->buf; bool nospace = false; va_list ap; int total = 0; logentry *e; const entry_details *d = &l->entry_map[event]; int reqlen = d->reqlen; pthread_mutex_lock(&l->mutex); /* Request a maximum length of data to write to */ e = (logentry *) bipbuf_request(buf, (sizeof(logentry) + reqlen)); if (e == NULL) { pthread_mutex_unlock(&l->mutex); l->dropped++; return LOGGER_RET_NOSPACE; } e->gid = logger_get_gid(); e->event = d->subtype; /* TODO: Could pass this down as an argument now that we're using * LOGGER_LOG() macro. */ e->eflags = d->eflags; /* Noting time isn't optional. A feature may be added to avoid rendering * time and/or gid to a logger. */ gettimeofday(&e->tv, NULL); switch (d->subtype) { case LOGGER_TEXT_ENTRY: va_start(ap, entry); total = vsnprintf((char *) e->data, reqlen, d->format, ap); va_end(ap); if (total >= reqlen || total <= 0) { fprintf(stderr, "LOGGER: Failed to vsnprintf a text entry: (total) %d\n", total); break; } e->size = total + 1; /* null byte */ break; case LOGGER_EVICTION_ENTRY: _logger_log_evictions(e, (item *)entry); break; #ifdef EXTSTORE case LOGGER_EXT_WRITE_ENTRY: va_start(ap, entry); int ew_bucket = va_arg(ap, int); va_end(ap); _logger_log_ext_write(e, (item *)entry, ew_bucket); break; #endif case LOGGER_ITEM_GET_ENTRY: va_start(ap, entry); int was_found = va_arg(ap, int); char *key = va_arg(ap, char *); size_t nkey = va_arg(ap, size_t); uint8_t gclsid = va_arg(ap, int); _logger_log_item_get(e, was_found, key, nkey, gclsid); va_end(ap); break; case LOGGER_ITEM_STORE_ENTRY: va_start(ap, entry); enum store_item_type status = va_arg(ap, enum store_item_type); int comm = va_arg(ap, int); char *skey = va_arg(ap, char *); size_t snkey = va_arg(ap, size_t); rel_time_t sttl = va_arg(ap, rel_time_t); uint8_t sclsid = va_arg(ap, int); _logger_log_item_store(e, status, comm, skey, snkey, sttl, sclsid); break; } /* Push pointer forward by the actual amount required */ if (bipbuf_push(buf, (sizeof(logentry) + e->size)) == 0) { fprintf(stderr, "LOGGER: Failed to bipbuf push a text entry\n"); pthread_mutex_unlock(&l->mutex); return LOGGER_RET_ERR; } l->written++; L_DEBUG("LOGGER: Requested %d bytes, wrote %lu bytes\n", reqlen, (sizeof(logentry) + e->size)); pthread_mutex_unlock(&l->mutex); if (nospace) { return LOGGER_RET_NOSPACE; } else { return LOGGER_RET_OK; } }
static ya_result config_main_section_postprocess(struct config_section_descriptor_s *csd) { u32 port = 0; u32 cpu_per_core = (sys_has_hyperthreading())?2:1; int ret; char tmp[PATH_MAX]; if(FAIL(parse_u32_check_range(g_config->server_port, &port, 1, MAX_U16, 10))) { port = DNS_DEFAULT_PORT; ttylog_err("config: main: wrong dns port set in main '%s', defaulted to %d", g_config->server_port, port); } if(g_config->hostname_chaos == NULL) { #if _BSD_SOURCE || _XOPEN_SOURCE >= 500 || /* Since glibc 2.12: */ _POSIX_C_SOURCE >= 200112L if(gethostname(tmp, sizeof(tmp)) == 0) { g_config->hostname_chaos = strdup(tmp); } else { osformatln(termerr,"config: main: unable to get hostname: %r", ERRNO_ERROR); g_config->hostname_chaos = strdup("not disclosed"); } #else g_config->hostname_chaos = strdup("not disclosed"); #endif } if(g_config->thread_affinity_multiplier == 0) { g_config->thread_affinity_multiplier = cpu_per_core; } class_ch_set_hostname(g_config->hostname_chaos); class_ch_set_id_server(g_config->serverid_chaos); class_ch_set_version(g_config->version_chaos); host_set_default_port_value(g_config->listen, ntohs(port)); if(g_config->server_flags & SERVER_FL_CHROOT) { uid_t euid = geteuid(); if(euid != 0) { ttylog_err("config: main: chroot has been enabled but euid is not root (%i != 0)", (int)euid); return INVALID_STATE_ERROR; } } else // disables the base-path/chroot-path feature { if(strcmp(g_config->chroot_path, "/") != 0) { free(g_config->chroot_path); g_config->chroot_path = strdup("/"); chroot_set_path(g_config->chroot_path); } } message_edns0_setmaxsize(g_config->edns0_max_size); g_config->total_interfaces = host_address_count(g_config->listen); if(g_config->total_interfaces > MAX_INTERFACES) { ttylog_err("error: more than %d listening addresses defined.", MAX_INTERFACES); return CONFIG_TOO_MANY_HOSTS; } #ifndef SO_REUSEPORT if(g_config->network_model == 1) { ttylog_err("error: network-model 1 requires features not available on this system (SO_REUSEPORT)"); return FEATURE_NOT_SUPPORTED; } #endif g_config->axfr_retry_jitter = BOUND(AXFR_RETRY_JITTER_MIN, g_config->axfr_retry_jitter, g_config->axfr_retry_delay); g_config->dnssec_thread_count = BOUND(1, g_config->dnssec_thread_count, sys_get_cpu_count()); if(g_config->cpu_count_override > 0) { sys_set_cpu_count(g_config->cpu_count_override); } if(g_config->thread_count_by_address == 0) { ttylog_err("config: single thread engine has been removed, thread-count-by-address set to 1"); g_config->thread_count_by_address = 1; } if(g_config->thread_count_by_address < 0) { g_config->thread_count_by_address = MAX(sys_get_cpu_count() / cpu_per_core, 1); } else if((g_config->thread_count_by_address > sys_get_cpu_count())) { g_config->thread_count_by_address = MAX(sys_get_cpu_count() / cpu_per_core, 1); ttylog_err("config: bounding down thread-count-by-address to the number of physical CPUs (%d)", g_config->thread_count_by_address); } g_config->tcp_query_min_rate_us = g_config->tcp_query_min_rate * 0.000001; #if HAS_DNSSEC_SUPPORT g_config->dnssec_thread_count = BOUND(1, g_config->dnssec_thread_count, sys_get_cpu_count()); if(!IS_TYPE_PRIVATE(g_config->sig_signing_type)) { ttylog_err("error: signing type is not in the accepted range: %hx", g_config->sig_signing_type); return CONFIG_WRONG_SIG_TYPE; } if(g_config->sig_validity_interval > SIGNATURE_VALIDITY_INTERVAL_MAX) { ttylog_err("error: signature validity interval too high"); return CONFIG_WRONG_SIG_VALIDITY; } if(g_config->sig_validity_regeneration * SIGNATURE_VALIDITY_REGENERATION_S * 2 > g_config->sig_validity_interval * SIGNATURE_VALIDITY_INTERVAL_S) { ttylog_err("error: default signature regeneration is more than half the interval (%ds * 2 > %ds)", g_config->sig_validity_regeneration * SIGNATURE_VALIDITY_REGENERATION_S, g_config->sig_validity_interval * SIGNATURE_VALIDITY_INTERVAL_S); return CONFIG_WRONG_SIG_REGEN; } #endif /// @note config_main_verify_and_update_directory updates the folder with the base_path /** * * @note All base paths are updated with the chroot variable so it does not * need to be added all the time. * */ const char *base_path = g_config->chroot_path; if(FAIL(ret = config_main_verify_and_update_directory(base_path, &g_config->data_path))) { return ret; } if(FAIL(ret = config_main_verify_and_update_directory(base_path, &g_config->keys_path))) { return ret; } if(FAIL(ret = config_main_verify_and_update_directory(base_path, &g_config->log_path))) { return ret; } #if 0 /* fix */ #else if(FAIL(ret = config_main_verify_and_update_file(base_path, &g_config->pid_file))) { return ret; } #endif g_config->reloadable = TRUE; if((g_config->server_flags & SERVER_FL_CHROOT) != 0) { if(FAIL(chroot_manage_path(&g_config->config_file, g_config->config_file, FALSE))) { log_warn("config file '%s' will not be accessible within the chroot jail '%s' : config reload will not work", g_config->config_file, base_path); g_config->reloadable = FALSE; } } if(FAIL(ret = config_main_verify_and_update_directory(base_path, &g_config->xfr_path))) { return ret; } #if ZDB_HAS_DNSSEC_SUPPORT dnssec_keystore_setpath(g_config->keys_path); dnssec_set_xfr_path(g_config->xfr_path); journal_set_xfr_path(g_config->xfr_path); #endif if((logger_get_uid() != g_config->uid) || (logger_get_gid() != g_config->gid)) { logger_set_uid(g_config->uid); logger_set_gid(g_config->gid); logger_reopen(); } return SUCCESS; }
static ya_result config_section_handles_set_wild(struct config_section_descriptor_s *csd, const char *key, const char *value) { if(logger_channel_get_usage_count(key) >= 0) { return CONFIG_LOGGER_HANDLE_ALREADY_DEFINED; // already defined } char value_target[PATH_MAX]; parse_copy_word(value_target, sizeof(value_target), value); if(strcasecmp("stdout", value_target) == 0) { output_stream stdout_os; fd_output_stream_attach(&stdout_os, dup_ex(1)); logger_channel *stdout_channel = logger_channel_alloc(); logger_channel_stream_open(&stdout_os, FALSE, stdout_channel); logger_channel_register(key, stdout_channel); } else if(strcasecmp("stderr", value_target) == 0) { output_stream stderr_os; fd_output_stream_attach(&stderr_os, dup_ex(2)); logger_channel *stderr_channel = logger_channel_alloc(); logger_channel_stream_open(&stderr_os, FALSE, stderr_channel); logger_channel_register(key, stderr_channel); } else if(strcasecmp("syslog", value_target) == 0) { char* token; /* * Tokenize */ u32 options = 0; u32 facility = 0; /* WARNING: NEVER EVER USE A CONST STRING AS ARGUMENT OR strtok WILL SIGSEGV */ char *tmp_value = strdup(value); // value, not value_target for(token = strtok(tmp_value, SYSLOG_CHANNEL_TOKEN_DELIMITER); token != NULL; token = strtok(NULL, SYSLOG_CHANNEL_TOKEN_DELIMITER)) { u32 token_value; if(ISOK(get_value_from_casename(syslog_channel_arguments_options, token, &token_value))) { options |= token_value; } else if(ISOK(get_value_from_casename(syslog_channel_arguments_facility, token, &token_value))) { facility = token_value; // Facility is NOT a bit mask } else { /* Note: empty statement is taken care of here */ osformatln(termerr, "wrong syslog argument '%s' : ", csd->vtbl->name); free(tmp_value); return PARSE_INVALID_ARGUMENT; } } free(tmp_value); logger_channel *syslog_channel = logger_channel_alloc(); logger_channel_syslog_open(key, options, facility, syslog_channel); logger_channel_register(key, syslog_channel); } else { const char *chroot_base = chroot_get_path(); uid_t uid = logger_get_uid(); gid_t gid = logger_get_gid(); ya_result return_code; unsigned int access_rights; char fullpath[PATH_MAX]; // find the end of the word // cut it const char *path_limit = parse_next_blank(value); size_t path_len = path_limit - value; size_t pathbase_len; if(value[0] != '/') { pathbase_len = snformat(fullpath, sizeof(fullpath), "%s%s", chroot_base, log_path); } else { pathbase_len = snformat(fullpath, sizeof(fullpath), "%s", chroot_base); } if(pathbase_len + path_len + 1 >= sizeof(fullpath)) { return CONFIG_FILE_PATH_TOO_BIG; } memcpy(&fullpath[pathbase_len], value, path_len); path_len += pathbase_len; fullpath[path_len] = '\0'; // parse the next word, it is supposed to be an octal number if(sscanf(path_limit, "%o", &access_rights) != 1) { access_rights = FILE_CHANNEL_DEFAULT_ACCESS_RIGHTS; } // if the path starts with a slash, it's absolute, else it's relative // to the log directory logger_channel* file_channel = logger_channel_alloc(); if(FAIL(return_code = logger_channel_file_open(fullpath, uid, gid, access_rights, FALSE, file_channel))) { osformatln(termerr, "config: unable to open file channel '%s' (%d:%d %o) : %r", fullpath, uid, gid, access_rights, return_code); flusherr(); return return_code; } logger_channel_register(key, file_channel); } return SUCCESS; }