#include <stdio.h>
#include <string.h>
#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "util.h"
#include "trace.h"
#include "config-parser.h"
+#include "proc-common.h"
+
+#define MAX_SECTION 64
+#define CPU_INIT_PRIO 100
-#define MAX_LINE 128
-#define MAX_SECTION 64
-#define WHITESPACE " \t"
-#define NEWLINE "\n\r"
-#define COMMENT '#'
+static GSList *app_conf_info_list;
+static GSList *service_conf_info_list;
-static inline char *trim_str(char *s)
+static struct proc_conf_info* process_exist(const char *name, enum proc_type proc_type)
{
- char *t;
- /* left trim */
- s += strspn(s, WHITESPACE);
+ GSList *iter;
+ bool found = false;
+ struct proc_conf_info *pci = NULL;
- /* right trim */
- for (t = strchr(s, 0); t > s; t--)
- if (!strchr(WHITESPACE, t[-1]))
+ if (proc_type != APP_TYPE && proc_type != SERVICE_TYPE)
+ return pci;
+
+ gslist_for_each_item(iter,
+ proc_type == APP_TYPE ? app_conf_info_list : service_conf_info_list) {
+ pci = (struct proc_conf_info *)iter->data;
+ if (!strncmp(pci->name, name, strlen(name)+1)) {
+ found = true;
break;
- *t = 0;
- return s;
+ }
+ }
+
+ if (!found)
+ pci = NULL;
+
+ return pci;
+}
+
+static int vendor_config(struct parse_result *result, void *user_data)
+{
+ int *config_type = (int *)user_data;
+ static struct proc_conf_info *pci = NULL;
+
+ if (!result || !user_data)
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+
+ if (strncmp(result->section, PER_PROCESS_SECTION_CONF, strlen(PER_PROCESS_SECTION_CONF)+1))
+ return RESOURCED_ERROR_NONE;
+
+ if (!strncmp(result->name, SERVICE_NAME_CONF, strlen(SERVICE_NAME_CONF)+1) ||
+ !strncmp(result->name, APP_NAME_CONF, strlen(APP_NAME_CONF)+1)) {
+ pci = process_exist(result->value, result->name[0] == 'A' ? APP_TYPE : SERVICE_TYPE);
+ if (pci == NULL) {
+ pci = (struct proc_conf_info *)calloc(1, sizeof(struct proc_conf_info));
+ if (pci == NULL) {
+ _E("Failed to allocate memory during parsing vendor configurations");
+ return RESOURCED_ERROR_OUT_OF_MEMORY;
+ }
+ if (result->name[0] == 'A') {
+ pci->proc_type = APP_TYPE;
+ app_conf_info_list = g_slist_prepend(app_conf_info_list, (gpointer)pci);
+ }
+ else {
+ pci->proc_type = SERVICE_TYPE;
+ service_conf_info_list = g_slist_prepend(service_conf_info_list, (gpointer)pci);
+ }
+ pci->mem_type = CGROUP_TOP;
+ pci->cpu_type = CGROUP_TOP;
+ pci->cpu_priority = CPU_INIT_PRIO;
+ strncpy(pci->name, result->value, sizeof(pci->name)-1);
+ }
+ }
+ else if (!strncmp(result->name, CPU_CGROUP_NAME_CONF, strlen(CPU_CGROUP_NAME_CONF)+1) &&
+ *config_type == LIMITER_CONFIG) {
+ if (!pci) {
+ _E("process configuration information pointer should not be NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!strncmp(result->value, CGROUP_VIP_VALUE_CONF,
+ strlen(CGROUP_VIP_VALUE_CONF) +1)) {
+ pci->cpu_type = CGROUP_VIP;
+ }
+ else if (!strncmp(result->value, CGROUP_HIGH_VALUE_CONF,
+ strlen(CGROUP_HIGH_VALUE_CONF) +1)) {
+ pci->cpu_type = CGROUP_HIGH;
+ }
+ else if (!strncmp(result->value, CGROUP_MEDIUM_VALUE_CONF,
+ strlen(CGROUP_MEDIUM_VALUE_CONF) +1)) {
+ pci->cpu_type = CGROUP_MEDIUM;
+ }
+ else if (!strncmp(result->value, CGROUP_LOW_VALUE_CONF,
+ strlen(CGROUP_LOW_VALUE_CONF) +1)) {
+ pci->cpu_type = CGROUP_LOW;
+ }
+ else {
+ _E("invalid parameter (%s)", result->value);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ }
+ else if (!strncmp(result->name, MEM_CGROUP_NAME_CONF, strlen(MEM_CGROUP_NAME_CONF)+1) &&
+ *config_type == LIMITER_CONFIG) {
+ if (!pci) {
+ _E("process configuration information pointer should not be NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!strncmp(result->value, CGROUP_VIP_VALUE_CONF,
+ strlen(CGROUP_VIP_VALUE_CONF) +1)) {
+ pci->mem_type = CGROUP_VIP;
+ }
+ else if (!strncmp(result->value, CGROUP_HIGH_VALUE_CONF,
+ strlen(CGROUP_HIGH_VALUE_CONF) +1)) {
+ pci->mem_type = CGROUP_HIGH;
+ }
+ else if (!strncmp(result->value, CGROUP_MEDIUM_VALUE_CONF,
+ strlen(CGROUP_MEDIUM_VALUE_CONF) +1)) {
+ pci->mem_type = CGROUP_MEDIUM;
+ }
+ else if (!strncmp(result->value, CGROUP_LOW_VALUE_CONF,
+ strlen(CGROUP_LOW_VALUE_CONF) +1)) {
+ pci->mem_type = CGROUP_LOW;
+ }
+ else {
+ _E("invalid parameter (%s)", result->value);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ }
+ else if (!strncmp(result->name, MEM_LIMIT_ACTION_NAME_CONF,
+ strlen(MEM_LIMIT_ACTION_NAME_CONF)+1) && *config_type == LIMITER_CONFIG) {
+ if (!pci) {
+ _E("process configuration information pointer should not be NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ char *ptr = strchr(result->value, ',');
+ if (ptr == NULL) {
+ _E("Cannot find ',' in the string (%s)", result->value);
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ char *second_value = ptr + 1;
+ char temp;
+
+ if (result->value > (ptr - 2)) {
+ _E("Size of string should be larger than 2");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (*(ptr - 1) == 'B') {
+ temp = *(ptr - 2);
+ *(ptr - 2) = '\0';
+
+ if (temp == 'G') {
+ pci->mem_action.memory = GBYTE_TO_MBYTE(atoi(result->value));
+ }
+ else if (temp == 'M') {
+ pci->mem_action.memory = atoi(result->value);
+ }
+ else if (temp == 'K') {
+ pci->mem_action.memory = KBYTE_TO_MBYTE(atoi(result->value));
+ }
+ else if (temp == ' ') {
+ pci->mem_action.memory = BYTE_TO_MBYTE(atoi(result->value));
+ }
+ else {
+ _E("Memory size unit should be GB or MB or KB or B");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!strncmp(second_value, ACTION_BROADCAST_VALUE_CONF,
+ strlen(ACTION_BROADCAST_VALUE_CONF)+1))
+ pci->mem_action.action = PROC_ACTION_BROADCAST;
+ else if (!strncmp(second_value, ACTION_RECLAIM_VALUE_CONF,
+ strlen(ACTION_RECLAIM_VALUE_CONF)+1))
+ pci->mem_action.action = PROC_ACTION_RECLAIM;
+ else if (!strncmp(second_value, ACTION_KILL_VALUE_CONF,
+ strlen(ACTION_KILL_VALUE_CONF)+1))
+ pci->mem_action.action = PROC_ACTION_KILL;
+ else {
+ _E("action (%s) is not supported", second_value);
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ else {
+ _E("Memory size unit should be XB");
+ return RESOURCED_ERROR_FAIL;
+ }
+ }
+ else if (!strncmp(result->name, CPU_PRIORITY_NAME_CONF, strlen(CPU_PRIORITY_NAME_CONF)+1) &&
+ *config_type == LIMITER_CONFIG) {
+ if (!pci) {
+ _E("process configuration information pointer should not be NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+ pci->cpu_priority = atoi(result->value);
+ }
+ else if (!strncmp(result->name, ACTION_ON_FAILURE_NAME_CONF,
+ strlen(ACTION_ON_FAILURE_NAME_CONF)+1) && *config_type == PROCESS_CONFIG) {
+ if (!pci) {
+ _E("process configuration information pointer should not be NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!strncmp(result->value, ACTION_REBOOT_VALUE_CONF,
+ strlen(ACTION_REBOOT_VALUE_CONF) +1)) {
+ pci->fail_action = PROC_ACTION_REBOOT;
+ }
+ else {
+ _E("invalid parameter (%s)", result->value);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ }
+ else if (!strncmp(result->name, WATCHDOG_ACTION_NAME_CONF,
+ strlen(WATCHDOG_ACTION_NAME_CONF)+1) && *config_type == PROCESS_CONFIG) {
+ if (!pci) {
+ _E("process configuration information pointer should not be NULL");
+ return RESOURCED_ERROR_FAIL;
+ }
+
+ if (!strncmp(result->value, ACTION_IGNORE_VALUE_CONF,
+ strlen(ACTION_IGNORE_VALUE_CONF) +1)) {
+ pci->watchdog_action = PROC_ACTION_IGNORE;
+ }
+ else if (!strncmp(result->value, ACTION_KILL_VALUE_CONF,
+ strlen(ACTION_KILL_VALUE_CONF) +1)) {
+ pci->watchdog_action = PROC_ACTION_KILL;
+ }
+ else {
+ _E("invalid parameter (%s)", result->value);
+ return RESOURCED_ERROR_INVALID_PARAMETER;
+ }
+ }
+ else {
+ _E("Unknown configuration name (%s) and value (%s) on section (%s)",
+ result->name, result->value, result->section);
+ }
+
+ return RESOURCED_ERROR_NONE;
+}
+
+static void load_per_vendor_configs(const char *dir, int func(struct parse_result *result,
+ void *user_data), void *user_data)
+{
+ int count;
+ int idx;
+ struct dirent **namelist;
+
+ if ((count = scandir(dir, &namelist, NULL, alphasort)) == -1) {
+ _W("failed to opendir (%s)", dir);
+ return;
+ }
+
+ for (idx = 0; idx < count; idx++) {
+ char path[PATH_MAX] = {0, };
+
+ if (!strstr(namelist[idx]->d_name, CONF_FILE_SUFFIX))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/%s", dir, namelist[idx]->d_name);
+ config_parse(path, func, user_data);
+ free(namelist[idx]);
+ }
+
+ free(namelist);
+}
+
+void resourced_parse_vendor_configs(void)
+{
+ int config_type;
+
+ config_type = LIMITER_CONFIG;
+ load_per_vendor_configs(CGROUP_VIP_LIST_DIR, vendor_config, &config_type);
+
+ config_type = PROCESS_CONFIG;
+ load_per_vendor_configs(PROC_CONF_DIR, vendor_config, &config_type);
+
+ //DEBUG
+ GSList *iter;
+ gslist_for_each_item(iter, service_conf_info_list) {
+ struct proc_conf_info *pci = (struct proc_conf_info *)iter->data;
+ _I("[CONFIG] name: %s", pci->name);
+ _I("[CONFIG] mem cgroup: %d", pci->mem_type);
+ _I("[CONFIG] cpu cgroup: %d", pci->cpu_type);
+ _I("[CONFIG] cpu priority: %d", pci->cpu_priority);
+ _I("[CONFIG] watchdog_flag: %x", pci->watchdog_action);
+ _I("[CONFIG] fail_flag: %x", pci->fail_action);
+ _I("[CONFIG] memory: %lu MB", pci->mem_action.memory);
+ _I("[CONFIG] action: %d", pci->mem_action.action);
+ }
+
+ gslist_for_each_item(iter, app_conf_info_list) {
+ struct proc_conf_info *pci = (struct proc_conf_info *)iter->data;
+ _I("[CONFIG] name: %s", pci->name);
+ _I("[CONFIG] mem cgroup: %d", pci->mem_type);
+ _I("[CONFIG] cpu cgroup: %d", pci->cpu_type);
+ _I("[CONFIG] cpu priority: %d", pci->cpu_priority);
+ _I("[CONFIG] watchdog_flag: %x", pci->watchdog_action);
+ _I("[CONFIG] fail_flag: %x", pci->fail_action);
+ _I("[CONFIG] memory: %lu MB", pci->mem_action.memory);
+ _I("[CONFIG] action: %d", pci->mem_action.action);
+ }
}
int config_parse(const char *file_name, int cb(struct parse_result *result,
- void *user_data), void *user_data)
+ void *user_data), void *user_data)
{
FILE *f = NULL;
struct parse_result result;
/* use stack for parsing */
- char line[MAX_LINE];
+ char line[LINE_MAX];
char section[MAX_SECTION];
char *start, *end, *name, *value;
int lineno = 0, ret = 0;
}
/* parsing line by line */
- while (fgets(line, MAX_LINE, f) != NULL) {
+ while (fgets(line, LINE_MAX, f) != NULL) {
lineno++;
start = line;
- start[strcspn(start, NEWLINE)] = '\0';
- start = trim_str(start);
+ truncate_nl(start);
+ start = strstrip(start);
if (*start == COMMENT) {
continue;
}
*end = '\0';
- strncpy(section, start + 1, sizeof(section));
+ strncpy(section, start + 1, sizeof(section)-1);
section[MAX_SECTION-1] = '\0';
} else if (*start) {
/* parse name & value */
goto error;
}
*end = '\0';
- name = trim_str(start);
- value = trim_str(end + 1);
+ name = strstrip(start);
+ value = strstrip(end + 1);
end = strchr(value, COMMENT);
if (end && *end == COMMENT) {
*end = '\0';
- value = trim_str(value);
+ value = strstrip(value);
}
result.section = section;
}
}
}
- _D("Success to load %s", file_name);
- fclose(f);
return 0;
error:
return ret;
}
+static int config_table_lookup(void *table,
+ const char *section,
+ const char *lvalue,
+ ConfigParserCallback *func,
+ int *ltype,
+ void **data)
+{
+ ConfigTableItem *t;
+
+ assert(table);
+ assert(lvalue);
+ assert(func);
+ assert(ltype);
+ assert(data);
+
+ for (t = table; t->lvalue; t++) {
+
+ if (!streq(lvalue, t->lvalue))
+ continue;
+
+ if (!streq_ptr(section, t->section))
+ continue;
+
+ *func = t->cb;
+ *ltype = t->ltype;
+ *data = t->data;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Run the user supplied parser for an assignment */
+static int config_parse_table(const char *filename,
+ unsigned line,
+ void *table,
+ const char *section,
+ const char *lvalue,
+ const char *rvalue)
+{
+ ConfigParserCallback cb = NULL;
+ int ltype = 0;
+ void *data = NULL;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ r = config_table_lookup(table,
+ section,
+ lvalue,
+ &cb,
+ <ype,
+ &data);
+ if (r <= 0)
+ return r;
+
+ if (cb)
+ return cb(filename,
+ line,
+ section,
+ lvalue,
+ ltype,
+ rvalue,
+ data);
+
+ return 0;
+}
+
+int config_parse_new(const char *filename, void *table)
+{
+ _cleanup_fclose_ FILE *f = NULL;
+ char *sections[MAX_SECTION] = { 0 };
+ char *section = NULL, *n, *e, l[LINE_MAX];
+ size_t len;
+ int i, r, num_section = 0;
+ bool already;
+ unsigned line = 0;
+
+ assert(filename);
+
+ f = fopen(filename, "r");
+ if (!f) {
+ const int saved_errno = errno;
+ _E("Failed to open file %s", filename); // can modify errno
+ return -saved_errno;
+ }
+
+ while (!feof(f)) {
+ _cleanup_free_ char *lvalue = NULL, *rvalue = NULL;
+ char *rs = NULL;
+
+ if (fgets(l, LINE_MAX, f) == NULL) {
+ if (feof(f))
+ break;
+
+ _E("Failed to parse configuration file '%s': %m", filename);
+ r = -errno;
+ goto finish;
+ }
+
+ line++;
+ truncate_nl(l);
+
+ if (strchr(COMMENTS NEWLINE, *l))
+ continue;
+
+ if (*l == '[') {
+ len = strlen(l);
+ if (l[len-1] != ']') {
+ _E("Error: Invalid section header: %s", l);
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ n = strndup(l+1, len-2);
+ if (!n) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ already = false;
+ for (i = 0; i < num_section; i++) {
+ if (streq(n, sections[i])) {
+ section = sections[i];
+ already = true;
+ free(n);
+ break;
+ }
+ }
+
+ if (already)
+ continue;
+
+ section = n;
+ sections[num_section] = n;
+ num_section++;
+ if (num_section > MAX_SECTION) {
+ _E("Error: max number of section reached: %d", num_section);
+ r = -EOVERFLOW;
+ goto finish;
+ }
+
+ continue;
+ }
+
+ if (!section)
+ continue;
+
+ e = strchr(l, '=');
+ if (e == NULL) {
+ _D("Warning: config: no '=' character in line '%s'.", l);
+ continue;
+ }
+
+ lvalue = strndup(l, e-l);
+ if (!lvalue) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strstrip(lvalue);
+
+ rs = strstrip(e+1);
+ rvalue = strdup(rs);
+ if (!rvalue) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strstrip(rvalue);
+
+ r = config_parse_table(filename,
+ line,
+ table,
+ section,
+ lvalue,
+ rvalue);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ for (i = 0; i < num_section; i++)
+ if (sections[i])
+ free(sections[i]);
+
+ return r;
+}
+
+int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data)
+{
+ _cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
+
+ d = opendir(dir);
+ if (!d) {
+ _E("Failed to open dir: %s", dir);
+ return errno;
+ }
+
+ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) {
+ if (!de) {
+ if (errno > 0)
+ return -errno;
+ break;
+ } else if (streq(de->d_name, ".") || streq(de->d_name, "..")) {
+ continue;
+ }
+
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ if (de->d_type != DT_REG)
+ continue;
+
+ r = asprintf(&path, "%s/%s", dir, de->d_name);
+ if (r < 0)
+ return -ENOMEM;
+
+ r = fp(path, data);
+ /* Do not just break loop until parse all file of
+ * dir. Just only put log */
+ if (r < 0)
+ _D("Failed to parse config: %s", de->d_name);
+ }
+
+ return 0;
+}
+
+int config_parse_bool(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ int k;
+ bool *b = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ k = parse_boolean(rvalue);
+ if (k < 0) {
+ _E("Failed to parse boolean value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *b = !!k;
+
+ return 0;
+}
+
+#define DEFINE_PARSER(type, vartype, conv_func) \
+ int config_parse_##type( \
+ const char *filename, \
+ unsigned line, \
+ const char *section, \
+ const char *lvalue, \
+ int ltype, \
+ const char *rvalue, \
+ void *data) { \
+ \
+ vartype *i = data; \
+ \
+ assert(filename); \
+ assert(lvalue); \
+ assert(rvalue); \
+ assert(data); \
+ \
+ *i = conv_func(rvalue); \
+ return 0; \
+ }
+
+DEFINE_PARSER(int, int, atoi)
+DEFINE_PARSER(float, float, atof)
+DEFINE_PARSER(long, long, atol)
+
+int config_parse_string(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ char **s = data, *n;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (is_empty(rvalue))
+ n = NULL;
+ else {
+ n = strdup(rvalue);
+ if (!n)
+ return -ENOMEM;
+ }
+
+ free(*s);
+ *s = n;
+
+ return 0;
+}
+
+int config_parse_bytes(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ size_t *s = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (is_empty(rvalue))
+ *s = 0;
+ else {
+ r = parse_bytes(rvalue, s);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int config_parse_strv(const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+ char ***strv = data;
+ char **o = NULL, **v = NULL, **vv = NULL;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (is_empty(rvalue))
+ return 0;
+
+ r = str_to_strv(rvalue, &v, WHITESPACE);
+ if (r < 0)
+ return r;
+
+ o = *strv;
+
+ r = strv_attach(o, v, &vv, true);
+ if (r < 0)
+ return r;
+
+ *strv = vv;
+
+ return 0;
+}