da234f01b7e9cfb1a3ad446383a32e5e8aabc556
[platform/core/system/crash-worker.git] / src / dump_systemstate-service / dump_systemstate-service.c
1 /*
2  * dump_systemstate
3  *
4  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <assert.h>
20 #include <dumpsys-system.h>
21 #include <gio/gio.h>
22 #include <stdio.h>
23 #include <unistd.h>
24
25 #include "dump_systemstate/dump_systemstate.h"
26 #include "dump_systemstate/extras.h"
27 #include "shared/log.h"
28 #include "shared/spawn.h"
29 #include "shared/util.h"
30
31 #define DUMP_SYSTEMSTATE_BUS_NAME            "org.tizen.systemstate"
32 #define DUMP_SYSTEMSTATE_OBJECT_PATH         "/Org/Tizen/Systemstate"
33 #define DUMP_SYSTEMSTATE_INTERFACE_NAME      DUMP_SYSTEMSTATE_BUS_NAME
34 #define TIMEOUT_INTERVAL_SEC 30
35
36 static GMainLoop *loop = NULL;
37 static GMutex timeout_mutex;
38 static guint timeout_id;
39 static GHashTable *commands = NULL;
40 struct extra_items_vector commands_list = { .size = 0, .data = NULL };
41
42 static int timeout_cb(gpointer data)
43 {
44         assert(data);
45         _I("Time out!");
46         g_main_loop_quit((GMainLoop *)data);
47
48         return 0;
49 }
50
51 static void add_timeout(void)
52 {
53         g_mutex_lock(&timeout_mutex);
54
55         if (timeout_id)
56                 g_source_remove(timeout_id);
57         timeout_id = g_timeout_add_seconds(TIMEOUT_INTERVAL_SEC, timeout_cb, loop);
58
59         g_mutex_unlock(&timeout_mutex);
60         _D("Add loop timeout (%d)", TIMEOUT_INTERVAL_SEC);
61 }
62
63 static void remove_timeout(void)
64 {
65         g_mutex_lock(&timeout_mutex);
66
67         if (timeout_id) {
68                 g_source_remove(timeout_id);
69                 timeout_id = 0;
70         }
71
72         g_mutex_unlock(&timeout_mutex);
73         _D("Remove loop timeout");
74 }
75
76 char** split(const char *str, size_t *size)
77 {
78         assert(str);
79
80         char **result = NULL;
81         char *str_tmp = strdup(str);
82         if (str_tmp == NULL) {
83                 _E("Out of memory");
84                 return NULL;
85         }
86
87         char *tmpptr = NULL;
88         char *chunk = strtok_r(str_tmp, " ", &tmpptr);
89         *size = 0;
90
91         while (chunk != NULL) {
92                 char **result_tmp = realloc(result, (*size + 1) * sizeof(char *));
93                 char *chunk_copy = strdup(chunk);
94
95                 if (result_tmp == NULL || chunk_copy == NULL) {
96                         _E("Out of memory");
97                         if (result_tmp != NULL)
98                                 free(result_tmp);
99                         if (chunk_copy != NULL)
100                                 free(chunk_copy);
101                         free(result);
102                         result = NULL;
103                         goto out;
104                 }
105
106                 result = result_tmp;
107                 result[(*size)++] = chunk_copy;
108                 chunk = strtok_r(NULL, " ", &tmpptr);
109         }
110
111         char **result_tmp = realloc(result, (*size + 1) * sizeof(char *));
112         if (result_tmp == NULL) {
113                 _E("Out of memory");
114                 free(result);
115                 result = NULL;
116                 goto out;
117         }
118
119         result = result_tmp;
120         result[*size] = NULL;
121
122 out:
123         free(str_tmp);
124         return result;
125 }
126
127 static void free_array(char **array)
128 {
129         if (array != NULL) {
130                 for (size_t i = 0; array[i] != NULL; i++)
131                         free(array[i]);
132                 free(array);
133         }
134 }
135
136 static char** build_argv_array(const char *path, int argc, char **argv)
137 {
138         char **av = malloc((argc + 2) * sizeof(char *));
139         if (av == NULL) {
140                 _E("Out of memory");
141                 return NULL;
142         }
143
144         av[0] = strdup(path);
145         if (av[0] == NULL) {
146                 _E("Out of memory");
147                 free(av);
148                 return NULL;
149         }
150
151         for (int i = 0; i < argc; i++) {
152                 av[i + 1] = strdup(argv[i]);
153                 if (av[i + 1] == NULL) {
154                         _E("Out of memory");
155                         free_array(av);
156                         return NULL;
157                 }
158         }
159
160         av[argc + 1] = NULL;
161
162         return av;
163 }
164
165 static char** prepare_dumpsys_args(const char *path, int dumpsys_argc, char **dumpsys_argv)
166 {
167         return build_argv_array(path, dumpsys_argc - 1, dumpsys_argv+1);
168 }
169
170 static char** prepare_config_args(const char *path, char *args)
171 {
172         size_t argc = 0;
173         char **argv = split(args, &argc);
174         if (argv == NULL)
175                 return NULL;
176
177         char **result = build_argv_array(path, argc, argv);
178         free_array(argv);
179         return result;
180 }
181
182 static char** prepare_args(struct extra_dump_item *item, const int dumpsys_argc, char **dumpsys_argv)
183 {
184         bool allow_dumpsys_args = false;
185         if (item->fields[INI_FIELD_ALLOWDUMPSYSARGS] != NULL)
186                 allow_dumpsys_args = strncasecmp(item->fields[INI_FIELD_ALLOWDUMPSYSARGS], "yes", 4) == 0;
187
188         if (allow_dumpsys_args && dumpsys_argc > 1)
189                 return prepare_dumpsys_args(item->fields[INI_FIELD_PATH], dumpsys_argc, dumpsys_argv);
190         else
191                 return prepare_config_args(item->fields[INI_FIELD_PATH], item->fields[INI_FIELD_ARGS]);
192 }
193
194 static char** prepare_env(const char *config_env)
195 {
196         char **ev = NULL;
197
198         if (config_env != NULL) {
199                 size_t size = 0;
200                 ev = split(config_env, &size);
201         }
202
203         return ev;
204 }
205
206 static int dump_item(int fd, struct extra_dump_item *item, const int dumpsys_argc, char **dumpsys_argv)
207 {
208         assert(item);
209
210         int result = TIZEN_ERROR_NONE;
211
212         char **av = prepare_args(item, dumpsys_argc, dumpsys_argv);
213
214         if (av == NULL)
215                 goto out;
216
217         char **ev = prepare_env(item->fields[INI_FIELD_ENV]);
218         int err;
219         spawn_param_s param_stdout = { .fn = spawn_setstdout, .u.int_val = fd };
220         spawn_param_s param_stderr = { .fn = spawn_setstderr, .u.int_val = fd, .next = &param_stdout };
221
222         if (!spawn_wait(av, ev, &param_stderr, DEFAULT_COMMAND_TIMEOUT_MS, &err))
223                 result = TIZEN_ERROR_INVALID_OPERATION;
224
225         free_array(ev);
226 out:
227         free_array(av);
228
229         return result;
230 }
231
232 static bool dumpsys_enabled(struct extra_dump_item *item)
233 {
234         assert(item);
235
236         return item->fields[INI_FIELD_DUMPSYSCMD] != NULL && *(item->fields[INI_FIELD_DUMPSYSCMD]) != '\0';
237 }
238
239 static void print_header(int fd, struct extra_dump_item *item)
240 {
241         assert(item);
242         dprintf(fd,
243                 "\n==== %s (%s %s)\n",
244                 item->fields[INI_FIELD_TITLE] ?: "",
245                 item->fields[INI_FIELD_PATH] ?: "",
246                 item->fields[INI_FIELD_ARGS] ?: "");
247 }
248
249 static int dump_all_items(int fd)
250 {
251         int result = TIZEN_ERROR_NONE;
252
253         for (size_t i = 0; i < commands_list.size; i++) {
254                 if (dumpsys_enabled(&commands_list.data[i])) {
255                         print_header(fd, &commands_list.data[i]);
256                         result |= dump_item(fd, &commands_list.data[i], 0, NULL);
257                 }
258         }
259
260         return result;
261 }
262
263 static int dumpsys_cb(const int fd, const int argc, char **argv)
264 {
265         assert(fd >= 0);
266         assert(argv);
267
268         if (commands == NULL) {
269                 _E("Commands not initialized");
270                 return TIZEN_ERROR_INVALID_OPERATION;
271         }
272
273         int result = TIZEN_ERROR_NONE;
274         if (argc == 0) {
275                 result = dump_all_items(fd);
276         } else {
277                 struct extra_dump_item *item = g_hash_table_lookup(commands, argv[0]);
278                 if (item == NULL) {
279                         _E("Command not found: %s", argv[0]);
280                         return TIZEN_ERROR_INVALID_PARAMETER;
281                 }
282                 result = dump_item(fd, item, argc, argv);
283         }
284
285         return result;
286 }
287
288 static void free_item(struct extra_dump_item *item)
289 {
290         assert(item);
291
292         for (size_t i = 0; i < ARRAY_SIZE(item->fields); i++)
293                 free(item->fields[i]);
294 }
295
296 static int fill_commands()
297 {
298         int result = read_ini_files(STDOUT_FILENO, DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH, &commands_list);
299
300         if (result != EXIT_OK)
301                 return result;
302
303         commands = g_hash_table_new(g_str_hash, g_str_equal);
304         if (commands == NULL) {
305                 _E("Out of memory");
306                 return EXIT_ERR;
307         }
308
309         struct extra_dump_item *item = commands_list.data;
310
311         for (size_t i = 0; i < commands_list.size; i++, item++) {
312                 char *dumpcmd = item->fields[INI_FIELD_DUMPSYSCMD];
313
314                 if (dumpcmd != NULL && strlen(dumpcmd) > 0) {
315                         if (g_hash_table_lookup(commands, dumpcmd) != NULL) {
316                                 _I("Duplicated command: %s", dumpcmd);
317                                 free_item(item);
318                         } else {
319                                 _D("New dumpsys command: %s", dumpcmd);
320                                 g_hash_table_insert(commands, dumpcmd, item);
321                         }
322                 }
323         }
324
325         return result;
326 }
327
328 int main()
329 {
330         void *handler = NULL;
331         int exit_code = EXIT_SUCCESS;
332
333         if (fill_commands() != EXIT_OK) {
334                 exit_code = EXIT_FAILURE;
335                 goto out;
336         }
337
338         loop = g_main_loop_new(NULL, false);
339
340         if (dumpsys_system_register_dump_cb(dumpsys_cb, DUMP_SYSTEMSTATE_BUS_NAME, &handler) != TIZEN_ERROR_NONE) {
341                 _E("Callback registration error");
342                 exit_code = EXIT_FAILURE;
343                 goto out;
344         }
345
346         g_mutex_init(&timeout_mutex);
347         add_timeout();
348         g_main_loop_run(loop);
349
350         g_mutex_clear(&timeout_mutex);
351
352 out:
353         if (loop != NULL)
354                 g_main_loop_unref(loop);
355
356         if (handler != NULL)
357                 dumpsys_system_unregister_dump_cb(handler);
358
359         if (commands != NULL)
360                 g_hash_table_destroy(commands);
361
362         for (size_t i = 0; i < commands_list.size; i++)
363                 free_item(&commands_list.data[i]);
364
365         return exit_code;
366 }