Example #1
0
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;
}
Example #3
0
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;
}
Example #4
0
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;
}