Check errors aggressively
[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 = NULL;
55
56     if (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)) == -1) {
58         _E("Couldn't print dbus variant format: %m");
59         goto skip_dbus_signal;
60     }
61
62     if (snprintf(objpath, PATH_MAX, TSM_DBUS_PATH "/%s", ad->ds->name) == -1) {
63         _E("Couldn't print dbus object path: %m");
64         goto skip_dbus_signal;
65     }
66     if (snprintf(interface, PATH_MAX, TSM_DBUS_INTERFACE ".%s", ad->ds->param_name) == -1) {
67         _E("Couldn't print dbus interface: %m");
68         goto skip_dbus_signal;
69     }
70
71     builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
72     g_variant_builder_add(builder, "{sv}",
73                           "report_path",
74                           g_variant_new_string(ad->report_path ? ad->report_path : ""));
75
76     g_variant_builder_add(builder, "{sv}",
77                           "killed",
78                           g_variant_new_boolean(ad->ds->process->kill));
79
80     g_variant_builder_add(builder, "{sv}",
81                           "process_name",
82                           g_variant_new_string(ad->ds->process->name));
83
84     g_variant_builder_add(builder, "{sv}",
85                           "process_state",
86                           g_variant_new_string(process_is_foreground(ad->ds->process) ?
87                                                "foreground" : "background"));
88
89     signal_params = g_variant_new(format,
90                                   ad->ds->process->pid,
91                                   limit_type_to_string(ad->lt),
92                                   ad->actual_value,
93                                   ad->allowed_value,
94                                   g_variant_builder_end(builder));
95
96     g_variant_builder_unref(builder);
97
98 skip_dbus_signal:
99
100     /* Kill the process */
101     if (ad->ds->process->kill) {
102         _D_PROC(ad->ds->process, "Killing process");
103         if (kill(ad->ds->process->pid, SIGKILL))
104             _E("Unable to kill process %s(%d): %m",
105                ad->ds->process->name,
106                ad->ds->process->pid);
107     }
108
109     /* Send signal */
110     if (signal_params) {
111         dbus_send_signal(objpath, interface, "AbnormalityDetected", signal_params);
112
113         _D_PROC(ad->ds->process, "Sent D-Bus signal (%s, %s)",
114                                  limit_type_to_string(ad->lt),
115                                  ad->ds->param_name);
116     } else {
117         _E("Skipping D-Bus signal due to errors");
118     }
119
120     ad->ds->action_in_progress = 0;
121
122     process_unref(ad->ds->process);
123     close(ad->stdout_fd);
124     close(ad->stderr_fd);
125     free(ad->report_path);
126     free(ad);
127 }
128
129 static void crash_manager_exit_cb(GPid pid, gint status, gpointer user_data)
130 {
131     struct action_data *ad = user_data;
132     FILE *stream = NULL;
133     char *line = NULL;
134     size_t len = 0;
135     ssize_t n;
136
137     if (!g_spawn_check_exit_status (status, NULL)) {
138         _E("Crash-manager failed with code: %d", status);
139
140         stream = fdopen(ad->stderr_fd, "r");
141         if (!stream) {
142             _E("Unable to open stderr stream: %m");
143             goto finish;
144         }
145
146         while ((n = getline(&line, &len, stream)) != -1)
147             _D("%s", line);
148
149         goto finish;
150     }
151
152     stream = fdopen(ad->stdout_fd, "r");
153     if (!stream) {
154         _E("Unable to open stdout stream: %m");
155         goto finish;
156     }
157
158     /* Get report path from stdout */
159     while ((n = getline(&line, &len, stream)) != -1) {
160         if (strncmp(line, "REPORT_PATH=", 12) == 0) {
161             line[n-1] = 0;
162
163             ad->report_path = strndup(line + 12, n - 12);
164             if (!ad->report_path)
165                 _E("Unable to allocate memory");
166             else
167                 _D_PROC(ad->ds->process, "Received report: %s", ad->report_path);
168
169             goto finish;
170         }
171     }
172
173     _E("Crash-worker ended without error but didn't provide report path");
174
175 finish:
176     action_finish(ad); // NB: also closes `stream` through `ad->stdout_fd`
177
178     free(line);
179     g_spawn_close_pid(pid);
180 }
181
182 static GPid spawn_crash_manager(struct action_data *ad)
183 {
184     GPid child_pid = 0;
185     GError *error = NULL;
186     char *pid_str = NULL;
187     if (asprintf(&pid_str, "%d", ad->ds->process->pid) == -1)
188        goto out;
189
190     char *argv[] = {CRASH_MANAGER_BIN, "-lrp", pid_str, NULL};
191
192     _D_PROC(ad->ds->process, "Generating report...");
193
194     /* Spawn child */
195     if (!g_spawn_async_with_pipes(NULL, argv, NULL,
196                                   G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL,
197                                   NULL, &child_pid, NULL, &ad->stdout_fd, &ad->stderr_fd, &error)) {
198
199         _E("Unable to spawn child process: %s", error->message);
200         g_error_free(error);
201     }
202     _D("Spawned child process pid %d", child_pid);
203
204 out:
205     free(pid_str);
206     return child_pid;
207 }
208
209 static gboolean action_run(gpointer data)
210 {
211     struct action_data *ad = data;
212     GPid child_pid;
213
214     if (ad->ds->process->report) {
215         child_pid = spawn_crash_manager(ad);
216         if (child_pid != 0) {
217             g_child_watch_add(child_pid, crash_manager_exit_cb, ad);
218             return FALSE;
219         }
220     }
221
222     action_finish(ad);
223
224     return FALSE;
225 }
226
227 int action_run_default(struct data_source *ds, enum limit_type lt)
228 {
229     struct action_data *ad;
230     struct smpl_container *sc = process_is_foreground(ds->process) ? &ds->data_fg : &ds->data_bg;
231
232     if (ds->action_in_progress)
233         return 0;
234     ds->action_in_progress = 1;
235
236     _D_PROC(ds->process, "Running default action");
237
238     ad = calloc(1, sizeof(*ad));
239     if (!ad) {
240         _E("Unable to allocate memory");
241         return -ENOMEM;
242     }
243
244     ad->ds = ds;
245     ad->report_path = NULL;
246     ad->lt = lt;
247
248     if (lt == LIMIT_TYPE_AVG) {
249         ad->actual_value = g_variant_new_double(sc->average);
250         ad->allowed_value = g_variant_new_double(ds->limit.avg);
251     } else {
252         ad->actual_value = sample_to_gvariant(list_first_entry(&sc->samples, struct sample_s, node));
253         ad->allowed_value = sample_to_gvariant(&ds->limit.peak);
254     }
255
256     process_ref(ds->process);
257     g_timeout_add(0, action_run, ad);
258
259     return 0;
260 }