Split dbus-notify-send from crash-manager
[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
24 #include <assert.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <gio/gio.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/mman.h>
33 #include <sys/procfs.h>
34 #include <unistd.h>
35
36 #define CRASH_BUS_NAME          "org.tizen.system.crash"
37 #define CRASH_OBJECT_PATH       "/Org/Tizen/System/Crash"
38 #define CRASH_INTERFACE_NAME    CRASH_BUS_NAME
39 #define CRASH_PATH_CRASH        CRASH_OBJECT_PATH"/Crash"
40 #define CRASH_INTERFACE_CRASH   CRASH_INTERFACE_NAME".Crash"
41 #define PROCESS_CRASHED         "ProcessCrashed"
42
43 #define KERNEL_DEFINED_TASK_COMM_LEN 16 // from include/linux/sched.h
44
45 #define ARM_REG_LR 14
46 #define ARM_REG_PC 15
47 #define AARCH64_REG_LR 30
48
49 struct RegInfo {
50         char *name;
51         long long int value;
52 };
53
54 struct NotifyParams {
55         int prstatus_fd;
56         pid_t pid;
57         pid_t tid;
58         char *cmd_name;
59         char *cmd_path;
60         char *report_path;
61         char *appid;
62         char *pkgid;
63 };
64
65 static int _get_important_registers(int fd, struct RegInfo **reg_info)
66 {
67         int count = -1;
68
69         if (reg_info == NULL) {
70                 _E("reg_info is NULL\n");
71                 return -1;
72         }
73
74         *reg_info = NULL;
75         struct elf_prstatus *prstatus = mmap(NULL, sizeof(struct elf_prstatus),
76                                          PROT_READ, MAP_SHARED, fd, 0);
77         if (prstatus == MAP_FAILED) {
78                 _E("mmap error: %m\n");
79                 return -1;
80         }
81
82 #if (defined __i386__) || (defined __x86_64__)
83         count = 1;
84 #elif (defined __arm__) || (defined __aarch64__)
85         count = 2;
86 #endif
87
88         *reg_info = (struct RegInfo*)malloc(sizeof(struct RegInfo)*count);
89         struct RegInfo *reginfo = *reg_info;
90
91         if (*reg_info == NULL) {
92                 _E("malloc for reg_info error: %m");
93                 goto out;
94         }
95
96 #if (defined __i386__) || (defined __x86_64__)
97         struct user_regs_struct *g_registers = (struct user_regs_struct*)prstatus->pr_reg;
98
99 #if defined(__i386__)
100         reginfo[0].name = strdup("x86.eip");
101         reginfo[0].value = (long long int)g_registers->eip;
102 #else
103         reginfo[0].name = strdup("x86_64.rip");
104         reginfo[0].value = (long long int)g_registers->rip;
105 #endif // defined(__i386__)
106
107         if (reginfo[0].name == NULL)
108                 goto strdup_error;
109
110 #elif defined(__arm__)
111
112         struct user_regs *g_registers;
113         g_registers = (struct user_regs*)prstatus->pr_reg;
114
115         if ((reginfo[0].name = strdup("arm.pc")) == NULL)
116                 goto strdup_error;
117         reginfo[0].value = (long long int)g_registers->uregs[ARM_REG_PC];
118         if ((reginfo[1].name = strdup("arm.lr")) == NULL)
119                 goto strdup_error;
120         reginfo[1].value = (long long int)g_registers->uregs[ARM_REG_LR];
121
122 #elif defined(__aarch64__)
123
124         struct user_regs_struct *g_registers = (struct user_regs_struct*)prstatus->pr_reg;
125
126         if ((reginfo[0].name = strdup("aarch64.pc")) == NULL)
127                 goto strdup_error;
128         reginfo[0].value = (long long int)g_registers->pc;
129         if ((reginfo[1].name = strdup("aarch64.lr")) == NULL)
130                 goto strdup_error;
131         reginfo[1].value = (long long int)g_registers->regs[AARCH64_REG_LR];
132
133 #endif // (defined __i386__) || (defined __x86_64__)
134
135 out:
136         if (count <= 0) {
137                 free(*reg_info);
138                 *reg_info = NULL;
139         }
140
141         munmap(prstatus, sizeof(*prstatus));
142         return count;
143
144 strdup_error:
145         _E("strdup error: %m\n");
146         count = -1;
147         goto out;
148 }
149
150 static GVariant* build_message_data(const struct NotifyParams *notify_params)
151 {
152         if (notify_params == NULL)
153                 return NULL;
154
155         GVariantBuilder md_builder;
156         g_variant_builder_init(&md_builder, G_VARIANT_TYPE("(sssssiia{sv})"));
157
158         g_variant_builder_add(&md_builder, "s", notify_params->cmd_name);
159         g_variant_builder_add(&md_builder, "s", notify_params->cmd_path);
160         g_variant_builder_add(&md_builder, "s", notify_params->appid);
161         g_variant_builder_add(&md_builder, "s", notify_params->pkgid);
162         g_variant_builder_add(&md_builder, "s", notify_params->report_path);
163         g_variant_builder_add(&md_builder, "i", notify_params->pid);
164         g_variant_builder_add(&md_builder, "i", notify_params->tid);
165
166         g_variant_builder_open(&md_builder, G_VARIANT_TYPE("a{sv}"));
167
168         struct RegInfo *reg_info;
169         int regs_count = _get_important_registers(notify_params->prstatus_fd, &reg_info);
170
171         /* registers */
172         for (int i = 0; i < regs_count; i++) {
173                 g_variant_builder_open(&md_builder, G_VARIANT_TYPE("{sv}"));
174
175                 g_variant_builder_add(&md_builder, "s", reg_info[i].name);
176 #if (defined __x86_64__) || (defined __aarch64__)
177                 g_variant_builder_add(&md_builder, "v", g_variant_new_uint64(reg_info[i].value));
178 #else
179                 g_variant_builder_add(&md_builder, "v", g_variant_new_uint32(reg_info[i].value));
180 #endif
181
182                 free(reg_info[i].name);
183                 g_variant_builder_close(&md_builder);
184         }
185         free(reg_info);
186
187         g_variant_builder_close(&md_builder);
188
189         return g_variant_builder_end(&md_builder);
190 }
191
192 static bool send_notify(GDBusConnection *conn, const struct NotifyParams *notify_params)
193 {
194         int result = false;
195
196         GVariant *data = build_message_data(notify_params);
197         if (data == NULL) {
198                 _E("Error while preparing parameters");
199                 goto out;
200         }
201
202         GError *error = NULL;
203         g_dbus_connection_emit_signal(conn,
204                                  NULL,
205                                  CRASH_PATH_CRASH,
206                                  CRASH_INTERFACE_CRASH,
207                                  PROCESS_CRASHED,
208                                  data,
209                                  &error);
210         if (error) {
211                 _E("Failed to emit signal: %s", error->message);
212                 goto out;
213         }
214
215         g_dbus_connection_flush_sync(conn, NULL, &error);
216         if (error)
217                 _E("Failed to flush connection - signal might not be delivered: %s", error->message);
218
219         result = true;
220
221 out:
222         if (error)
223                 g_error_free(error);
224
225         return result;
226 }
227
228 static bool parse_cmdline(int ac, char *av[], struct NotifyParams *params)
229 {
230         assert(av);
231         assert(params);
232
233         enum {
234                 FLAG_CMDLINE = 1,
235                 FLAG_CMDPATH,
236                 FLAG_PID,
237                 FLAG_TID,
238                 FLAG_APPID,
239                 FLAG_PKGID,
240                 FLAG_REPORTPATH,
241                 FLAG_PRSTATUS_FD,
242         };
243         static const struct option options[] = {
244                 { .name = "cmdline",     .has_arg = required_argument, .flag = NULL, .val = FLAG_CMDLINE },
245                 { .name = "cmdpath",     .has_arg = required_argument, .flag = NULL, .val = FLAG_CMDPATH },
246                 { .name = "pid",         .has_arg = required_argument, .flag = NULL, .val = FLAG_PID },
247                 { .name = "tid",         .has_arg = required_argument, .flag = NULL, .val = FLAG_TID },
248                 { .name = "appid",       .has_arg = required_argument, .flag = NULL, .val = FLAG_APPID },
249                 { .name = "pkgid",       .has_arg = required_argument, .flag = NULL, .val = FLAG_PKGID },
250                 { .name = "reportpath",  .has_arg = required_argument, .flag = NULL, .val = FLAG_REPORTPATH },
251                 { .name = "prstatus_fd", .has_arg = required_argument, .flag = NULL, .val = FLAG_PRSTATUS_FD },
252                 { NULL },
253         };
254
255         int val;
256         do {
257                 val = getopt_long_only(ac, av, "", options, NULL);
258
259                 if (FLAG_CMDLINE == val)
260                         params->cmd_name = basename(optarg);
261                 else if (FLAG_CMDPATH == val)
262                         params->cmd_path = optarg;
263                 else if (FLAG_PID == val)
264                         params->pid = atoi(optarg);
265                 else if (FLAG_TID == val)
266                         params->tid = atoi(optarg);
267                 else if (FLAG_APPID == val)
268                         params->appid = optarg;
269                 else if (FLAG_PKGID == val)
270                         params->pkgid = optarg;
271                 else if (FLAG_PRSTATUS_FD == val)
272                         params->prstatus_fd = atoi(optarg);
273         } while (val != -1);
274
275         return params->cmd_name && params->cmd_path && params->appid && params->pkgid && params->prstatus_fd > 0;
276 }
277
278 static void usage(const char *const progname)
279 {
280         assert(progname);
281
282         printf("%s --prstatus_fd N --pid PID --tid TID --cmdline CMDLINE --cmdpath CMDPATH --appid APPID --pkgid PKGID\n", progname);
283 }
284
285 int main(int ac, char *av[])
286 {
287         struct NotifyParams params = {0, };
288
289         if (!parse_cmdline(ac, av, &params)) {
290                 usage(av[0]);
291                 return EXIT_FAILURE;
292         }
293
294         GDBusConnection *conn = NULL;
295         if (!bus_get(&conn))
296                 return EXIT_FAILURE;
297
298         send_notify(conn, &params);
299         bus_put(conn);
300
301         return EXIT_SUCCESS;
302 }