60360981660d0ffbcf807fb343e603ac18e037a6
[platform/core/connectivity/stc-manager.git] / src / monitor / stc-app-lifecycle.c
1 /*
2  * Copyright (c) 2017 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 <stdbool.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <sys/socket.h>
25 #include <linux/netlink.h>
26 #include <linux/connector.h>
27 #include <linux/cn_proc.h>
28 #include <glib.h>
29
30 #include "stc-app-lifecycle.h"
31 #include "helper-procfs.h"
32
33 typedef struct {
34         pid_t pid;
35 } proc_key_s;
36
37 typedef struct {
38         char cmdline[PROC_NAME_MAX];
39         char status[PROC_BUF_MAX];
40 } proc_value_s;
41
42 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
43         struct nlmsghdr nl_hdr;
44         struct __attribute__ ((__packed__)) {
45                 struct cn_msg cn_msg;
46                 enum proc_cn_mcast_op cn_mcast;
47         };
48 } nl_connector_msg_s;
49
50 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
51         struct nlmsghdr nl_hdr;
52         struct __attribute__ ((__packed__)) {
53                 struct cn_msg cn_msg;
54                 struct proc_event proc_ev;
55         };
56 } nl_connector_proc_event_s;
57
58 static int nl_connector_sock = -1;
59 static guint nl_connector_gsource_id = 0;
60 static GTree *proc_tree;
61
62
63 static gboolean __process_nl_connector_message(GIOChannel *source,
64                                                GIOCondition condition,
65                                                gpointer user_data);
66
67 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
68                                    gpointer UNUSED user_data)
69 {
70         proc_key_s *key_a = (proc_key_s *)a;
71         proc_key_s *key_b = (proc_key_s *)b;
72
73         return key_a->pid - key_b->pid;
74 }
75
76 static void __proc_tree_value_free(gpointer data)
77 {
78         proc_value_s *value = (proc_value_s *)data;
79
80         FREE(value);
81 }
82
83 static void __proc_tree_key_free(gpointer data)
84 {
85         proc_key_s *key = (proc_key_s *)data;
86
87         FREE(key);
88 }
89
90 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
91 {
92         proc_value_s *lookup;
93
94         if (proc_tree == NULL) {
95                 STC_LOGE("tree is null");
96                 return NULL;
97         }
98
99         lookup = g_tree_lookup(proc_tree, key);
100         return lookup;
101 }
102
103 static void __proc_tree_add(proc_key_s *key,
104                             proc_value_s *value)
105 {
106         if (proc_tree == NULL) {
107                 STC_LOGE("tree is null");
108                 return;
109         }
110
111         g_tree_insert(proc_tree, key, value);
112
113         stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
114                                        value->cmdline, value->cmdline,
115                                        STC_APP_TYPE_SERVICE);
116 }
117
118 static void __proc_tree_remove(const proc_key_s *key)
119 {
120         if (proc_tree == NULL) {
121                 STC_LOGE("tree is null");
122                 return;
123         }
124
125         stc_manager_app_status_changed(STC_CMD_SET_TERMINATED, key->pid, NULL,
126                                        NULL, STC_APP_TYPE_NONE);
127         g_tree_remove(proc_tree, key);
128 }
129
130 static void __open_nl_connector_sock(void)
131 {
132         __STC_LOG_FUNC_ENTER__;
133         GIOChannel *gio = NULL;
134
135         if (nl_connector_sock != -1 &&
136             nl_connector_gsource_id != 0) {
137                 STC_LOGE("Socket is already open");
138                 __STC_LOG_FUNC_EXIT__;
139                 return;
140         }
141
142         if (nl_connector_sock != -1) {
143                 close(nl_connector_sock);
144                 nl_connector_sock = -1;
145         }
146
147         if (nl_connector_gsource_id != 0) {
148                 g_source_remove(nl_connector_gsource_id);
149                 nl_connector_gsource_id = 0;
150         }
151
152         nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
153         if (nl_connector_sock == -1) {
154                 __STC_LOG_FUNC_EXIT__;
155                 return;
156         }
157
158         gio = g_io_channel_unix_new(nl_connector_sock);
159         nl_connector_gsource_id =
160                 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
161                                (GIOFunc) __process_nl_connector_message,
162                                NULL);
163         g_io_channel_unref(gio);
164         __STC_LOG_FUNC_EXIT__;
165 }
166
167 static void __close_nl_connector_sock(void)
168 {
169         __STC_LOG_FUNC_ENTER__;
170         if (nl_connector_sock != -1) {
171                 close(nl_connector_sock);
172                 nl_connector_sock = -1;
173         }
174
175         if (nl_connector_gsource_id != 0) {
176                 g_source_remove(nl_connector_gsource_id);
177                 nl_connector_gsource_id = 0;
178         }
179         __STC_LOG_FUNC_EXIT__;
180 }
181
182 static void __reopen_nl_connector_sock(void)
183 {
184         __close_nl_connector_sock();
185         __open_nl_connector_sock();
186 }
187
188 stc_error_e stc_manager_app_status_changed(stc_cmd_type_e cmd,
189                                            pid_t pid,
190                                            gchar *app_id,
191                                            gchar *pkg_id,
192                                            stc_app_type_e app_type)
193 {
194         __STC_LOG_FUNC_ENTER__;
195         stc_error_e ret = STC_ERROR_NONE;
196
197         switch (cmd) {
198         case STC_CMD_SET_FOREGRD:
199         {
200                 stc_app_key_s app_key;
201                 stc_app_value_s app_value;
202                 stc_process_key_s proc_key;
203                 stc_process_value_s proc_value;
204
205                 memset(&app_key, 0, sizeof(stc_app_key_s));
206                 memset(&app_value, 0, sizeof(stc_app_value_s));
207                 memset(&proc_key, 0, sizeof(stc_process_key_s));
208                 memset(&proc_value, 0, sizeof(stc_process_value_s));
209
210                 app_key.pkg_id = g_strdup(pkg_id);
211                 app_key.app_id = g_strdup(app_id);
212
213                 app_value.type = app_type;
214                 app_value.processes = NULL;
215
216                 proc_key.pid = pid;
217
218                 proc_value.ground = STC_APP_STATE_FOREGROUND;
219
220                 stc_monitor_application_add(app_key, app_value);
221                 stc_monitor_process_add(app_key, proc_key, proc_value);
222                 stc_monitor_process_update_ground(app_key, proc_key,
223                                                   STC_APP_STATE_FOREGROUND);
224
225                 FREE(app_key.pkg_id);
226                 FREE(app_key.app_id);
227                 break;
228         }
229         case STC_CMD_SET_BACKGRD:
230         {
231                 stc_app_key_s app_key;
232                 stc_app_value_s app_value;
233                 stc_process_key_s proc_key;
234                 stc_process_value_s proc_value;
235
236                 memset(&app_key, 0, sizeof(stc_app_key_s));
237                 memset(&app_value, 0, sizeof(stc_app_value_s));
238                 memset(&proc_key, 0, sizeof(stc_process_key_s));
239                 memset(&proc_value, 0, sizeof(stc_process_value_s));
240
241                 app_key.pkg_id = g_strdup(pkg_id);
242                 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
243                                              NULL);
244
245                 app_value.type = app_type;
246                 app_value.processes = NULL;
247
248                 proc_key.pid = pid;
249
250                 proc_value.ground = STC_APP_STATE_BACKGROUND;
251
252                 stc_monitor_application_add(app_key, app_value);
253                 stc_monitor_process_add(app_key, proc_key, proc_value);
254                 stc_monitor_process_update_ground(app_key, proc_key,
255                                                   STC_APP_STATE_BACKGROUND);
256
257                 FREE(app_key.pkg_id);
258                 FREE(app_key.app_id);
259                 break;
260         }
261         case STC_CMD_SET_SERVICE_LAUNCHED:
262         {
263                 stc_app_key_s app_key;
264                 stc_app_value_s app_value;
265                 stc_process_key_s proc_key;
266                 stc_process_value_s proc_value;
267
268                 memset(&app_key, 0, sizeof(stc_app_key_s));
269                 memset(&app_value, 0, sizeof(stc_app_value_s));
270                 memset(&proc_key, 0, sizeof(stc_process_key_s));
271                 memset(&proc_value, 0, sizeof(stc_process_value_s));
272
273                 app_key.pkg_id = g_strdup(pkg_id);
274                 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
275                                              NULL);
276
277                 app_value.type = app_type;
278                 app_value.processes = NULL;
279
280                 proc_key.pid = pid;
281
282                 /* services will run always in background. */
283                 proc_value.ground = STC_APP_STATE_BACKGROUND;
284
285                 stc_monitor_application_add(app_key, app_value);
286                 stc_monitor_process_add(app_key, proc_key, proc_value);
287
288                 FREE(app_key.pkg_id);
289                 g_free(app_key.app_id);
290                 break;
291         }
292         case STC_CMD_SET_TERMINATED:
293         {
294                 stc_monitor_process_remove(pid);
295                 break;
296         }
297         default:
298                 STC_LOGE("Unhandled command");
299                 ret = STC_ERROR_INVALID_PARAMETER;
300         }
301
302         __STC_LOG_FUNC_EXIT__;
303         return ret;
304 }
305
306 static void __process_event_exec(int tid, int pid)
307 {
308         char cmdline[PROC_NAME_MAX] = {0, };
309         char status[PROC_BUF_MAX] = {0, };
310
311         if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
312             STC_ERROR_NONE == proc_get_status(pid, status, PROC_BUF_MAX)) {
313
314                 if (!g_strcmp0(cmdline, "iptables") || !g_strcmp0(cmdline, "ip6tables") ||
315                     !g_strcmp0(cmdline, "modprobe") || !g_strcmp0(cmdline, "net-cls-release")) {
316                         return;
317                 }
318
319                 proc_key_s *key;
320                 proc_value_s *value;
321
322                 key = MALLOC0(proc_key_s, 1);
323                 if (key == NULL) {
324                         STC_LOGE("memory allocation failed");
325                         return;
326                 }
327
328                 value = MALLOC0(proc_value_s, 1);
329                 if (value == NULL) {
330                         STC_LOGE("memory allocation failed");
331                         FREE(key);
332                         return;
333                 }
334
335                 key->pid = pid;
336                 g_strlcpy(value->status, status, sizeof(value->status));
337                 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
338
339                 __proc_tree_add(key, value);
340
341                 STC_LOGD("EXEC:pid=%d,tgid=%d\t[%s]\t[%s]", pid, tid, status,
342                          cmdline);
343         }
344 }
345
346 static void __process_event_exit(int tid, int pid, int exit_code)
347 {
348         proc_key_s key;
349         proc_value_s *lookup;
350
351         key.pid = pid;
352         lookup = __proc_tree_lookup(&key);
353         if (lookup == NULL) /* unmonitored process */
354                 return;
355
356         __proc_tree_remove(&key);
357
358         STC_LOGD("EXIT:pid=%d,%d ruid=%d,euid=%d", pid, tid, exit_code);
359 }
360
361 static gboolean __process_nl_connector_message(GIOChannel *source,
362                                                GIOCondition condition,
363                                                gpointer user_data)
364 {
365         int ret;
366         int sock = g_io_channel_unix_get_fd(source);
367         nl_connector_proc_event_s msg;
368
369         if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
370             (condition & G_IO_NVAL)) {
371                 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
372
373                 STC_LOGE("Netlink Connector socket received G_IO event, closing"
374                          " socket. G_IO_ERR [%d], G_IO_HUP [%d], G_IO_NVAL [%s]",
375                          (condition & G_IO_ERR), (condition & G_IO_HUP),
376                          (condition & G_IO_NVAL));
377                 __reopen_nl_connector_sock();
378                 __STC_LOG_FUNC_EXIT__;
379                 return FALSE;
380         }
381
382         memset(&msg, 0, sizeof(nl_connector_proc_event_s));
383
384         ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
385         if (ret == 0) {
386                 __STC_LOG_FUNC_EXIT__;
387                 return TRUE;
388         }
389
390         switch (msg.proc_ev.what) {
391         case PROC_EVENT_EXEC:
392                 __process_event_exec(msg.proc_ev.event_data.exec.process_pid,
393                                      msg.proc_ev.event_data.exec.process_tgid);
394                 break;
395         case PROC_EVENT_EXIT:
396                 __process_event_exit(msg.proc_ev.event_data.exit.process_pid,
397                                      msg.proc_ev.event_data.exit.process_tgid,
398                                      msg.proc_ev.event_data.exit.exit_code);
399                 break;
400         default:
401                 ; /* Do nothing */
402         }
403
404         return TRUE;
405 }
406
407 static int __subscribe_proc_events(void)
408 {
409         __STC_LOG_FUNC_ENTER__;
410         nl_connector_msg_s msg;
411         int ret;
412         int sock = nl_connector_sock;
413
414         if (sock == -1) {
415                 __STC_LOG_FUNC_EXIT__;
416                 return -1;
417         }
418
419         memset(&msg, 0, sizeof(nl_connector_msg_s));
420
421         msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
422         msg.nl_hdr.nlmsg_pid = getpid();
423         msg.nl_hdr.nlmsg_type = NLMSG_DONE;
424
425         msg.cn_msg.id.idx = CN_IDX_PROC;
426         msg.cn_msg.id.val = CN_VAL_PROC;
427         msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
428
429         msg.cn_mcast = PROC_CN_MCAST_LISTEN;
430
431         ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
432         if (ret == -1) {
433                 STC_LOGE("Error sending netlink connector message");
434                 __STC_LOG_FUNC_EXIT__;
435                 return -1;
436         }
437
438         __STC_LOG_FUNC_EXIT__;
439         return 0;
440 }
441
442 static int __unsubscribe_proc_events(void)
443 {
444         __STC_LOG_FUNC_ENTER__;
445         nl_connector_msg_s msg;
446         int ret;
447         int sock = nl_connector_sock;
448
449         if (sock == -1) {
450                 __STC_LOG_FUNC_EXIT__;
451                 return -1;
452         }
453
454         memset(&msg, 0, sizeof(nl_connector_msg_s));
455
456         msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
457         msg.nl_hdr.nlmsg_pid = getpid();
458         msg.nl_hdr.nlmsg_type = NLMSG_DONE;
459
460         msg.cn_msg.id.idx = CN_IDX_PROC;
461         msg.cn_msg.id.val = CN_VAL_PROC;
462         msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
463
464         msg.cn_mcast = PROC_CN_MCAST_IGNORE;
465
466         ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
467         if (ret == -1) {
468                 STC_LOGE("Error sending netlink connector message");
469                 __STC_LOG_FUNC_EXIT__;
470                 return -1;
471         }
472
473         __STC_LOG_FUNC_EXIT__;
474         return 0;
475 }
476
477 void stc_app_lifecycle_monitor_init(void)
478 {
479         __STC_LOG_FUNC_ENTER__;
480
481         proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
482                                     __proc_tree_key_free,
483                                     __proc_tree_value_free);
484
485         /* TODO: Fill proc tree with current procfs state */
486
487         __open_nl_connector_sock();
488         __subscribe_proc_events();
489         __STC_LOG_FUNC_EXIT__;
490 }
491
492 void stc_app_lifecycle_monitor_deinit(void)
493 {
494         __STC_LOG_FUNC_ENTER__;
495
496         if (nl_connector_sock == -1) {
497                 STC_LOGE("socket already closed");
498                 return;
499         }
500
501         __unsubscribe_proc_events();
502         __close_nl_connector_sock();
503
504         g_tree_destroy(proc_tree);
505         proc_tree = NULL;
506
507         __STC_LOG_FUNC_EXIT__;
508 }