Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • 0d135d43f0afc128b28e2f66512afe8796a32459
  • master par défaut
  • script
  • new-devel
  • devel
  • timingView-edit
  • fix-mpv
7 résultats

DialogHelp.hh

Blame
  • config.c 13,35 Kio
    #include <lektor/common.h>
    #include <lektor/config.h>
    #include <lektor/database.h>
    #include <lektor/net.h>
    #include <lektor/reg.h>
    #include <lektor/lib/strv.h>
    
    #include <dlfcn.h>
    #include <pwd.h>
    
    /* Get the path to the config file that may be red, taking into account the
       priority between existing files. The returned path is a path to an existing
       file. If no file is found, returns a non zero value. Returns 0 otherwise. */
    PRIVATE_FUNCTION int config_detect_file(char *conf, size_t conf_len);
    
    /* Create and read the configuration in the conf file and write it into
       lkt_conf. The type is opaque because it's a C++ class.
       Return 0 if the read operation was a success, a non zero value otherwise. */
    PRIVATE_FUNCTION int config_new(lkt_db *db, const char *conf);
    
    /* Get the default user config file, the ~/.config/lektor/lektor.ini file. */
    PRIVATE_FUNCTION void config_default_file(char *dest, size_t len);
    
    PRIVATE_FUNCTION int
    handler(lkt_db *user, const char *section, const struct strv name, const struct strv value)
    {
        RETURN_UNLESS(section && strv_len(name) && strv_len(value), "Skip incomplete line", 1);
        RETURN_UNLESS(database_config_set_strv_1_2(user, section, name, value),
                      "Failed to update config", 1);
        return 0;
    }
    
    PRIVATE_FUNCTION void
    ___set_log_level_internal(const struct strv level)
    {
        static UNUSED struct strv error_strv   = STRV_STATIC("error");
        static UNUSED struct strv warning_strv = STRV_STATIC("warning");
        static UNUSED struct strv warn_strv    = STRV_STATIC("warn");
        static UNUSED struct strv debug_strv   = STRV_STATIC("debug");
        static UNUSED struct strv info_strv    = STRV_STATIC("info");
    
        if (strv_len(level) == 0) {
            LOG_WARN("CONFIG", "Invalid empty 'log' option");
            return;
        }
    
        if (strv_equal_nocase(level, error_strv))
            lkt_set_log_level(LOG_LEVEL_ERROR);
        else if (strv_equal_nocase(level, warn_strv))
            lkt_set_log_level(LOG_LEVEL_WARN);
        else if (strv_equal_nocase(level, warning_strv))
            lkt_set_log_level(LOG_LEVEL_WARN);
        else if (strv_equal_nocase(level, info_strv))
            lkt_set_log_level(LOG_LEVEL_INFO);
        else if (strv_equal_nocase(level, debug_strv))
            lkt_set_log_level(LOG_LEVEL_DEBUG);
        else
            lkt_set_log_level((LOG_LEVEL)(int)strv_to_int(level));
    
        LOG_INFO("CONFIG", "Log level set to %d", lkt_get_log_level());
    }
    
    PRIVATE_FUNCTION void
    ___set_log_level(const struct strv name, const struct strv level)
    {
        static const struct strv log_strv = STRV_STATIC("log");
    
        /* Just check the key name here */
        if (strv_equal_nocase(name, log_strv)) {
            ___set_log_level_internal(level);
        } else {
            LOG_WARN("CONFIG", "Invalid option '" STRV_FMT "=" STRV_FMT "' with no section",
                     STRV_ARG(name), STRV_ARG(level));
        }
    }
    
    PRIVATE_FUNCTION void
    ___apply_log_level(lkt_db *db)
    {
        char loglevel[LKT_LINE_MAX];
        if (!database_config_get_text_nospace(db, "log", "level", loglevel, LKT_LINE_MAX)) {
            /* 'log/level' is not present, use the '/log' */
            LOG_DEBUG("CONFIG", "No entry in config for 'log/level', set it to the '/log' entry");
            safe_snprintf(loglevel, LKT_LINE_MAX, "%d", lkt_get_log_level());
            database_config_set(db, "log", "level", loglevel);
        } else {
            /* 'log/level' is present, use that one */
            ___set_log_level_internal(strv_from_str(loglevel));
        }
    }
    
    PRIVATE_FUNCTION bool
    ___char_is_not_comment_delim(char x)
    {
        return !(x == ';' || x == '#');
    }
    
    PRIVATE_FUNCTION int
    ini_parse(const char *path, lkt_db *db)
    {
        char section[LKT_LINE_MAX], file_line[LKT_LINE_MAX];
        int error = 0, linenum = 0;
        FILE *file = fopen(path, "r");
        if (!file) {
            LOG_ERROR("PARSER", "Failed to open config file '%s'", path);
            return 1;
        }
    
        memset(section, 0, LKT_LINE_MAX);
        memset(file_line, 0, LKT_LINE_MAX);
    
        static struct strv comment_prefix_1   = STRV_STATIC(";");
        static struct strv comment_prefix_2   = STRV_STATIC("#");
        static struct strv bracket_left_strv  = STRV_STATIC("[");
        static struct strv bracket_right_strv = STRV_STATIC("]");
    
        LOG_INFO("CONFIG", "Save config to database from file");
    
        /* Parse the file */
        while (NULL != fgets(file_line, LKT_LINE_MAX, file)) {
            ++linenum;
            struct strv line = strv_trim(strv_from_str(file_line));
    
            /* Skip comments */
            if (strv_starts_with(line, comment_prefix_1) || strv_starts_with(line, comment_prefix_2))
                continue;
    
            /* Handle sections */
            else if (strv_starts_with(line, bracket_left_strv)) {
                if (strv_ends_with(line, bracket_right_strv)) {
                    strv_chop_left(&line, 1);
                    strv_chop_right(&line, 1);
                    if (!strv_as_str(line, section, LKT_LINE_MAX)) {
                        error = 1;
                        LOG_ERROR("PARSER", "Failed to copy the section name at line '%d'", linenum);
                    }
                } else {
                    error = 1;
                    LOG_ERROR("PARSER", "Invalid section name at line '%d'", linenum);
                }
            }
    
            /* Handle empty lines */
            else if (strv_len(line) == 0) {
                continue;
            }
    
            /* Handle name=name pair */
            else {
                int separator_index = -1;
                if (!strv_index_of(line, '=', &separator_index)) {
                    LOG_ERROR("PARSER", "Invalid line '%d', no (key, value) pair found", linenum);
                    continue;
                }
    
                const struct strv key = strv_trim(strv_chop_left(&line, separator_index));
                strv_chop_left(&line, 1); /* Skip the "=" character */
                const struct strv value =
                    strv_trim(strv_take_left_while(line, ___char_is_not_comment_delim));
    
                /* Handle the (key, value) pair */
                if (strv_len(value) == 0 || strv_len(key) == 0) {
                    LOG_ERROR("PARSER", "Invalid key pair at line '%d'", linenum);
                    LOG_ERROR_IF(strv_len(key), "PARSER", "The key was: '" STRV_FMT "'", STRV_ARG(key));
                    LOG_ERROR_IF(strv_len(value), "PARSER", "The value was: '" STRV_FMT "'",
                                 STRV_ARG(value));
                }
    
                else if (section[0]) {
                    LOG_DEBUG("PARSER", "Found line: [%s] '" STRV_FMT "' = '" STRV_FMT "'", section,
                              STRV_ARG(key), STRV_ARG(value));
                    if (handler(db, section, key, value)) {
                        error = 1;
                        LOG_ERROR("PARSER",
                                  "Failed to '[handle] %s, " STRV_FMT "=" STRV_FMT "' at line '%d'",
                                  section, STRV_ARG(key), STRV_ARG(value), linenum);
                    }
                }
    
                else {
                    /* Keep for legacy reasons */
                    ___set_log_level(key, value);
                }
            }
        }
    
        ___apply_log_level(db);
    
        /* End of the function */
        fclose(file);
        if (error)
            LOG_ERROR("PARSER", "An error occured while parsing the file '%s'", path);
        return error;
    }
    
    PRIVATE_FUNCTION void
    config_default_file(char *dest, size_t len)
    {
        /* First try the XDG_CONFIG_HOME variable, else the default location HOME/.config. */
        memset(dest, 0, len * sizeof(char));
        char *home = getenv("XDG_CONFIG_HOME");
        if ((NULL == home) || (strlen(home) >= len)) {
            LOG_DEBUG("CONFIG", "No env variable XDG_CONFIG_HOME, try to use HOME");
            home = getenv("HOME");
            if ((NULL == home) || (strlen(home) >= len)) {
                LOG_FATAL("Failed to get home folder for user, will now exit");
            }
            LOG_DEBUG("CONFIG", "Using HOME: %s", home);
            safe_strncpy(dest, home, len - 1);
            strncat(dest, "/.config/lektor/lektor.ini", len - 1 - strlen(home));
        } else {
            LOG_DEBUG("CONFIG", "Using XDG_CONFIG_HOME: %s", home);
            safe_strncpy(dest, home, len - 1);
            strncat(dest, "/lektor/lektor.ini", len - 1 - strlen(home));
        }
    }
    
    PRIVATE_FUNCTION int
    config_detect_file(char *conf, size_t conf_len)
    {
        bool is_malloc    = false;
        struct passwd *pw = getpwuid(getuid());
        char *home;
    
        if (conf == NULL)
            return 1;
    
        memset(conf, 0, conf_len * sizeof(char));
    
        /* Try the current working dir config file. */
        if (getcwd(conf, conf_len - 1) != NULL) {
            strncat(conf, "/lektor.ini", conf_len - 1);
            LOG_INFO("CONFIG", "Trying %s", conf);
            if (!access(conf, R_OK | F_OK))
                goto found;
        }
    
        /* Try the config file from the config directory. */
        home = getenv("XDG_CONFIG_HOME");
        if (home && strlen(home) < conf_len) {
            /* Skip the strncat to not append the '.config/' to
               the XDG_CONFIG_HOME which must already have this directory in it */
            memcpy(conf, home, (strlen(home) + 1) * sizeof(char));
            strncat(conf, "/lektor/lektor.ini", conf_len - 1);
            goto skip_this_strcat;
        }
        if (!home || (strlen(home) >= conf_len))
            home = getenv("HOME");
        if (!home || (strlen(home) >= conf_len))
            home = pw->pw_dir;
        if (!home || (strlen(home) >= conf_len))
            goto no_config_directory;
        memcpy(conf, home, (strlen(home) + 1) * sizeof(char));
        strncat(conf, "/.config/lektor/lektor.ini", conf_len - 1);
    skip_this_strcat:
        LOG_INFO("CONFIG", "Trying %s", conf);
        if (!access(conf, R_OK | F_OK))
            goto found;
    
    no_config_directory:
        /* Try the '/opt/lektor' file. */
        memcpy(conf, "/opt/lektor/lektor.ini", sizeof("/opt/lektor/lektor.ini"));
        LOG_INFO("CONFIG", "Trying %s", conf);
        if (!access(conf, R_OK | F_OK))
            goto found;
    
        /* Try the '/usr/local/etc/lektor.ini' file. */
        memcpy(conf, "/usr/local/etc/lektor.ini", sizeof("/usr/local/etc/lektor.ini"));
        LOG_INFO("CONFIG", "Trying %s", conf);
        if (!access(conf, R_OK | F_OK))
            goto found;
    
        /* Try the '/etc/lektor.ini' file. */
        memcpy(conf, "/etc/lektor.ini", sizeof("/etc/lektor.ini"));
        LOG_INFO("CONFIG", "Trying %s", conf);
        if (!access(conf, R_OK | F_OK))
            goto found;
    
        /* Error */
        LOG_ERROR("CONFIG", "An error occured with file %s: %s", conf, strerror(errno));
        if (is_malloc)
            free(conf);
        return 1;
    found:
        LOG_INFO("CONFIG", "Using file %s", conf);
        return 0;
    }
    
    PRIVATE_FUNCTION int
    config_new(lkt_db *db, const char *conf)
    {
        if (ini_parse(conf, db)) {
            LOG_ERROR("CONFIG", "Failed to parse file %s", conf);
            goto error;
        }
    
        if (!database_validate_conf(db)) {
            LOG_ERROR("CONFIG", "Configuration file %s is incomplete", conf);
            goto error;
        }
    
        LOG_INFO("CONFIG", "Ensure that optional fields are present: set then to default "
                           "value if not present");
        database_config_set_optional_fields(db);
    
        return 0;
    error:
        LOG_ERROR("CONFIG", "Errors detected, here is a default config");
        config_default(stdout);
        return 1;
    }
    
    void
    config_default(FILE *output)
    {
        fwrite(lkt_default_config_file, sizeof(char), strlen(lkt_default_config_file), output);
    }
    
    /* Recursive mkdir, where the last word of the string is a file, not a folder. */
    PRIVATE_FUNCTION void
    ___mkdir(const char *dir)
    {
        char tmp[PATH_MAX];
        char *p = NULL;
        safe_snprintf(tmp, sizeof(tmp), "%s", dir);
        size_t len = strlen(tmp);
        /* In our case, the final word is always a file, not a folder. */
        if (tmp[len - 1] == '/')
            tmp[len - 1] = 0;
        for (p = tmp + 1; *p; p++) {
            if (*p == '/') {
                *p = 0;
                mkdir(tmp, 00700);
                *p = '/';
            }
        }
        /* Don't do final mkdir here, because in our case the final word in the string
         * is a file, not a folder.
         * mkdir(tmp, S_IRWXU); */
    }
    
    int
    config_open(lkt_db *db, char *conf_file, size_t conf_len)
    {
        bool retry_config_once = false;
    retry_config:
        if ((conf_file[0] == '\0') && config_detect_file(conf_file, conf_len)) {
            RETURN_IF(retry_config_once, "Failed to find a config file", 1);
    
            LOG_INFO("INIT", "Creating default config file");
            config_default_file(conf_file, conf_len);
            ___mkdir(conf_file); /* Create the folder for the file. */
    
            errno           = 0;
            FILE *file_desc = fopen(conf_file, "w+");
            if (file_desc == NULL) {
                LOG_ERROR("INIT", "Failed to open default config file and initialize it");
                LOG_ERROR("INTI", "Conf file is %s, errno is %d: %s", conf_file, errno,
                          strerror(errno));
                return 1;
            }
            config_default(file_desc);
            fclose(file_desc);
    
            LOG_INFO("INIT", "Default configuration file has been writen to %s", conf_file);
            retry_config_once = true;
            goto retry_config;
        }
    
        RETURN_IF(config_new(db, conf_file), "Failed to read the config", 1);
        return 0;
    }
    
    void
    config_handle_hook(lkt_db *db, const char *hook_name)
    {
        char hook_handlers[LKT_LINE_MAX];
        if (!database_config_get_text_nospace(db, "hook", hook_name, hook_handlers, LKT_LINE_MAX)) {
            LOG_ERROR("HOOK", "Failed to get the hook handlers list for hook '%s'", hook_name);
            return;
        }
    
        size_t save = 0;
        char name[LKT_LINE_MAX];
        int code;
        struct module_reg *server_reg;
        reg_global(&server_reg);
    
        while (0 == (code = iter_string(hook_handlers, ",", name, LKT_LINE_MAX, &save))) {
            if (STR_MATCH("none", name))
                continue;
            reg_call(server_reg, name, 1, db);
        }
    
        if (code == -1)
            LOG_ERROR("HOOK", "Failed to parse the handle list for hook '%s'", hook_name);
    }