Change signatures of cleanup functions
[platform/core/api/diagnostics.git] / src / library / diagnostics.c
1 /*
2  * Copyright (c) 2020 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
17 #include <diagnostics.h>
18 #include <stdlib.h>
19 #include <system_info.h>
20 #include <gio/gio.h>
21 #include <glib.h>
22 #include <libdumpsys.h>
23 #include <poll.h>
24
25 #include "log.h"
26 #include "signal.h"
27 #include "dbus.h"
28
29 #ifndef STATIC
30 #define STATIC static
31 #endif
32
33 #define DIAGNOSTICS_FEATURE "http://tizen.org/feature/diagnostics"
34
35 #define FEATURE_UNKNOWN -1
36 #define FEATURE_FALSE    0
37 #define FEATURE_TRUE     1
38
39 STATIC struct _diagnostics_cb_info_s {
40     diagnostics_notification_cb cb;
41     void *user_data;
42 } cb_info = {
43     NULL,
44     NULL
45 };
46
47 struct _diagnostics_ctx_s {
48     char *client_id;
49     struct dbus_signal_s *signal;
50     signal_type_e signal_type;
51 };
52
53 struct _diagnostics_data_s {
54     int fd;
55 };
56
57 STATIC int diagnostics_feature = FEATURE_UNKNOWN;
58
59 STATIC bool __is_feature_supported(void)
60 {
61     int ret = SYSTEM_INFO_ERROR_NONE;
62     bool feature = false;
63
64     if (diagnostics_feature == FEATURE_UNKNOWN) {
65         ret = system_info_get_platform_bool(DIAGNOSTICS_FEATURE, &feature);
66         RETVM_IF(ret != SYSTEM_INFO_ERROR_NONE, false, "Failed to get system info");
67
68         diagnostics_feature = (feature ? FEATURE_TRUE : FEATURE_FALSE);
69     }
70
71     return (diagnostics_feature == FEATURE_TRUE ? true : false);
72 }
73
74 STATIC struct _diagnostics_ctx_s *diagnostics_create(struct dbus_signal_s *signal)
75 {
76     RETV_IF(signal == NULL, NULL);
77
78     struct _diagnostics_ctx_s *ctx;
79
80     ctx = calloc(1, sizeof(struct _diagnostics_ctx_s));
81     if (!ctx) {
82         _E("Unable to allocate memory");
83         return NULL;
84     }
85
86     if (signal->signal_name && strcmp(signal->signal_name, DBUS_MEMBER_CRASH) == 0) {
87         ctx->client_id = DBUS_SENDER_CRASH;
88         ctx->signal_type = signal_is_valid_crash(signal) ? SIG_TYPE_CRASH : SIG_TYPE_INVALID;
89     } else {
90         _E("Unknown signal name");
91         free(ctx);
92         return NULL;
93     }
94
95     ctx->signal = signal;
96     return ctx;
97 }
98
99 STATIC struct _diagnostics_data_s *diagnostics_data_create(int fd)
100 {
101     RETV_IF(fd < 0, NULL);
102
103     struct _diagnostics_data_s *data;
104
105     data = calloc(1, sizeof(struct _diagnostics_data_s));
106     if (!data) {
107         _E("Unable to allocate memory");
108         return NULL;
109     }
110
111     data->fd = fd;
112     return data;
113 }
114
115 STATIC void signal_handler(GDBusConnection *connection,
116                            const gchar *sender_name,
117                            const gchar *object_path,
118                            const gchar *interface_name,
119                            const gchar *signal_name,
120                            GVariant *parameters,
121                            gpointer user_data)
122 {
123     struct _diagnostics_ctx_s *ctx;
124     struct dbus_signal_s *signal;
125
126     _D("signal_handler");
127     _D("parameters: %s", g_variant_print(parameters, TRUE));
128
129     if (!cb_info.cb) {
130         _E("No user cb set");
131         return;
132     }
133
134     _D("dbus_signal_create");
135     signal = dbus_signal_create(sender_name,
136                                 object_path,
137                                 interface_name,
138                                 signal_name,
139                                 parameters);
140     if (!signal) {
141         _E("Unable to create signal structure");
142         return;
143     }
144
145     _D("diagnostics_create");
146     ctx = diagnostics_create(signal);
147     if (!ctx) {
148         _E("Unable to create diagnostics context");
149         dbus_signal_cleanup(signal);
150         return;
151     }
152
153     _D("Fireing user cb!");
154     cb_info.cb(ctx, cb_info.user_data);
155 }
156
157 int diagnostics_set_notification_cb(diagnostics_notification_cb callback, void *user_data)
158 {
159     RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported");
160     RETV_IF(callback == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
161
162     int ret;
163
164     _D("diagnostics_set_notification_cb()");
165
166     if (cb_info.cb) {
167         _E("Callback has already been set");
168         return DIAGNOSTICS_ERROR_RESOURCE_BUSY;
169     }
170
171     ret = dbus_subscribe(signal_handler);
172     if (ret) {
173         _E("Unable to subscribe to dbus signals");
174         return DIAGNOSTICS_ERROR_IO_ERROR;
175     }
176
177     cb_info.cb = callback;
178     cb_info.user_data = user_data;
179
180     return DIAGNOSTICS_ERROR_NONE;
181 }
182
183 int diagnostics_unset_notification_cb(void)
184 {
185     RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported");
186
187     _D("diagnostics_unset_notification_cb()");
188
189     cb_info.cb = NULL;
190     cb_info.user_data = NULL;
191
192     dbus_unsubscribe();
193     return DIAGNOSTICS_ERROR_NONE;
194 }
195
196 int diagnostics_request_client_data(const char *client_id, const char **params, int params_size, diagnostics_data_h *data)
197 {
198     RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported");
199     RETV_IF(client_id == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
200     RETV_IF(params_size < 0, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
201     RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
202
203     int fd = -1;
204     int ret;
205
206     ret = dumpsys_dump(client_id, params_size, params, &fd);
207     if (ret != DIAGNOSTICS_ERROR_NONE) {
208         _E("dumpsys_dump() failed: %d", ret);
209         if (ret == TIZEN_ERROR_PERMISSION_DENIED)
210             return DIAGNOSTICS_ERROR_PERMISSION_DENIED;
211         return DIAGNOSTICS_ERROR_IO_ERROR;
212     }
213
214     *data = diagnostics_data_create(fd);
215     if (*data == NULL) {
216         _E("Unable to create diagnostics_data");
217         return DIAGNOSTICS_ERROR_OUT_OF_MEMORY;
218     }
219
220     return DIAGNOSTICS_ERROR_NONE;
221 }
222
223 int diagnostics_data_read(diagnostics_data_h data, void *buf, size_t count, int timeout_ms, size_t *bytes_read)
224 {
225     RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported");
226     RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
227     RETV_IF(buf == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
228     RETV_IF(bytes_read == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
229
230     struct pollfd poll_fd;
231     int ready;
232     int ret;
233
234     poll_fd.fd = ((struct _diagnostics_data_s *)data)->fd;
235     poll_fd.events = POLLIN;
236
237     ready = poll(&poll_fd, 1, timeout_ms <= 0 ? -1 : timeout_ms);
238
239     if (ready == 0) {
240         _E("poll() timeout");
241         return DIAGNOSTICS_ERROR_TIMED_OUT;
242     }
243
244     if (ready < 0) {
245         _E("poll() failed: %m");
246         return DIAGNOSTICS_ERROR_IO_ERROR;
247     }
248
249     if (poll_fd.revents & POLLIN) {
250         ret = read(poll_fd.fd, buf, count);
251         if (ret < 0) {
252             _E("read() failed: %m, fd: %d", poll_fd.fd);
253             if (errno == EAGAIN)
254                 return DIAGNOSTICS_ERROR_TRY_AGAIN;
255             return DIAGNOSTICS_ERROR_IO_ERROR;
256         }
257         *bytes_read = ret;
258         return DIAGNOSTICS_ERROR_NONE;
259     } else if (poll_fd.revents & POLLHUP) {
260         *bytes_read = 0;
261         return DIAGNOSTICS_ERROR_NONE;
262     }
263
264     _E("received event: %d", poll_fd.revents);
265     return DIAGNOSTICS_ERROR_IO_ERROR;
266 }
267
268 int diagnostics_get_client_id(diagnostics_ctx_h ctx, char **client_id)
269 {
270     RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported");
271     RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
272     RETV_IF(client_id == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
273
274     *client_id = strdup(((struct _diagnostics_ctx_s *)ctx)->client_id);
275
276     return DIAGNOSTICS_ERROR_NONE;
277 }
278
279 STATIC int get_report_path(diagnostics_ctx_h ctx, const char **path, size_t *len)
280 {
281     RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
282     RETV_IF(path == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
283     RETV_IF(((struct _diagnostics_ctx_s *)ctx)->signal_type != SIG_TYPE_CRASH, DIAGNOSTICS_ERROR_NOT_SUPPORTED);
284
285     GVariant *val;
286
287     val = g_variant_get_child_value(((struct _diagnostics_ctx_s *)ctx)->signal->parameters, SIG_CRASH_REPORTPATH);
288     *path = g_variant_get_string(val, (gsize *)len);
289
290     return DIAGNOSTICS_ERROR_NONE;
291 }
292
293 int diagnostics_get_data(diagnostics_ctx_h ctx, const char **params, int params_size, diagnostics_data_h *data)
294 {
295     RETVM_IF(__is_feature_supported() == false, DIAGNOSTICS_ERROR_NOT_SUPPORTED, "Diagnostics feature is not supported");
296     RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
297     RETV_IF(params_size < 0, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
298     RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
299     RETV_IF(((struct _diagnostics_ctx_s *)ctx)->signal_type != SIG_TYPE_CRASH, DIAGNOSTICS_ERROR_NOT_SUPPORTED);
300
301     const char *report_path;
302     size_t len;
303     int report_id;
304     int fd;
305     int ret;
306
307     /*
308      * TODO: Make this suitable for supporting other clients, not just crash-worker
309      */
310
311     ret = get_report_path(((struct _diagnostics_ctx_s *)ctx), &report_path, &len);
312     if (ret) {
313         _E("diagnostics_get_report_path() failed: %d", ret);
314         return DIAGNOSTICS_ERROR_IO_ERROR;
315     }
316
317     if (params_size < 1)
318         return DIAGNOSTICS_ERROR_NOT_SUPPORTED;
319
320     if (strcmp(params[0], "cs_full") == 0)
321         report_id = 0;
322     else if (strcmp(params[0], "cs_info_json") == 0)
323         report_id = 1;
324     else {
325         _E("Unsupported parameter: %s", params[0]);
326         return DIAGNOSTICS_ERROR_NOT_SUPPORTED;
327     }
328
329     ret = dbus_get_file_from_report(report_path, report_id, &fd);
330     if (ret) {
331         _E("dbus_get_file_from_report() failed: %d", ret);
332         if (ret == -EACCES)
333             return DIAGNOSTICS_ERROR_PERMISSION_DENIED;
334         return DIAGNOSTICS_ERROR_IO_ERROR;
335     }
336
337     *data = diagnostics_data_create(fd);
338     if (*data == NULL) {
339         _E("Unable to create diagnostics_data");
340         close(fd);
341         return DIAGNOSTICS_ERROR_OUT_OF_MEMORY;
342     }
343
344     return DIAGNOSTICS_ERROR_NONE;
345 }
346
347 int diagnostics_destroy(diagnostics_ctx_h ctx) {
348     RETV_IF(ctx == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
349
350     dbus_signal_cleanup(((struct _diagnostics_ctx_s *)ctx)->signal);
351     free(ctx);
352
353     return DIAGNOSTICS_ERROR_NONE;
354 }
355
356 int diagnostics_data_destroy(diagnostics_data_h data) {
357     RETV_IF(data == NULL, DIAGNOSTICS_ERROR_INVALID_PARAMETER);
358
359     close(((struct _diagnostics_data_s *)data)->fd);
360     free(data);
361
362     return DIAGNOSTICS_ERROR_NONE;
363 }