ca010e5cca1143a64c903ef15ebbe36e61956625
[platform/core/system/crash-worker.git] / src / crash-manager / dbus_notify.c
1 /*
2  * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * Authors: Mateusz Moscicki <m.moscicki2@partner.samsung.com>
17  *          Karol Lewandowski <k.lewandowsk@samsung.com>
18  */
19
20 #define LOG_TAG "CRASH_MANAGER"
21 #include "shared/log.h"
22 #include "dbus-util.h"
23 #include "defs.h"
24
25 #include <assert.h>
26 #include <fcntl.h>
27 #include <getopt.h>
28 #include <gio/gio.h>
29 #include <stdbool.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/mman.h>
34 #include <sys/procfs.h>
35 #include <unistd.h>
36
37 #define CRASH_BUS_NAME          "org.tizen.system.crash"
38 #define CRASH_OBJECT_PATH       "/Org/Tizen/System/Crash"
39 #define CRASH_INTERFACE_NAME    CRASH_BUS_NAME
40 #define CRASH_PATH_CRASH        CRASH_OBJECT_PATH"/Crash"
41 #define CRASH_INTERFACE_CRASH   CRASH_INTERFACE_NAME".Crash"
42 #define PROCESS_CRASHED         "ProcessCrashed"
43
44 #define ARM_REG_LR 14
45 #define ARM_REG_PC 15
46 #define AARCH64_REG_LR 30
47
48 struct RegInfo {
49         char *name;
50         long long int value;
51 };
52
53 struct NotifyParams {
54         bool legacy_notification;
55         int prstatus_fd;
56         pid_t pid;
57         pid_t tid;
58         int signal;
59         char *cmd_name;
60         char *cmd_path;
61         char *report_path;
62         char *appid;
63         char *pkgid;
64         char *tid_comm;
65 };
66
67 static int _get_important_registers(int fd, struct RegInfo **reg_info)
68 {
69         int count = -1;
70
71         if (reg_info == NULL) {
72                 _E("reg_info is NULL\n");
73                 return -1;
74         }
75
76         *reg_info = NULL;
77         struct elf_prstatus *prstatus = mmap(NULL, sizeof(struct elf_prstatus),
78                                          PROT_READ, MAP_SHARED, fd, 0);
79         if (prstatus == MAP_FAILED) {
80                 _E("mmap error: %m\n");
81                 return -1;
82         }
83
84 #if (defined __i386__) || (defined __x86_64__)
85         count = 1;
86 #elif (defined __arm__) || (defined __aarch64__)
87         count = 2;
88 #endif
89
90         *reg_info = (struct RegInfo*)malloc(sizeof(struct RegInfo)*count);
91         struct RegInfo *reginfo = *reg_info;
92
93         if (*reg_info == NULL) {
94                 _E("malloc for reg_info error: %m");
95                 goto out;
96         }
97
98 #if (defined __i386__) || (defined __x86_64__)
99         struct user_regs_struct *g_registers = (struct user_regs_struct*)prstatus->pr_reg;
100
101 #if defined(__i386__)
102         reginfo[0].name = strdup("x86.eip");
103         reginfo[0].value = (long long int)g_registers->eip;
104 #else
105         reginfo[0].name = strdup("x86_64.rip");
106         reginfo[0].value = (long long int)g_registers->rip;
107 #endif // defined(__i386__)
108
109         if (reginfo[0].name == NULL)
110                 goto strdup_error;
111
112 #elif defined(__arm__)
113
114         struct user_regs *g_registers;
115         g_registers = (struct user_regs*)prstatus->pr_reg;
116
117         if ((reginfo[0].name = strdup("arm.pc")) == NULL)
118                 goto strdup_error;
119         reginfo[0].value = (long long int)g_registers->uregs[ARM_REG_PC];
120         if ((reginfo[1].name = strdup("arm.lr")) == NULL)
121                 goto strdup_error;
122         reginfo[1].value = (long long int)g_registers->uregs[ARM_REG_LR];
123
124 #elif defined(__aarch64__)
125
126         struct user_regs_struct *g_registers = (struct user_regs_struct*)prstatus->pr_reg;
127
128         if ((reginfo[0].name = strdup("aarch64.pc")) == NULL)
129                 goto strdup_error;
130         reginfo[0].value = (long long int)g_registers->pc;
131         if ((reginfo[1].name = strdup("aarch64.lr")) == NULL)
132                 goto strdup_error;
133         reginfo[1].value = (long long int)g_registers->regs[AARCH64_REG_LR];
134
135 #endif // (defined __i386__) || (defined __x86_64__)
136
137 out:
138         if (count <= 0) {
139                 free(*reg_info);
140                 *reg_info = NULL;
141         }
142
143         munmap(prstatus, sizeof(*prstatus));
144         return count;
145
146 strdup_error:
147         _E("strdup error: %m\n");
148         count = -1;
149         goto out;
150 }
151
152 static GVariant *build_legacy_message_data(const struct NotifyParams *notify_params)
153 {
154         assert(notify_params);
155
156         return g_variant_new("(ssss)", notify_params->cmd_name, notify_params->cmd_path, notify_params->appid, notify_params->pkgid);
157 }
158
159 static GVariant *build_message_data(const struct NotifyParams *notify_params)
160 {
161         assert(notify_params);
162
163         GVariantBuilder md_builder;
164         g_variant_builder_init(&md_builder, G_VARIANT_TYPE("(sssssiia{sv})"));
165
166         g_variant_builder_add(&md_builder, "s", notify_params->cmd_name);
167         g_variant_builder_add(&md_builder, "s", notify_params->cmd_path);
168         g_variant_builder_add(&md_builder, "s", notify_params->appid);
169         g_variant_builder_add(&md_builder, "s", notify_params->pkgid);
170         g_variant_builder_add(&md_builder, "s", notify_params->report_path);
171         g_variant_builder_add(&md_builder, "i", notify_params->pid);
172         g_variant_builder_add(&md_builder, "i", notify_params->tid);
173
174         g_variant_builder_open(&md_builder, G_VARIANT_TYPE("a{sv}"));
175
176         if (notify_params->signal) {
177                 g_variant_builder_open(&md_builder, G_VARIANT_TYPE("{sv}"));
178                 g_variant_builder_add(&md_builder, "s", "sys.signal");
179                 g_variant_builder_add(&md_builder, "v", g_variant_new_int32(notify_params->signal));
180                 g_variant_builder_close(&md_builder);
181         }
182
183         if (notify_params->tid_comm) {
184                 g_variant_builder_open(&md_builder, G_VARIANT_TYPE("{sv}"));
185                 g_variant_builder_add(&md_builder, "s", "sys.tid.comm");
186                 g_variant_builder_add(&md_builder, "v", g_variant_new_string(notify_params->tid_comm));
187                 g_variant_builder_close(&md_builder);
188         }
189
190         struct RegInfo *reg_info;
191         int regs_count = _get_important_registers(notify_params->prstatus_fd, &reg_info);
192
193         /* registers */
194         for (int i = 0; i < regs_count; i++) {
195                 g_variant_builder_open(&md_builder, G_VARIANT_TYPE("{sv}"));
196
197                 g_variant_builder_add(&md_builder, "s", reg_info[i].name);
198 #if (defined __x86_64__) || (defined __aarch64__)
199                 g_variant_builder_add(&md_builder, "v", g_variant_new_uint64(reg_info[i].value));
200 #else
201                 g_variant_builder_add(&md_builder, "v", g_variant_new_uint32(reg_info[i].value));
202 #endif
203
204                 free(reg_info[i].name);
205                 g_variant_builder_close(&md_builder);
206         }
207         free(reg_info);
208
209         g_variant_builder_close(&md_builder);
210
211         return g_variant_builder_end(&md_builder);
212 }
213
214 static bool send_notify(GDBusConnection *conn, const struct NotifyParams *notify_params)
215 {
216         int result = false;
217         GError *error = NULL;
218
219         GVariant *data = notify_params->legacy_notification
220                          ? build_legacy_message_data(notify_params)
221                          : build_message_data(notify_params);
222         if (data == NULL) {
223                 _E("Error while preparing parameters");
224                 goto out;
225         }
226
227         g_dbus_connection_emit_signal(conn,
228                                  NULL,
229                                  CRASH_PATH_CRASH,
230                                  CRASH_INTERFACE_CRASH,
231                                  PROCESS_CRASHED,
232                                  data,
233                                  &error);
234         if (error) {
235                 _E("Failed to emit signal: %s", error->message);
236                 goto out;
237         }
238
239         g_dbus_connection_flush_sync(conn, NULL, &error);
240         if (error)
241                 _E("Failed to flush connection - signal might not be delivered: %s", error->message);
242
243         result = true;
244
245 out:
246         if (error)
247                 g_error_free(error);
248
249         return result;
250 }
251
252 static bool parse_cmdline(int ac, char *av[], struct NotifyParams *params)
253 {
254         assert(av);
255         assert(params);
256
257         enum {
258                 FLAG_CMDLINE = 1,
259                 FLAG_CMDPATH,
260                 FLAG_PID,
261                 FLAG_TID,
262                 FLAG_APPID,
263                 FLAG_PKGID,
264                 FLAG_REPORTPATH,
265                 FLAG_PRSTATUS_FD,
266                 FLAG_SIGNAL,
267                 FLAG_TID_COMM,
268                 FLAG_LEGACY_NOTIFICATION,
269         };
270         static const struct option options[] = {
271                 { .name = "cmdline",     .has_arg = required_argument, .flag = NULL, .val = FLAG_CMDLINE },
272                 { .name = "cmdpath",     .has_arg = required_argument, .flag = NULL, .val = FLAG_CMDPATH },
273                 { .name = "pid",         .has_arg = required_argument, .flag = NULL, .val = FLAG_PID },
274                 { .name = "tid",         .has_arg = required_argument, .flag = NULL, .val = FLAG_TID },
275                 { .name = "appid",       .has_arg = required_argument, .flag = NULL, .val = FLAG_APPID },
276                 { .name = "pkgid",       .has_arg = required_argument, .flag = NULL, .val = FLAG_PKGID },
277                 { .name = "reportpath",  .has_arg = required_argument, .flag = NULL, .val = FLAG_REPORTPATH },
278                 { .name = "prstatus_fd", .has_arg = required_argument, .flag = NULL, .val = FLAG_PRSTATUS_FD },
279                 { .name = "signal",      .has_arg = required_argument, .flag = NULL, .val = FLAG_SIGNAL },
280                 { .name = "tid-comm",    .has_arg = required_argument, .flag = NULL, .val = FLAG_TID_COMM },
281                 { .name = "legacy-sig",  .has_arg = no_argument,       .flag = NULL, .val = FLAG_LEGACY_NOTIFICATION },
282                 { NULL },
283         };
284
285         int val;
286         do {
287                 val = getopt_long_only(ac, av, "", options, NULL);
288
289                 if (FLAG_CMDLINE == val)
290                         params->cmd_name = basename(optarg);
291                 else if (FLAG_CMDPATH == val)
292                         params->cmd_path = optarg;
293                 else if (FLAG_REPORTPATH == val)
294                         params->report_path = optarg;
295                 else if (FLAG_PID == val)
296                         params->pid = atoi(optarg);
297                 else if (FLAG_TID == val)
298                         params->tid = atoi(optarg);
299                 else if (FLAG_APPID == val)
300                         params->appid = optarg;
301                 else if (FLAG_PKGID == val)
302                         params->pkgid = optarg;
303                 else if (FLAG_PRSTATUS_FD == val)
304                         params->prstatus_fd = atoi(optarg);
305                 else if (FLAG_SIGNAL == val)
306                         params->signal = atoi(optarg);
307                 else if (FLAG_TID_COMM == val)
308                         params->tid_comm = optarg;
309                 else if (FLAG_LEGACY_NOTIFICATION == val)
310                         params->legacy_notification = true;
311         } while (val != -1);
312
313         return params->cmd_name && params->cmd_path && params->appid && params->pkgid && params->prstatus_fd > 0;
314 }
315
316 static void usage(const char *const progname)
317 {
318         assert(progname);
319
320         printf("%s --prstatus_fd N --pid PID --tid TID --cmdline CMDLINE --cmdpath CMDPATH --appid APPID --pkgid PKGID --signal SIGNALNR [--legacy-sig]\n", progname);
321 }
322
323 int main(int ac, char *av[])
324 {
325         struct NotifyParams params = {0, };
326
327         if (!parse_cmdline(ac, av, &params)) {
328                 usage(av[0]);
329                 return EXIT_FAILURE;
330         }
331
332         GDBusConnection *conn = NULL;
333         if (!bus_get(&conn))
334                 return EXIT_FAILURE;
335
336         send_notify(conn, &params);
337         bus_put(conn);
338
339         return EXIT_SUCCESS;
340 }