Parse 'PerProcess' section
[platform/core/system/resourced.git] / src / common / config-parser.c
index e184a5b..b6b85b5 100644 (file)
 #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;
@@ -67,12 +348,12 @@ int config_parse(const char *file_name, int cb(struct parse_result *result,
        }
 
        /* 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;
@@ -85,7 +366,7 @@ int config_parse(const char *file_name, int cb(struct parse_result *result,
                        }
 
                        *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 */
@@ -95,12 +376,12 @@ int config_parse(const char *file_name, int cb(struct parse_result *result,
                                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;
@@ -114,8 +395,6 @@ int config_parse(const char *file_name, int cb(struct parse_result *result,
                        }
                }
        }
-       _D("Success to load %s", file_name);
-       fclose(f);
        return 0;
 
 error:
@@ -125,3 +404,379 @@ 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,
+                               &ltype,
+                               &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;
+}