09fcef07cf020b02c2bbed46841820f966cfaef4
[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-manager-gdbus.h"
31 #include "stc-app-lifecycle.h"
32 #include "helper-procfs.h"
33
34 typedef struct {
35         pid_t pid;
36 } proc_key_s;
37
38 typedef struct {
39         char cmdline[PROC_NAME_MAX];
40         char status[PROC_STATUS_CNT][PROC_BUF_MAX];
41 } proc_value_s;
42
43 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
44         struct nlmsghdr nl_hdr;
45         struct __attribute__ ((__packed__)) {
46                 struct cn_msg cn_msg;
47                 enum proc_cn_mcast_op cn_mcast;
48         };
49 } nl_connector_msg_s;
50
51 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
52         struct nlmsghdr nl_hdr;
53         struct __attribute__ ((__packed__)) {
54                 struct cn_msg cn_msg;
55                 struct proc_event proc_ev;
56         };
57 } nl_connector_proc_event_s;
58
59 static int nl_connector_sock = -1;
60 static guint nl_connector_gsource_id = 0;
61 static GTree *proc_tree;
62
63
64 static gboolean __process_nl_connector_message(GIOChannel *source,
65                                                GIOCondition condition,
66                                                gpointer user_data);
67
68 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
69                                    gpointer UNUSED user_data)
70 {
71         proc_key_s *key_a = (proc_key_s *)a;
72         proc_key_s *key_b = (proc_key_s *)b;
73
74         return key_a->pid - key_b->pid;
75 }
76
77 static void __proc_tree_value_free(gpointer data)
78 {
79         proc_value_s *value = (proc_value_s *)data;
80
81         FREE(value);
82 }
83
84 static void __proc_tree_key_free(gpointer data)
85 {
86         proc_key_s *key = (proc_key_s *)data;
87
88         FREE(key);
89 }
90
91 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
92 {
93         proc_value_s *lookup;
94
95         if (proc_tree == NULL) {
96                 STC_LOGE("tree is null");
97                 return NULL;
98         }
99
100         lookup = g_tree_lookup(proc_tree, key);
101         return lookup;
102 }
103
104 #if STC_DEBUG_LOG
105 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
106                                           gpointer data)
107 {
108         proc_key_s *proc_key = (proc_key_s *)key;
109         proc_value_s *proc_value = (proc_value_s *)value;
110
111         STC_LOGD("Proc pid [\033[1;33m%d\033[0;m] ppid [\033[1;35m%s\033[0;m] "
112                 "cmdline [\033[0;34m%s\033[0;m]", proc_key->pid,
113                 proc_value->status[PROC_STATUS_PPID], proc_value->cmdline);
114
115         return FALSE;
116 }
117
118 static void __proc_tree_printall(void)
119 {
120         g_tree_foreach(proc_tree, __proc_tree_foreach_print, NULL);
121 }
122 #endif
123
124 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
125 {
126         proc_value_s *parent = NULL;
127         proc_value_s *lookup = value;
128
129         do {
130                 proc_key_s key;
131                 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
132                 lookup = __proc_tree_lookup(&key);
133                 if (lookup != NULL)
134                         parent = lookup;
135         } while (lookup);
136
137 #if STC_DEBUG_LOG
138                 if (parent != NULL)
139                         STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] pid[%s] "
140                                 "ppid[\033[1;35m%s\033[0;m] cmdline[\033[0;34m%s\033[0;m] name[%s]",
141                                 parent->status[PROC_STATUS_TGID], parent->status[PROC_STATUS_PID],
142                                 parent->status[PROC_STATUS_PPID], parent->cmdline,
143                                 parent->status[PROC_STATUS_NAME]);
144 #endif
145
146         return parent;
147 }
148
149 static void __proc_tree_add(proc_key_s *key,
150                             proc_value_s *value)
151 {
152         proc_value_s *lookup;
153         proc_value_s *parent;
154
155         if (proc_tree == NULL) {
156                 STC_LOGE("tree is null");
157                 return;
158         }
159
160         lookup = g_tree_lookup(proc_tree, key);
161         if (lookup) {
162 #if STC_DEBUG_LOG
163                         STC_LOGD("LOOKUP: tgid[\033[1;33m%s\033[0;m] pid[%s] ppid[\033[1;35m%s\033[0;m] "
164                                 "cmdline[\033[0;34m%s\033[0;m] name[%s]", lookup->status[PROC_STATUS_TGID],
165                                 lookup->status[PROC_STATUS_PID], lookup->status[PROC_STATUS_PPID],
166                                 lookup->cmdline, lookup->status[PROC_STATUS_NAME]);
167 #endif
168                 return;
169         }
170
171         STC_LOGD("cmdline [%s] pid[%s] ppid[%s]", value->cmdline,
172                 value->status[PROC_STATUS_PID], value->status[PROC_STATUS_PPID]);
173
174         g_tree_insert(proc_tree, key, value);
175
176 #if STC_DEBUG_LOG
177                 __proc_tree_printall();
178 #endif
179
180         parent = __proc_tree_find_parent(value);
181         if (parent != NULL)
182                 stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
183                         parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
184         else
185                 stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
186                         value->cmdline, value->cmdline, STC_APP_TYPE_SERVICE);
187 }
188
189 static void __proc_tree_remove(const proc_key_s *key)
190 {
191         if (proc_tree == NULL) {
192                 STC_LOGE("tree is null");
193                 return;
194         }
195
196         stc_manager_app_status_changed(STC_CMD_SET_TERMINATED, key->pid, NULL,
197                                        NULL, STC_APP_TYPE_NONE);
198         g_tree_remove(proc_tree, key);
199
200 #if STC_DEBUG_LOG
201                 __proc_tree_printall();
202 #endif
203 }
204
205 static gboolean __check_excn(char *cmdline)
206 {
207         stc_error_e ret = stc_monitor_check_excn_by_cmdline(cmdline);
208
209         if (ret == STC_ERROR_NO_DATA)
210                 return FALSE;
211         else
212                 return TRUE;
213 }
214
215 static void __open_nl_connector_sock(void)
216 {
217         __STC_LOG_FUNC_ENTER__;
218         GIOChannel *gio = NULL;
219
220         if (nl_connector_sock != -1 &&
221             nl_connector_gsource_id != 0) {
222                 STC_LOGE("Socket is already open");
223                 __STC_LOG_FUNC_EXIT__;
224                 return;
225         }
226
227         if (nl_connector_sock != -1) {
228                 close(nl_connector_sock);
229                 nl_connector_sock = -1;
230         }
231
232         if (nl_connector_gsource_id != 0) {
233                 g_source_remove(nl_connector_gsource_id);
234                 nl_connector_gsource_id = 0;
235         }
236
237         nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
238         if (nl_connector_sock == -1) {
239                 __STC_LOG_FUNC_EXIT__;
240                 return;
241         }
242
243         gio = g_io_channel_unix_new(nl_connector_sock);
244         nl_connector_gsource_id =
245                 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
246                                (GIOFunc) __process_nl_connector_message,
247                                NULL);
248         g_io_channel_unref(gio);
249         __STC_LOG_FUNC_EXIT__;
250 }
251
252 static void __close_nl_connector_sock(void)
253 {
254         __STC_LOG_FUNC_ENTER__;
255         if (nl_connector_sock != -1) {
256                 close(nl_connector_sock);
257                 nl_connector_sock = -1;
258         }
259
260         if (nl_connector_gsource_id != 0) {
261                 g_source_remove(nl_connector_gsource_id);
262                 nl_connector_gsource_id = 0;
263         }
264         __STC_LOG_FUNC_EXIT__;
265 }
266
267 static void __reopen_nl_connector_sock(void)
268 {
269         __close_nl_connector_sock();
270         __open_nl_connector_sock();
271 }
272
273 stc_error_e stc_manager_app_status_changed(stc_cmd_type_e cmd,
274                                            pid_t pid,
275                                            const gchar *app_id,
276                                            const gchar *pkg_id,
277                                            stc_app_type_e app_type)
278 {
279         stc_error_e ret = STC_ERROR_NONE;
280
281         if (pkg_id && app_id)
282                 STC_LOGD("cmd [%d] pkgid [%s] appid [%s] pid[%d] type [%d]",
283                         cmd, pkg_id, app_id, pid, app_type);
284
285         switch (cmd) {
286         case STC_CMD_SET_FOREGRD:
287         {
288                 stc_app_key_s app_key;
289                 stc_app_value_s app_value;
290                 stc_process_key_s proc_key;
291                 stc_process_value_s proc_value;
292
293                 memset(&app_key, 0, sizeof(stc_app_key_s));
294                 memset(&app_value, 0, sizeof(stc_app_value_s));
295                 memset(&proc_key, 0, sizeof(stc_process_key_s));
296                 memset(&proc_value, 0, sizeof(stc_process_value_s));
297
298                 app_key.pkg_id = g_strdup(pkg_id);
299                 app_key.app_id = g_strdup(app_id);
300
301                 app_value.type = app_type;
302                 app_value.processes = NULL;
303
304                 proc_key.pid = pid;
305
306                 proc_value.ground = STC_APP_STATE_FOREGROUND;
307
308                 stc_monitor_application_add(app_key, app_value);
309                 stc_monitor_process_add(app_key, proc_key, proc_value);
310                 stc_monitor_process_update_ground(app_key, proc_key,
311                                                   STC_APP_STATE_FOREGROUND);
312
313                 FREE(app_key.pkg_id);
314                 FREE(app_key.app_id);
315                 break;
316         }
317         case STC_CMD_SET_BACKGRD:
318         {
319                 stc_app_key_s app_key;
320                 stc_app_value_s app_value;
321                 stc_process_key_s proc_key;
322                 stc_process_value_s proc_value;
323
324                 memset(&app_key, 0, sizeof(stc_app_key_s));
325                 memset(&app_value, 0, sizeof(stc_app_value_s));
326                 memset(&proc_key, 0, sizeof(stc_process_key_s));
327                 memset(&proc_value, 0, sizeof(stc_process_value_s));
328
329                 app_key.pkg_id = g_strdup(pkg_id);
330                 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
331                                              NULL);
332
333                 app_value.type = app_type;
334                 app_value.processes = NULL;
335
336                 proc_key.pid = pid;
337
338                 proc_value.ground = STC_APP_STATE_BACKGROUND;
339
340                 stc_monitor_application_add(app_key, app_value);
341                 stc_monitor_process_add(app_key, proc_key, proc_value);
342                 stc_monitor_process_update_ground(app_key, proc_key,
343                                                   STC_APP_STATE_BACKGROUND);
344
345                 FREE(app_key.pkg_id);
346                 FREE(app_key.app_id);
347                 break;
348         }
349         case STC_CMD_SET_SERVICE_LAUNCHED:
350         {
351                 stc_app_key_s app_key;
352                 stc_app_value_s app_value;
353                 stc_process_key_s proc_key;
354                 stc_process_value_s proc_value;
355
356                 memset(&app_key, 0, sizeof(stc_app_key_s));
357                 memset(&app_value, 0, sizeof(stc_app_value_s));
358                 memset(&proc_key, 0, sizeof(stc_process_key_s));
359                 memset(&proc_value, 0, sizeof(stc_process_value_s));
360
361                 app_key.pkg_id = g_strdup(pkg_id);
362                 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
363                                              NULL);
364
365                 app_value.type = app_type;
366                 app_value.processes = NULL;
367
368                 proc_key.pid = pid;
369
370                 /* services will run always in background. */
371                 proc_value.ground = STC_APP_STATE_BACKGROUND;
372
373                 stc_monitor_application_add(app_key, app_value);
374                 stc_monitor_process_add(app_key, proc_key, proc_value);
375
376                 FREE(app_key.pkg_id);
377                 g_free(app_key.app_id);
378                 break;
379         }
380         case STC_CMD_SET_TERMINATED:
381         {
382                 stc_monitor_process_remove(pid);
383                 break;
384         }
385         default:
386                 STC_LOGE("Unhandled command");
387                 ret = STC_ERROR_INVALID_PARAMETER;
388         }
389
390         return ret;
391 }
392
393 static void __process_event_fork(int tgid, int pid)
394 {
395         char cmdline[PROC_NAME_MAX] = {0, };
396         char status[PROC_STATUS_CNT][PROC_BUF_MAX];
397
398         if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
399             STC_ERROR_NONE == proc_get_status(pid, status)) {
400
401                 if (__check_excn(cmdline)) {
402 #if STC_DEBUG_LOG
403                                 STC_LOGD("[%s] monitoring is excepted", cmdline);
404 #endif
405                         return;
406                 }
407
408                 unsigned int i;
409                 proc_key_s *key;
410                 proc_value_s *value;
411
412                 key = MALLOC0(proc_key_s, 1);
413                 if (key == NULL) {
414                         STC_LOGE("memory allocation failed");
415                         return;
416                 }
417
418                 value = MALLOC0(proc_value_s, 1);
419                 if (value == NULL) {
420                         STC_LOGE("memory allocation failed");
421                         FREE(key);
422                         return;
423                 }
424
425                 key->pid = tgid;
426                 for (i = 0; i < PROC_STATUS_CNT; ++i)
427                         g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
428                 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
429
430 #if STC_DEBUG_LOG
431                         STC_LOGD("\033[1;34mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
432                                 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
433                         STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
434                                 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
435                                 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
436 #endif
437
438                 __proc_tree_add(key, value);
439         }
440 }
441
442 static void __process_event_exec(int tgid, int pid)
443 {
444         char cmdline[PROC_NAME_MAX] = {0, };
445         char status[PROC_STATUS_CNT][PROC_BUF_MAX];
446
447         if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
448             STC_ERROR_NONE == proc_get_status(pid, status)) {
449
450                 if (__check_excn(cmdline)) {
451 #if STC_DEBUG_LOG
452                                 STC_LOGD("[%s] monitoring is excepted", cmdline);
453 #endif
454                         return;
455                 }
456
457                 unsigned int i;
458                 proc_key_s *key;
459                 proc_value_s *value;
460
461                 key = MALLOC0(proc_key_s, 1);
462                 if (key == NULL) {
463                         STC_LOGE("memory allocation failed");
464                         return;
465                 }
466
467                 value = MALLOC0(proc_value_s, 1);
468                 if (value == NULL) {
469                         STC_LOGE("memory allocation failed");
470                         FREE(key);
471                         return;
472                 }
473
474                 key->pid = tgid;
475                 for (i = 0; i < PROC_STATUS_CNT; ++i)
476                         g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
477                 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
478
479 #if STC_DEBUG_LOG
480                         STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
481                                 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
482                         STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
483                                 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
484                                 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
485 #endif
486
487                 __proc_tree_add(key, value);
488         }
489 }
490
491 static void __process_event_exit(int tgid, int pid, int exit_code)
492 {
493         proc_key_s key;
494         proc_value_s *lookup;
495
496         if (tgid != pid)
497                 return;
498
499         key.pid = tgid;
500         lookup = __proc_tree_lookup(&key);
501         if (lookup == NULL) /* unmonitored process */
502                 return;
503
504 #if STC_DEBUG_LOG
505                 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
506                         "pid[%d] exitcode[\033[0;31m%d\033[0;m]", tgid, pid, exit_code);
507 #endif
508
509         __proc_tree_remove(&key);
510 }
511
512 static gboolean __process_nl_connector_message(GIOChannel *source,
513                                                GIOCondition condition,
514                                                gpointer user_data)
515 {
516         int ret;
517         int sock = g_io_channel_unix_get_fd(source);
518         nl_connector_proc_event_s msg;
519
520         if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
521             (condition & G_IO_NVAL)) {
522                 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
523
524                 STC_LOGE("Netlink Connector socket received G_IO event, closing"
525                          " socket. G_IO_ERR [%d], G_IO_HUP [%d], G_IO_NVAL [%s]",
526                          (condition & G_IO_ERR), (condition & G_IO_HUP),
527                          (condition & G_IO_NVAL));
528                 __reopen_nl_connector_sock();
529                 __STC_LOG_FUNC_EXIT__;
530                 return FALSE;
531         }
532
533         memset(&msg, 0, sizeof(nl_connector_proc_event_s));
534
535         ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
536         if (ret == 0) {
537                 __STC_LOG_FUNC_EXIT__;
538                 return TRUE;
539         }
540
541         switch (msg.proc_ev.what) {
542         case PROC_EVENT_FORK:
543                 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
544                                      msg.proc_ev.event_data.fork.child_pid);
545                 break;
546         case PROC_EVENT_EXEC:
547                 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
548                                      msg.proc_ev.event_data.exec.process_pid);
549                 break;
550         case PROC_EVENT_EXIT:
551                 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
552                                      msg.proc_ev.event_data.exit.process_pid,
553                                      msg.proc_ev.event_data.exit.exit_code);
554                 break;
555         default:
556                 ; /* Do nothing */
557         }
558
559         return TRUE;
560 }
561
562 static int __subscribe_proc_events(void)
563 {
564         __STC_LOG_FUNC_ENTER__;
565         nl_connector_msg_s msg;
566         int ret;
567         int sock = nl_connector_sock;
568
569         if (sock == -1) {
570                 __STC_LOG_FUNC_EXIT__;
571                 return -1;
572         }
573
574         memset(&msg, 0, sizeof(nl_connector_msg_s));
575
576         msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
577         msg.nl_hdr.nlmsg_pid = getpid();
578         msg.nl_hdr.nlmsg_type = NLMSG_DONE;
579
580         msg.cn_msg.id.idx = CN_IDX_PROC;
581         msg.cn_msg.id.val = CN_VAL_PROC;
582         msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
583
584         msg.cn_mcast = PROC_CN_MCAST_LISTEN;
585
586         ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
587         if (ret == -1) {
588                 STC_LOGE("Error sending netlink connector message");
589                 __STC_LOG_FUNC_EXIT__;
590                 return -1;
591         }
592
593         __STC_LOG_FUNC_EXIT__;
594         return 0;
595 }
596
597 static int __unsubscribe_proc_events(void)
598 {
599         __STC_LOG_FUNC_ENTER__;
600         nl_connector_msg_s msg;
601         int ret;
602         int sock = nl_connector_sock;
603
604         if (sock == -1) {
605                 __STC_LOG_FUNC_EXIT__;
606                 return -1;
607         }
608
609         memset(&msg, 0, sizeof(nl_connector_msg_s));
610
611         msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
612         msg.nl_hdr.nlmsg_pid = getpid();
613         msg.nl_hdr.nlmsg_type = NLMSG_DONE;
614
615         msg.cn_msg.id.idx = CN_IDX_PROC;
616         msg.cn_msg.id.val = CN_VAL_PROC;
617         msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
618
619         msg.cn_mcast = PROC_CN_MCAST_IGNORE;
620
621         ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
622         if (ret == -1) {
623                 STC_LOGE("Error sending netlink connector message");
624                 __STC_LOG_FUNC_EXIT__;
625                 return -1;
626         }
627
628         __STC_LOG_FUNC_EXIT__;
629         return 0;
630 }
631
632 void stc_app_lifecycle_monitor_init(void)
633 {
634         __STC_LOG_FUNC_ENTER__;
635
636         proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
637                                     __proc_tree_key_free,
638                                     __proc_tree_value_free);
639
640         /* TODO: Fill proc tree with current procfs state */
641
642         __open_nl_connector_sock();
643         __subscribe_proc_events();
644         __STC_LOG_FUNC_EXIT__;
645 }
646
647 void stc_app_lifecycle_monitor_deinit(void)
648 {
649         __STC_LOG_FUNC_ENTER__;
650
651         if (nl_connector_sock == -1) {
652                 STC_LOGE("socket already closed");
653                 return;
654         }
655
656         __unsubscribe_proc_events();
657         __close_nl_connector_sock();
658
659         g_tree_destroy(proc_tree);
660         proc_tree = NULL;
661
662         __STC_LOG_FUNC_EXIT__;
663 }