Release 5.5.35
[platform/core/system/crash-worker.git] / src / shared / config.c
1 /*
2  * crash-manager
3  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the License);
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <assert.h>
18 #include <dirent.h>
19 #include <fcntl.h>
20 #include <iniparser.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26
27 #include "config.h"
28 #include "defs.h"
29 #include "log.h"
30 #include "util.h"
31
32 static const char *const report_type_strmap[] = {
33         [REP_TYPE_INFO] = "INFO",
34         [REP_TYPE_FULL] = "FULL",
35 };
36
37 static const char *report_type_to_str(enum ReportType report_type)
38 {
39         if (report_type < 0 || report_type >= ARRAY_SIZE(report_type_strmap))
40                 return NULL;
41         return report_type_strmap[report_type];
42 }
43
44 enum ReportType report_type_from_str(const char *report_type_str)
45 {
46         assert(report_type_str);
47
48         for (int i = 0; i < (int)ARRAY_SIZE(report_type_strmap); i++) {
49                 if (0 == strcasecmp(report_type_str, report_type_strmap[i]))
50                         return (enum ReportType)i;
51         }
52
53         return REP_TYPE_INVALID;
54 }
55
56 static int config_load_exclude_paths(config_t *c, dictionary *ini)
57 {
58         assert(c);
59         assert(ini);
60
61         int n = iniparser_getsecnkeys(ini, EXCLUDEPATHS_SECTION);
62         _D("config: Found %d entries in " EXCLUDEPATHS_SECTION, n);
63         if (n <= 0)
64                 return 0;
65
66         char **keys = alloca(sizeof(char *) * n);
67         int total = n + c->n_exclude_paths;
68         int n_added = 0;
69
70         c->exclude_paths = realloc(c->exclude_paths, sizeof(char *) * total);
71
72         if (!keys || !c->exclude_paths)
73                 goto err_oom;
74
75         keys = iniparser_getseckeys(ini, EXCLUDEPATHS_SECTION);
76         if (!keys)
77                 goto err_oom;
78
79         for (int i = c->n_exclude_paths, j = 0; i < total; i++, j++) {
80                 const char *const str = iniparser_getstring(ini, keys[j], NULL);
81                 if (!str)
82                         continue;
83
84                 _D("config: Adding <%s> to exclude paths list", str);
85                 c->exclude_paths[i] = strdup(str);
86                 if (!c->exclude_paths[i])
87                         goto err_oom;
88                 c->n_exclude_paths += 1;
89                 n_added += 1;
90         }
91         return n_added;
92
93 err_oom:
94         _E("Out of memory. ExcludePaths configuration not loaded.");
95         return n_added;
96 }
97
98 bool config_is_path_excluded(config_t *c, const char *const path)
99 {
100         assert(c);
101         assert(path);
102
103         for (int i = 0; i < c->n_exclude_paths; i++) {
104                 if (strcmp(path, c->exclude_paths[i]) == 0) {
105                         _I("Found match on exclude paths list: %s", path);
106                         return true;
107                 }
108         }
109         return false;
110 }
111
112 static bool config_load_from_dict(config_t *c, dictionary *ini)
113 {
114         assert(c);
115         assert(ini);
116
117         const char *str = iniparser_getstring(ini, CRASH_SECTION ":CrashRootPath", NULL);
118         if (str) {
119                 char *crash_root_path = strdup(str);
120                 if (!crash_root_path) {
121                         _E("config: Unable to set CrashRootPath - aborting");
122                         return false;
123                 }
124
125                 free(c->crash_root_path);
126                 c->crash_root_path = crash_root_path;
127         }
128
129         /* strdup() can technically fail, but we don't mind. It is better to
130          * create a report without the extra script than to abort completely. */
131         str = iniparser_getstring(ini, CRASH_SECTION ":ExtraScript", NULL);
132         if (str) {
133                 char *extra_script = strdup(str);
134                 if (extra_script) {
135                         free(c->extra_script);
136                         c->extra_script = extra_script;
137                 } else
138                         _W("Out of memory. ExtraScript will not be executed.");
139         }
140
141         str = iniparser_getstring(ini, CRASH_SECTION ":ReportType", NULL);
142         if (str) {
143                 int type = report_type_from_str(str);
144                 if (report_type_to_str(type))
145                         c->report_type = type;
146         }
147
148 #define UPDATE(where, type, key) where = iniparser_get##type(ini, CRASH_SECTION ":" key, where)
149
150         UPDATE(c->system_max_use, int, "SystemMaxUse");
151         UPDATE(c->max_retention_sec, int, "MaxRetentionSec");
152         UPDATE(c->max_crash_dump, int, "MaxCrashDump");
153         UPDATE(c->dump_core, boolean, "DumpCore");
154         UPDATE(c->dump_so_info, boolean, "DumpSharedObjectInfo");
155         UPDATE(c->allow_zip, boolean, "AllowZip");
156         UPDATE(c->legacy_notification, boolean, "UseLegacyNotification");
157
158 #undef UPDATE
159
160         config_load_exclude_paths(c, ini);
161
162         return true;
163 }
164
165 static bool config_load_from_path(config_t *c, const char *const path)
166 {
167         assert(path);
168
169         dictionary *ini = iniparser_load(path);
170         if (!ini) {
171                 _E("Failed to load config file %s", path);
172                 return false;
173         }
174
175         bool ret = config_load_from_dict(c, ini);
176         iniparser_freedict(ini);
177
178         return ret;
179 }
180
181 static int entry_filter(const struct dirent *e)
182 {
183         assert(e);
184
185         const char *const conf_suffix = ".conf";
186         const char *const name = e->d_name;
187         int len = strlen(name);
188
189         if (e->d_type != DT_REG || len <= strlen(conf_suffix))
190                 return 0;
191
192         // accept only files ending with predefined suffix
193         return strcmp(name + len - strlen(conf_suffix), conf_suffix) == 0;
194 }
195
196 static bool config_load_from_dir_prefix(config_t *c, const char *const dir_prefix)
197 {
198         assert(dir_prefix);
199
200         char dir_path[PATH_MAX];
201         int ret = snprintf(dir_path, sizeof(dir_path), "%s.d", dir_prefix);
202         if (ret < 0 || ret >= PATH_MAX) {
203                 _W("config: internal error while trying to prepare config dir path");
204                 return false;
205         }
206
207         int fd = open(dir_path, O_RDONLY | O_DIRECTORY);
208         if (fd < 0 && errno == ENOENT)
209                 return true;
210         if (fd < 0) {
211                 _E("config: Unable to access config directory at %s: %m", dir_path);
212                 return false;
213         }
214
215         struct dirent **entries = NULL;
216         int n = scandirat(fd, ".", &entries, entry_filter, alphasort);
217         if (n < 0)
218                 goto out;
219
220         for (int i = 0; i < n; ++i) {
221                 char file_path[PATH_MAX];
222                 const char *fname = entries[i]->d_name;
223                 ret = snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, fname);
224                 if (ret < 0 || ret >= PATH_MAX) {
225                         _W("config: internal error while trying to prepare for reading %s config file", fname);
226                         continue;
227                 }
228                 _D("config: reading additional configuration file from %s", file_path);
229                 (void)config_load_from_path(c, file_path);
230         }
231
232 out:
233         free(entries);
234         close(fd);
235         return true;
236 }
237
238 static bool config_apply_defaults(config_t *c)
239 {
240         assert(c);
241
242         memset(c, 0, sizeof(*c));
243
244         c->crash_root_path = strdup(CRASH_ROOT_PATH);
245         c->report_type = REP_TYPE_FULL;
246         c->system_max_use = SYSTEM_MAX_USE;
247         c->system_keep_free = SYSTEM_KEEP_FREE;
248         c->max_retention_sec = MAX_RETENTION_SEC;
249         c->max_crash_dump = MAX_CRASH_DUMP;
250         c->dump_core = DUMP_CORE;
251         c->dump_so_info = DUMP_SO_INFO;
252         c->allow_zip = ALLOW_ZIP;
253         c->legacy_notification = LEGACY_NOTIFICATION;
254
255         return c->crash_root_path != NULL;
256 }
257
258 static void config_dump(config_t *c)
259 {
260         assert(c);
261
262         _D("config: crash_root_path = %s\n"
263            "config: extra_script = %s\n"
264            "config: report_type = %s\n"
265            "config: system_max_use = %d\n"
266            "config: system_keep_free = %d\n"
267            "config: max_retention_sec = %d\n"
268            "config: max_crash_dump = %d\n"
269            "config: dump_core = %d\n"
270            "config: dump_so_info = %d\n"
271            "config: allow_zip = %d\n"
272            "config: legacy_notification = %d\n",
273            c->crash_root_path,
274            c->extra_script,
275            report_type_to_str(c->report_type),
276            c->system_max_use,
277            c->system_keep_free,
278            c->max_retention_sec,
279            c->max_crash_dump,
280            c->dump_core,
281            c->dump_so_info,
282            c->allow_zip,
283            c->legacy_notification);
284 }
285
286 bool config_init(config_t *c, const char *const path)
287 {
288         if (!config_apply_defaults(c) || !config_load_from_path(c, path)) {
289                 _E("config: Unable to initialize configuration");
290                 return false;
291         }
292
293         (void)config_load_from_dir_prefix(c, path);
294
295         config_dump(c);
296
297         return true;
298 }
299
300 void config_free(config_t *c)
301 {
302         assert(c);
303
304         free(c->crash_root_path);
305         free(c->extra_script);
306         for (int i = 0; i < c->n_exclude_paths; i++)
307                 free(c->exclude_paths[i]);
308         free(c->exclude_paths);
309
310         memset(c, 0, sizeof(*c));
311 }