dump_systemstate-service: Fix a build error on GCC-13
[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) {
96                         _E("Out of memory");
97                         if (result != NULL)
98                                 free(result);
99                         result = NULL;
100
101                         if (chunk_copy != NULL)
102                                 free(chunk_copy);
103                         goto out;
104                 }
105
106                 if (chunk_copy == NULL) {
107                         _E("Out of memory");
108                         free(result_tmp);
109                         result = NULL;
110                         goto out;
111                 }
112
113                 result = result_tmp;
114                 result[(*size)++] = chunk_copy;
115                 chunk = strtok_r(NULL, " ", &tmpptr);
116         }
117
118         char **result_tmp = realloc(result, (*size + 1) * sizeof(char *));
119         if (result_tmp == NULL) {
120                 _E("Out of memory");
121                 free(result);
122                 result = NULL;
123                 goto out;
124         }
125
126         result = result_tmp;
127         result[*size] = NULL;
128
129 out:
130         free(str_tmp);
131         return result;
132 }
133
134 static void free_array(char **array)
135 {
136         if (array != NULL) {
137                 for (size_t i = 0; array[i] != NULL; i++)
138                         free(array[i]);
139                 free(array);
140         }
141 }
142
143 static char** build_argv_array(const char *path, int argc, char **argv)
144 {
145         char **av = malloc((argc + 2) * sizeof(char *));
146         if (av == NULL) {
147                 _E("Out of memory");
148                 return NULL;
149         }
150
151         av[0] = strdup(path);
152         if (av[0] == NULL) {
153                 _E("Out of memory");
154                 free(av);
155                 return NULL;
156         }
157
158         for (int i = 0; i < argc; i++) {
159                 av[i + 1] = strdup(argv[i]);
160                 if (av[i + 1] == NULL) {
161                         _E("Out of memory");
162                         free_array(av);
163                         return NULL;
164                 }
165         }
166
167         av[argc + 1] = NULL;
168
169         return av;
170 }
171
172 static char** prepare_dumpsys_args(const char *path, int dumpsys_argc, char **dumpsys_argv)
173 {
174         return build_argv_array(path, dumpsys_argc - 1, dumpsys_argv+1);
175 }
176
177 static char** prepare_config_args(const char *path, char *args)
178 {
179         size_t argc = 0;
180         char **argv = split(args, &argc);
181         if (argv == NULL)
182                 return NULL;
183
184         char **result = build_argv_array(path, argc, argv);
185         free_array(argv);
186         return result;
187 }
188
189 static char** prepare_args(struct extra_dump_item *item, const int dumpsys_argc, char **dumpsys_argv)
190 {
191         bool allow_dumpsys_args = false;
192         if (item->fields[INI_FIELD_ALLOWDUMPSYSARGS] != NULL)
193                 allow_dumpsys_args = strncasecmp(item->fields[INI_FIELD_ALLOWDUMPSYSARGS], "yes", 4) == 0;
194
195         if (allow_dumpsys_args && dumpsys_argc > 1)
196                 return prepare_dumpsys_args(item->fields[INI_FIELD_PATH], dumpsys_argc, dumpsys_argv);
197         else
198                 return prepare_config_args(item->fields[INI_FIELD_PATH], item->fields[INI_FIELD_ARGS]);
199 }
200
201 static char** prepare_env(const char *config_env)
202 {
203         char **ev = NULL;
204
205         if (config_env != NULL) {
206                 size_t size = 0;
207                 ev = split(config_env, &size);
208         }
209
210         return ev;
211 }
212
213 static int dump_item(int fd, struct extra_dump_item *item, const int dumpsys_argc, char **dumpsys_argv)
214 {
215         assert(item);
216
217         int result = TIZEN_ERROR_NONE;
218
219         char **av = prepare_args(item, dumpsys_argc, dumpsys_argv);
220
221         if (av == NULL)
222                 goto out;
223
224         char **ev = prepare_env(item->fields[INI_FIELD_ENV]);
225         int err;
226         spawn_param_s param_stdout = { .fn = spawn_setstdout, .u.int_val = fd };
227         spawn_param_s param_stderr = { .fn = spawn_setstderr, .u.int_val = fd, .next = &param_stdout };
228
229         if (!spawn_wait(av, ev, &param_stderr, DEFAULT_COMMAND_TIMEOUT_MS, &err))
230                 result = TIZEN_ERROR_INVALID_OPERATION;
231
232         free_array(ev);
233 out:
234         free_array(av);
235
236         return result;
237 }
238
239 static bool dumpsys_enabled(struct extra_dump_item *item)
240 {
241         assert(item);
242
243         return item->fields[INI_FIELD_DUMPSYSCMD] != NULL && *(item->fields[INI_FIELD_DUMPSYSCMD]) != '\0';
244 }
245
246 static void print_header(int fd, struct extra_dump_item *item)
247 {
248         assert(item);
249         dprintf(fd,
250                 "\n==== %s (%s %s)\n",
251                 item->fields[INI_FIELD_TITLE] ?: "",
252                 item->fields[INI_FIELD_PATH] ?: "",
253                 item->fields[INI_FIELD_ARGS] ?: "");
254 }
255
256 static int dump_all_items(int fd)
257 {
258         int result = TIZEN_ERROR_NONE;
259
260         for (size_t i = 0; i < commands_list.size; i++) {
261                 if (dumpsys_enabled(&commands_list.data[i])) {
262                         print_header(fd, &commands_list.data[i]);
263                         result |= dump_item(fd, &commands_list.data[i], 0, NULL);
264                 }
265         }
266
267         return result;
268 }
269
270 static int dumpsys_cb(const int fd, const int argc, char **argv)
271 {
272         assert(fd >= 0);
273         assert(argv);
274
275         if (commands == NULL) {
276                 _E("Commands not initialized");
277                 return TIZEN_ERROR_INVALID_OPERATION;
278         }
279
280         int result = TIZEN_ERROR_NONE;
281         if (argc == 0) {
282                 result = dump_all_items(fd);
283         } else {
284                 struct extra_dump_item *item = g_hash_table_lookup(commands, argv[0]);
285                 if (item == NULL) {
286                         _E("Command not found: %s", argv[0]);
287                         return TIZEN_ERROR_INVALID_PARAMETER;
288                 }
289                 result = dump_item(fd, item, argc, argv);
290         }
291
292         return result;
293 }
294
295 static void free_item(struct extra_dump_item *item)
296 {
297         assert(item);
298
299         for (size_t i = 0; i < ARRAY_SIZE(item->fields); i++)
300                 free(item->fields[i]);
301 }
302
303 static int fill_commands()
304 {
305         int result = read_ini_files(STDOUT_FILENO, DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH, &commands_list);
306
307         if (result != EXIT_OK)
308                 return result;
309
310         commands = g_hash_table_new(g_str_hash, g_str_equal);
311         if (commands == NULL) {
312                 _E("Out of memory");
313                 return EXIT_ERR;
314         }
315
316         struct extra_dump_item *item = commands_list.data;
317
318         for (size_t i = 0; i < commands_list.size; i++, item++) {
319                 char *dumpcmd = item->fields[INI_FIELD_DUMPSYSCMD];
320
321                 if (dumpcmd != NULL && strlen(dumpcmd) > 0) {
322                         if (g_hash_table_lookup(commands, dumpcmd) != NULL) {
323                                 _I("Duplicated command: %s", dumpcmd);
324                                 free_item(item);
325                         } else {
326                                 _D("New dumpsys command: %s", dumpcmd);
327                                 g_hash_table_insert(commands, dumpcmd, item);
328                         }
329                 }
330         }
331
332         return result;
333 }
334
335 int main()
336 {
337         void *handler = NULL;
338         int exit_code = EXIT_SUCCESS;
339
340         if (fill_commands() != EXIT_OK) {
341                 exit_code = EXIT_FAILURE;
342                 goto out;
343         }
344
345         loop = g_main_loop_new(NULL, false);
346
347         if (dumpsys_system_register_dump_cb(dumpsys_cb, DUMP_SYSTEMSTATE_BUS_NAME, &handler) != TIZEN_ERROR_NONE) {
348                 _E("Callback registration error");
349                 exit_code = EXIT_FAILURE;
350                 goto out;
351         }
352
353         g_mutex_init(&timeout_mutex);
354         add_timeout();
355         g_main_loop_run(loop);
356
357         g_mutex_clear(&timeout_mutex);
358
359 out:
360         if (loop != NULL)
361                 g_main_loop_unref(loop);
362
363         if (handler != NULL)
364                 dumpsys_system_unregister_dump_cb(handler);
365
366         if (commands != NULL)
367                 g_hash_table_destroy(commands);
368
369         for (size_t i = 0; i < commands_list.size; i++)
370                 free_item(&commands_list.data[i]);
371
372         return exit_code;
373 }