Sélectionner une révision Git
-
Kubat a rédigé
CMP0100 is set to NEW, so moc and uic can be run on .hh files if needed. This is the new behaviour of the policy so it's safe. It also permits to separate clearly C++ headers from C ones.
Kubat a rédigéCMP0100 is set to NEW, so moc and uic can be run on .hh files if needed. This is the new behaviour of the policy so it's safe. It also permits to separate clearly C++ headers from C ones.
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);
}