static ya_result journal_ix_append_ixfr_stream(journal *jh, input_stream *ixfr_wire_is) { journal_ix *jix = (journal_ix*)jh; journal_ix_writelock(jix); /* * Move at the end of the file * Check that the wire starts with the last soa/serial * Append the wire * update the last serial */ // read the record ya_result return_value; dns_resource_record rr; dns_resource_record_init(&rr); if((return_value = dns_resource_record_read(&rr, ixfr_wire_is)) <= 0) { /* FAIL or EOF */ dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to read record: %r", return_value); return return_value; } /* * The first record is an SOA and our starting point (to be deleted) */ #ifdef DEBUG rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: DEL %{dnsname} %{typerdatadesc}", rr.name, &rdatadesc); #endif if(rr.tctr.qtype != TYPE_SOA) { u16 rtype = rr.tctr.qtype; dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: expected SOA record but got %{dnstype} instead", &rtype); return ZDB_JOURNAL_SOA_RECORD_EXPECTED; } /* * check the journal file exists/is defined * do it now if not * proceed */ if(((jix->first_serial == 0) && (jix->last_serial == 0)) || (jix->fd == -1)) { /* the file does not exists yet */ if(FAIL(return_value = rr_soa_get_serial(rr.rdata, rr.rdata_size, &jix->first_serial))) { dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to read record: %r", return_value); return return_value; } int fd = open_create_ex(jix->journal_name, O_RDWR|O_CREAT, 0644); if(fd < 0) { return_value = ERRNO_ERROR; dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); log_err("journal: ix: unable to open journal file '%s': %r", jix->journal_name, return_value); return return_value; } log_info("journal: ix: journal file created '%s'", jix->journal_name); jix->fd = fd; } if(FAIL(return_value = journal_ix_ensure_opened(jix))) { return return_value; } u64 valid_offset = lseek(jix->fd, 0, SEEK_END); u64 current_offset = valid_offset; u32 valid_serial = jix->last_serial; u32 potential_serial = valid_serial; s64 valid_page_offset = jix->last_page_offset; s64 potential_page_offset = current_offset; #ifdef DEBUG log_debug("journal: ix: ready to append to journal after serial %08x (%d) at offset %lld", valid_serial, valid_serial, valid_offset); #endif u8 mode = 0; /* 0: del, 1: add */ output_stream fos; output_stream bos; fd_output_stream_attach(&fos, jix->fd); buffer_output_stream_init(&bos, &fos, 512); for(;;) { /* write the first */ if(FAIL(return_value = dns_resource_record_write(&rr, &bos))) { /* this is VERY bad */ log_err("journal: ix: error writing a record to the journal: %r", return_value); break; } /* update the current offset */ current_offset += return_value; if((return_value = dns_resource_record_read(&rr, ixfr_wire_is)) <= 0) /* no bytes read OR error, there is no macro for this */ { /* error or end of stream */ if(return_value == 0) /* end of stream */ { if(mode != 0) /* on add mode so everything should be fine */ { valid_offset = current_offset; valid_serial = potential_serial; valid_page_offset = potential_page_offset; } else /* but on delete mode instead of add mode */ { log_err("journal: ix: ixfr stream unexpected eof"); return_value = UNEXPECTED_EOF; /* we have an error */ } } break; } if(rr.tctr.qtype == TYPE_SOA) { mode ^= 1; #ifdef DEBUG rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: %s %{dnsname} %{typerdatadesc}", (mode!=0)?"add":"del", rr.name, &rdatadesc); #endif if(mode == 0) { /* * new SOA to delete * * it's a new "page" (delete -> add) * * the offset before we write this record is the highest valid one in the file * so the error correcting truncation will be made at that offset */ valid_offset = current_offset; /* * the serial number that has been added with the previous page */ valid_serial = potential_serial; /* * the offset of the previous page */ valid_page_offset = potential_page_offset; /* * the new page starts here : update */ potential_page_offset = current_offset; } else { /* * new SOA add * * this is the second half of the page, we know what serial it is about */ if(FAIL(return_value = rr_soa_get_serial(rr.rdata, rr.rdata_size, &potential_serial))) { break; } } } #ifdef DEBUG else { rdata_desc rdatadesc = {rr.tctr.qtype, rr.rdata_size, rr.rdata}; log_debug("journal: ix: %s %{dnsname} %{typerdatadesc}", (mode!=0)?"add":"del", rr.name, &rdatadesc); } #endif } if(FAIL(return_value)) { /* * The journal is only valid up to valid_offset with serial ... */ log_err("journal: ix: rewinding journal up to last valid point (%lld)", valid_offset); ftruncate(jix->fd, valid_offset); } #ifdef DEBUG log_debug("journal: ix: page offset got from %d to %d", jix->last_page_offset, valid_page_offset); log_debug("journal: ix: serial got from %d to %d", jix->last_serial, valid_serial); #endif jix->last_page_offset = valid_page_offset; jix->last_serial = valid_serial; /* * rename the file */ if(ISOK(return_value)) { char new_name[PATH_MAX]; memcpy(new_name, jix->journal_name, jix->journal_name_len); snformat(&new_name[jix->journal_name_len - FIRST_FROM_END], 8 + 1 + 8 + 1 + IX_EXT_STRLEN + 1, "%08x-%08x." IX_EXT , jix->first_serial, jix->last_serial); if(rename(jix->journal_name, new_name) >= 0) { memcpy(jix->journal_name, new_name, jix->journal_name_len); } } /* */ #ifdef DEBUG log_debug("journal: ix: fd=%i from=%08x to=%08x soa@%lld file=%s", jix->fd, jix->first_serial, jix->last_serial, jix->last_page_offset, (jix->journal_name!=NULL)?jix->journal_name:"NONE-YET"); #endif output_stream_flush(&bos); fd_output_stream_detach(buffer_output_stream_get_filtered(&bos)); output_stream_close(&bos); dns_resource_record_clear(&rr); journal_ix_writeunlock(jix); if(ISOK(return_value)) { #ifdef DEBUG log_debug("journal: ix: page added (fd=%i from=%08x to=%08x soa@%lld file=%s): %r", jix->fd, jix->first_serial, jix->last_serial, jix->last_page_offset, (jix->journal_name!=NULL)?jix->journal_name:"NONE-YET", return_value); #endif return TYPE_IXFR; /* that's what the caller expects to handle the new journal pages */ } else { log_err("journal: ix: failed to add page"); return return_value; } }
static void* scheduler_queue_zone_write_thread(void* data_) { char fullname_tmp[MAX_PATH]; zone_write_param *zwp = (zone_write_param*)data_; zdb_zone *zone = zwp->zone; u32 serial; log_info("zone write text: writing %{dnsname} zone file", zone->origin); if(ZDB_ZONE_INVALID(zone)) { log_err("zone write text: zone %{dnsname} marked as invalid", zone->origin); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); return NULL; } if(FAIL(zdb_zone_getserial(zone, &serial))) { log_err("zone write text: no SOA in %{dnsname}", zone->origin); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); return NULL; } if(FAIL(snformat(fullname_tmp, sizeof (fullname_tmp), "%s.%d.tmp", zwp->file_path, serial))) { log_err("zone write text: path '%s.%d.tmp' is too big", zwp->file_path, serial); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /* WARNING: From this point forward, 'zone' cannot be used anymore */ return NULL; } /** * @todo check there is not already a zone file writer working here ... * */ /* * delete the temp file if it exists already */ if(unlink(fullname_tmp) < 0) { int err = errno; if(err != ENOENT) { log_err("zone write text: cannot cleanup '%s': %r", MAKE_ERRNO_ERROR(err)); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /* WARNING: From this point forward, 'zone' cannot be used anymore */ return NULL; } } log_info("zone write text: writing '%s'", fullname_tmp); zdb_zone_lock(zone, ZDB_ZONE_MUTEX_SIMPLEREADER); zdb_zone_write_text_file(zone, fullname_tmp, FALSE); zdb_zone_unlock(zone, ZDB_ZONE_MUTEX_SIMPLEREADER); log_info("zone write text: renaming '%s' to '%s'", fullname_tmp, zwp->file_path); if(rename(fullname_tmp, zwp->file_path) < 0) { log_err("zone write text: unable to rename tmp zone file into '%s': %r", zwp->file_path, ERRNO_ERROR); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /** @note Calling this so the scheduler gets a SCHEDULER_TASK_FINISHED is mandatory. */ return NULL; } log_info("zone write text: %{dnsname} zone file written", zone->origin); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /* WARNING: From this point forward, 'zwp' cannot be used anymore */ return NULL; }
static ya_result config_main_verify_and_update_directory(const char *base_path, char **dirp) { char fullpath[PATH_MAX]; char tempfile[PATH_MAX]; if(dirp == NULL) { return UNEXPECTED_NULL_ARGUMENT_ERROR; } char *dir = *dirp; if(dir == NULL) { return UNEXPECTED_NULL_ARGUMENT_ERROR; } ya_result fullpath_len = snprintf(fullpath, sizeof(fullpath),"/%s/%s", base_path, dir); if(FAIL(fullpath_len)) { return fullpath_len; } bool dirsep = TRUE; int j = 1; for(int i = 1; i <= fullpath_len; i++) { char c = fullpath[i]; if(c == '/') { if(!dirsep) { fullpath[j++] = c; } dirsep = TRUE; } else { fullpath[j++] = fullpath[i]; dirsep = FALSE; } } struct stat ds; if(stat(fullpath, &ds) < 0) { int err = errno; ttylog_err("error: '%s': %s", fullpath, strerror(err)); return MAKE_ERRNO_ERROR(err); } if((ds.st_mode & S_IFMT) != S_IFDIR) { ttylog_err("error: '%s' is not a directory", dir); return INVALID_PATH; } snformat(tempfile, sizeof(tempfile), "%s/ydf.XXXXXX", fullpath); int tempfd; if((tempfd = mkstemp(tempfile)) < 0) { int return_code = ERRNO_ERROR; ttylog_err("error: '%s' is not writable: %r", fullpath, return_code); return return_code; } unlink(tempfile); close_ex(tempfd); free(dir); *dirp = strdup(fullpath); chroot_manage_path(dirp, fullpath, FALSE); 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; }