e3f0f80b22e46e1859d04d2637c356b6ba9c5731
[platform/core/system/stability-monitor.git] / src / action.c
1 /*
2  * This file is part of stability-monitor
3  *
4  * Copyright © 2019 Samsung Electronics
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 <sys/types.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <limits.h>
23 #include <glib.h>
24 #include <glib-unix.h>
25 #include <gio/gio.h>
26 #include <fcntl.h>
27 #include <string.h>
28
29 #include "utils.h"
30 #include "log.h"
31 #include "action.h"
32 #include "dbus.h"
33 #include "limit.h"
34
35
36 struct action_data {
37     int stdout_fd;
38     int stderr_fd;
39
40     struct data_source *ds;
41     enum limit_type lt;
42     GVariant *actual_value;
43     GVariant *allowed_value;
44
45     char *report_path;
46 };
47
48 static void action_finish(struct action_data *ad)
49 {
50     char objpath[PATH_MAX];
51     char interface[PATH_MAX];
52     char format[20];
53     GVariantBuilder *builder;
54     GVariant *signal_params;
55
56     snprintf(format, 20, "(is@%s@%s@a{sv})", g_variant_get_type_string(ad->actual_value),
57                                              g_variant_get_type_string(ad->allowed_value));
58
59     snprintf(objpath, PATH_MAX, "%s/%s", TSM_DBUS_PATH, ad->ds->name);
60     snprintf(interface, PATH_MAX, "%s.%s", TSM_DBUS_INTERFACE, ad->ds->param_name);
61
62
63     builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
64     g_variant_builder_add(builder, "{sv}",
65                           "report_path",
66                           g_variant_new_string(ad->report_path ? ad->report_path : ""));
67
68     g_variant_builder_add(builder, "{sv}",
69                           "killed",
70                           g_variant_new_boolean(ad->ds->process->kill));
71
72     g_variant_builder_add(builder, "{sv}",
73                           "process_name",
74                           g_variant_new_string(ad->ds->process->name));
75
76     g_variant_builder_add(builder, "{sv}",
77                           "process_state",
78                           g_variant_new_string(process_is_foreground(ad->ds->process) ?
79                                                "foreground" : "background"));
80
81     signal_params = g_variant_new(format,
82                                   ad->ds->process->pid,
83                                   limit_type_to_string(ad->lt),
84                                   ad->actual_value,
85                                   ad->allowed_value,
86                                   g_variant_builder_end(builder));
87
88     g_variant_builder_unref(builder);
89
90     /* Kill the process */
91     if (ad->ds->process->kill) {
92         _D_PROC(ad->ds->process, "Killing process");
93         if (kill(ad->ds->process->pid, SIGKILL))
94             _E("Unable to kill process %s(%d): %m",
95                ad->ds->process->name,
96                ad->ds->process->pid);
97     }
98
99     /* Send signal */
100     dbus_send_signal(objpath, interface, "AbnormalityDetected", signal_params);
101
102     _D_PROC(ad->ds->process, "Sent D-Bus signal (%s, %s)",
103                              limit_type_to_string(ad->lt),
104                              ad->ds->param_name);
105
106     ad->ds->action_in_progress = 0;
107
108     process_unref(ad->ds->process);
109     close(ad->stdout_fd);
110     close(ad->stderr_fd);
111     free(ad->report_path);
112     free(ad);
113 }
114
115 static void crash_manager_exit_cb(GPid pid, gint status, gpointer user_data)
116 {
117     struct action_data *ad = user_data;
118     FILE *stream = NULL;
119     char *line = NULL;
120     size_t len = 0;
121     ssize_t n;
122
123     if (!g_spawn_check_exit_status (status, NULL)) {
124         _E("Crash-manager failed with code: %d", status);
125
126         stream = fdopen(ad->stderr_fd, "r");
127         if (!stream) {
128             _E("Unable to open stderr stream: %m");
129             goto finish;
130         }
131
132         while ((n = getline(&line, &len, stream)) != -1)
133             _D("%s", line);
134
135         goto finish;
136     }
137
138     stream = fdopen(ad->stdout_fd, "r");
139     if (!stream) {
140         _E("Unable to open stdout stream: %m");
141         goto finish;
142     }
143
144     /* Get report path from stdout */
145     while ((n = getline(&line, &len, stream)) != -1) {
146         if (strncmp(line, "REPORT_PATH=", 12) == 0) {
147             line[n-1] = 0;
148
149             ad->report_path = strndup(line + 12, n - 12);
150             if (!ad->report_path)
151                 _E("Unable to allocate memory");
152             else
153                 _D_PROC(ad->ds->process, "Received report: %s", ad->report_path);
154
155             goto finish;
156         }
157     }
158
159     _E("Crash-worker ended without error but didn't provide report path");
160
161 finish:
162     action_finish(ad);
163
164     free(line);
165     if (stream)
166         fclose(stream);
167
168     g_spawn_close_pid(pid);
169 }
170
171 static GPid spawn_crash_manager(struct action_data *ad)
172 {
173     GPid child_pid = 0;
174     GError *error = NULL;
175     char *pid_str = NULL;
176     if (asprintf(&pid_str, "%d", ad->ds->process->pid) == -1)
177        goto out;
178
179     char *argv[] = {CRASH_MANAGER_BIN, "-lrp", pid_str, NULL};
180
181     _D_PROC(ad->ds->process, "Generating report...");
182
183     /* Spawn child */
184     if (!g_spawn_async_with_pipes(NULL, argv, NULL,
185                                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL,
186                                   NULL, &child_pid, NULL, &ad->stdout_fd, &ad->stderr_fd, &error)) {
187
188         _E("Unable to spawn child process: %s", error->message);
189         g_error_free(error);
190     }
191     _D("Spawned child process pid %d", child_pid);
192
193 out:
194     free(pid_str);
195     return child_pid;
196 }
197
198 static gboolean action_run(gpointer data)
199 {
200     struct action_data *ad = data;
201     GPid child_pid;
202
203     if (ad->ds->process->report) {
204         child_pid = spawn_crash_manager(ad);
205         if (child_pid != 0) {
206             g_child_watch_add(child_pid, crash_manager_exit_cb, ad);
207             return FALSE;
208         }
209     }
210
211     action_finish(ad);
212
213     return FALSE;
214 }
215
216 int action_run_default(struct data_source *ds, enum limit_type lt)
217 {
218     struct action_data *ad;
219     struct smpl_container *sc = process_is_foreground(ds->process) ? &ds->data_fg : &ds->data_bg;
220
221     if (ds->action_in_progress)
222         return 0;
223     ds->action_in_progress = 1;
224
225     _D_PROC(ds->process, "Running default action");
226
227     ad = calloc(1, sizeof(*ad));
228     if (!ad) {
229         _E("Unable to allocate memory");
230         return -ENOMEM;
231     }
232
233     ad->ds = ds;
234     ad->report_path = NULL;
235     ad->lt = lt;
236
237     if (lt == LIMIT_TYPE_AVG) {
238         ad->actual_value = g_variant_new_double(sc->average);
239         ad->allowed_value = g_variant_new_double(ds->limit.avg);
240     } else {
241         ad->actual_value = sample_to_gvariant(list_first_entry(&sc->samples, struct sample_s, node));
242         ad->allowed_value = sample_to_gvariant(&ds->limit.peak);
243     }
244
245     process_ref(ds->process);
246     g_timeout_add(0, action_run, ad);
247
248     return 0;
249 }