2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
24 #include <sys/socket.h>
25 #include <linux/netlink.h>
26 #include <linux/connector.h>
27 #include <linux/cn_proc.h>
30 #include "stc-manager-gdbus.h"
31 #include "stc-app-lifecycle.h"
32 #include "helper-procfs.h"
39 char cmdline[PROC_NAME_MAX];
40 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
43 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
44 struct nlmsghdr nl_hdr;
45 struct __attribute__ ((__packed__)) {
47 enum proc_cn_mcast_op cn_mcast;
51 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
52 struct nlmsghdr nl_hdr;
53 struct __attribute__ ((__packed__)) {
55 struct proc_event proc_ev;
57 } nl_connector_proc_event_s;
59 static int nl_connector_sock = -1;
60 static guint nl_connector_gsource_id = 0;
61 static GTree *proc_tree;
64 static gboolean __process_nl_connector_message(GIOChannel *source,
65 GIOCondition condition,
68 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
69 gpointer UNUSED user_data)
71 proc_key_s *key_a = (proc_key_s *)a;
72 proc_key_s *key_b = (proc_key_s *)b;
74 return key_a->pid - key_b->pid;
77 static void __proc_tree_value_free(gpointer data)
79 proc_value_s *value = (proc_value_s *)data;
84 static void __proc_tree_key_free(gpointer data)
86 proc_key_s *key = (proc_key_s *)data;
91 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
95 if (proc_tree == NULL) {
96 STC_LOGE("tree is null"); //LCOV_EXCL_LINE
97 return NULL; //LCOV_EXCL_LINE
100 lookup = g_tree_lookup(proc_tree, key);
105 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
108 proc_key_s *proc_key = (proc_key_s *)key;
109 proc_value_s *proc_value = (proc_value_s *)value;
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);
119 static void __proc_tree_printall(void)
121 g_tree_foreach(proc_tree, __proc_tree_foreach_print, NULL); //LCOV_EXCL_LINE
124 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
126 proc_value_s *parent = NULL;
127 proc_value_s *lookup = value;
131 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
132 lookup = __proc_tree_lookup(&key);
138 if (parent != NULL) //LCOV_EXCL_LINE
139 STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] pid[%s] " //LCOV_EXCL_LINE
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]);
149 static void __proc_tree_add(proc_key_s *key,
152 proc_value_s *lookup;
153 proc_value_s *parent;
155 if (proc_tree == NULL) {
156 STC_LOGE("tree is null"); //LCOV_EXCL_LINE
157 return; //LCOV_EXCL_LINE
160 lookup = g_tree_lookup(proc_tree, key);
163 STC_LOGD("LOOKUP: tgid[\033[1;33m%s\033[0;m] pid[%s] ppid[\033[1;35m%s\033[0;m] " //LCOV_EXCL_LINE
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]);
170 STC_LOGD("cmdline [%s] pid[%s] ppid[%s]", value->cmdline,
171 value->status[PROC_STATUS_PID], value->status[PROC_STATUS_PPID]);
173 g_tree_insert(proc_tree, key, value);
176 __proc_tree_printall();
178 parent = __proc_tree_find_parent(value);
180 stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
181 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
183 stc_manager_app_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
184 value->cmdline, value->cmdline, STC_APP_TYPE_SERVICE);
187 static void __proc_tree_remove(const proc_key_s *key)
189 if (proc_tree == NULL) {
190 STC_LOGE("tree is null"); //LCOV_EXCL_LINE
191 return; //LCOV_EXCL_LINE
194 stc_manager_app_status_changed(STC_CMD_SET_TERMINATED, key->pid, NULL,
195 NULL, STC_APP_TYPE_NONE);
197 g_tree_remove(proc_tree, key);
200 __proc_tree_printall(); //LCOV_EXCL_LINE
203 static gboolean __check_excn(char *cmdline)
205 stc_error_e ret = STC_ERROR_NONE;
207 if (cmdline[0] == '(')
210 ret = stc_monitor_check_excn_by_cmdline(cmdline);
211 if (ret == STC_ERROR_NO_DATA)
217 static void __open_nl_connector_sock(void)
219 __STC_LOG_FUNC_ENTER__;
220 GIOChannel *gio = NULL;
222 if (nl_connector_sock != -1 &&
223 nl_connector_gsource_id != 0) { //LCOV_EXCL_LINE
224 STC_LOGE("Socket is already open"); //LCOV_EXCL_LINE
225 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
226 return; //LCOV_EXCL_LINE
229 if (nl_connector_sock != -1) {
230 close(nl_connector_sock); //LCOV_EXCL_LINE
231 nl_connector_sock = -1; //LCOV_EXCL_LINE
234 if (nl_connector_gsource_id != 0) {
235 g_source_remove(nl_connector_gsource_id); //LCOV_EXCL_LINE
236 nl_connector_gsource_id = 0; //LCOV_EXCL_LINE
239 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
240 if (nl_connector_sock == -1) {
241 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
242 return; //LCOV_EXCL_LINE
245 gio = g_io_channel_unix_new(nl_connector_sock);
246 nl_connector_gsource_id =
247 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
248 (GIOFunc) __process_nl_connector_message,
250 g_io_channel_unref(gio);
251 __STC_LOG_FUNC_EXIT__;
254 static void __close_nl_connector_sock(void)
256 __STC_LOG_FUNC_ENTER__;
257 if (nl_connector_sock != -1) {
258 close(nl_connector_sock);
259 nl_connector_sock = -1;
262 if (nl_connector_gsource_id != 0) {
263 g_source_remove(nl_connector_gsource_id);
264 nl_connector_gsource_id = 0;
266 __STC_LOG_FUNC_EXIT__;
269 static void __reopen_nl_connector_sock(void)
271 __close_nl_connector_sock(); //LCOV_EXCL_LINE
272 __open_nl_connector_sock(); //LCOV_EXCL_LINE
276 stc_error_e stc_manager_app_status_changed(stc_cmd_type_e cmd,
280 stc_app_type_e app_type)
282 stc_error_e ret = STC_ERROR_NONE;
284 if (pkg_id && app_id)
285 STC_LOGD("cmd [%d] pkgid [%s] appid [%s] pid[%d] type [%d]",
286 cmd, pkg_id, app_id, pid, app_type);
289 case STC_CMD_SET_FOREGRD:
291 stc_app_key_s app_key;
292 stc_app_value_s app_value;
293 stc_process_key_s proc_key;
294 stc_process_value_s proc_value;
296 memset(&app_key, 0, sizeof(stc_app_key_s));
297 memset(&app_value, 0, sizeof(stc_app_value_s));
298 memset(&proc_key, 0, sizeof(stc_process_key_s));
299 memset(&proc_value, 0, sizeof(stc_process_value_s));
301 app_key.pkg_id = g_strdup(pkg_id);
302 app_key.app_id = g_strdup(app_id);
304 app_value.type = app_type;
305 app_value.processes = NULL;
309 proc_value.ground = STC_APP_STATE_FOREGROUND;
311 stc_monitor_application_add(app_key, app_value);
312 stc_monitor_process_add(app_key, proc_key, proc_value);
313 stc_monitor_process_update_ground(app_key, proc_key,
314 STC_APP_STATE_FOREGROUND);
316 FREE(app_key.pkg_id);
317 FREE(app_key.app_id);
320 case STC_CMD_SET_BACKGRD:
322 stc_app_key_s app_key;
323 stc_app_value_s app_value;
324 stc_process_key_s proc_key;
325 stc_process_value_s proc_value;
327 memset(&app_key, 0, sizeof(stc_app_key_s));
328 memset(&app_value, 0, sizeof(stc_app_value_s));
329 memset(&proc_key, 0, sizeof(stc_process_key_s));
330 memset(&proc_value, 0, sizeof(stc_process_value_s));
332 app_key.pkg_id = g_strdup(pkg_id);
333 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
336 app_value.type = app_type;
337 app_value.processes = NULL;
341 proc_value.ground = STC_APP_STATE_BACKGROUND;
343 stc_monitor_application_add(app_key, app_value);
344 stc_monitor_process_add(app_key, proc_key, proc_value);
345 stc_monitor_process_update_ground(app_key, proc_key,
346 STC_APP_STATE_BACKGROUND);
348 FREE(app_key.pkg_id);
349 FREE(app_key.app_id);
352 case STC_CMD_SET_SERVICE_LAUNCHED:
354 stc_app_key_s app_key;
355 stc_app_value_s app_value;
356 stc_process_key_s proc_key;
357 stc_process_value_s proc_value;
359 memset(&app_key, 0, sizeof(stc_app_key_s));
360 memset(&app_value, 0, sizeof(stc_app_value_s));
361 memset(&proc_key, 0, sizeof(stc_process_key_s));
362 memset(&proc_value, 0, sizeof(stc_process_value_s));
364 app_key.pkg_id = g_strdup(pkg_id);
365 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
368 app_value.type = app_type;
369 app_value.processes = NULL;
373 /* services will run always in background. */
374 proc_value.ground = STC_APP_STATE_BACKGROUND;
376 stc_monitor_application_add(app_key, app_value);
377 stc_monitor_process_add(app_key, proc_key, proc_value);
379 FREE(app_key.pkg_id);
380 g_free(app_key.app_id);
383 case STC_CMD_SET_TERMINATED:
385 stc_monitor_process_remove(pid);
389 STC_LOGE("Unhandled command");
390 ret = STC_ERROR_INVALID_PARAMETER;
397 static void __process_event_fork(int tgid, int pid)
399 char cmdline[PROC_NAME_MAX] = {0, };
400 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
402 memset(status, 0x0, sizeof(status));
404 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
405 STC_ERROR_NONE == proc_get_status(pid, status)) {
407 if (__check_excn(cmdline)) {
409 STC_LOGD("[%s] monitoring is excepted", cmdline); //LCOV_EXCL_LINE
417 key = MALLOC0(proc_key_s, 1);
419 STC_LOGE("memory allocation failed"); //LCOV_EXCL_LINE
420 return; //LCOV_EXCL_LINE
423 value = MALLOC0(proc_value_s, 1);
425 STC_LOGE("memory allocation failed"); //LCOV_EXCL_LINE
426 FREE(key); //LCOV_EXCL_LINE
427 return; //LCOV_EXCL_LINE
431 for (i = 0; i < PROC_STATUS_CNT; ++i)
432 g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
433 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
436 STC_LOGD("\033[1;34mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] " //LCOV_EXCL_LINE
437 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
438 STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]", //LCOV_EXCL_LINE
439 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
440 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
443 __proc_tree_add(key, value);
447 static void __process_event_exec(int tgid, int pid)
449 char cmdline[PROC_NAME_MAX] = {0, };
450 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
452 memset(status, 0x0, sizeof(status));
454 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
455 STC_ERROR_NONE == proc_get_status(pid, status)) {
457 if (__check_excn(cmdline)) {
459 STC_LOGD("[%s] monitoring is excepted", cmdline); //LCOV_EXCL_LINE
467 key = MALLOC0(proc_key_s, 1);
469 STC_LOGE("memory allocation failed"); //LCOV_EXCL_LINE
470 return; //LCOV_EXCL_LINE
473 value = MALLOC0(proc_value_s, 1);
475 STC_LOGE("memory allocation failed"); //LCOV_EXCL_LINE
476 FREE(key); //LCOV_EXCL_LINE
477 return; //LCOV_EXCL_LINE
481 for (i = 0; i < PROC_STATUS_CNT; ++i)
482 g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
483 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
486 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] " //LCOV_EXCL_LINE
487 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
488 STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]", //LCOV_EXCL_LINE
489 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
490 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
493 __proc_tree_add(key, value);
497 static void __process_event_exit(int tgid, int pid, int exit_code)
500 proc_value_s *lookup;
506 lookup = __proc_tree_lookup(&key);
507 if (lookup == NULL) /* unmonitored process */
511 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] " //LCOV_EXCL_LINE
512 "pid[%d] exitcode[\033[0;31m%d\033[0;m]", tgid, pid, exit_code);
514 __proc_tree_remove(&key);
517 static gboolean __process_nl_connector_message(GIOChannel *source,
518 GIOCondition condition,
522 int sock = g_io_channel_unix_get_fd(source);
523 nl_connector_proc_event_s msg;
525 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
526 (condition & G_IO_NVAL)) {
527 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
529 STC_LOGE("Netlink Connector socket received G_IO event, closing" //LCOV_EXCL_LINE
530 " socket. G_IO_ERR [%d], G_IO_HUP [%d], G_IO_NVAL [%s]",
531 (condition & G_IO_ERR), (condition & G_IO_HUP),
532 (condition & G_IO_NVAL));
533 __reopen_nl_connector_sock(); //LCOV_EXCL_LINE
534 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
535 return FALSE; //LCOV_EXCL_LINE
538 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
540 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
542 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
543 return TRUE; //LCOV_EXCL_LINE
546 switch (msg.proc_ev.what) {
547 case PROC_EVENT_FORK:
548 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
549 msg.proc_ev.event_data.fork.child_pid);
551 case PROC_EVENT_EXEC:
552 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
553 msg.proc_ev.event_data.exec.process_pid);
555 case PROC_EVENT_EXIT:
556 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
557 msg.proc_ev.event_data.exit.process_pid,
558 msg.proc_ev.event_data.exit.exit_code);
567 static int __subscribe_proc_events(void)
569 __STC_LOG_FUNC_ENTER__;
570 nl_connector_msg_s msg;
572 int sock = nl_connector_sock;
575 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
576 return -1; //LCOV_EXCL_LINE
579 memset(&msg, 0, sizeof(nl_connector_msg_s));
581 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
582 msg.nl_hdr.nlmsg_pid = getpid();
583 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
585 msg.cn_msg.id.idx = CN_IDX_PROC;
586 msg.cn_msg.id.val = CN_VAL_PROC;
587 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
589 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
591 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
593 STC_LOGE("Error sending netlink connector message"); //LCOV_EXCL_LINE
594 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
595 return -1; //LCOV_EXCL_LINE
598 __STC_LOG_FUNC_EXIT__;
602 static int __unsubscribe_proc_events(void)
604 __STC_LOG_FUNC_ENTER__;
605 nl_connector_msg_s msg;
607 int sock = nl_connector_sock;
610 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
611 return -1; //LCOV_EXCL_LINE
614 memset(&msg, 0, sizeof(nl_connector_msg_s));
616 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
617 msg.nl_hdr.nlmsg_pid = getpid();
618 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
620 msg.cn_msg.id.idx = CN_IDX_PROC;
621 msg.cn_msg.id.val = CN_VAL_PROC;
622 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
624 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
626 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
628 STC_LOGE("Error sending netlink connector message"); //LCOV_EXCL_LINE
629 __STC_LOG_FUNC_EXIT__; //LCOV_EXCL_LINE
630 return -1; //LCOV_EXCL_LINE
633 __STC_LOG_FUNC_EXIT__;
637 void stc_app_lifecycle_monitor_init(void)
639 __STC_LOG_FUNC_ENTER__;
641 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
642 __proc_tree_key_free,
643 __proc_tree_value_free);
645 /* TODO: Fill proc tree with current procfs state */
647 __open_nl_connector_sock();
648 __subscribe_proc_events();
649 __STC_LOG_FUNC_EXIT__;
652 void stc_app_lifecycle_monitor_deinit(void)
654 __STC_LOG_FUNC_ENTER__;
656 if (nl_connector_sock == -1) {
657 STC_LOGE("socket already closed"); //LCOV_EXCL_LINE
658 return; //LCOV_EXCL_LINE
661 __unsubscribe_proc_events();
662 __close_nl_connector_sock();
664 g_tree_destroy(proc_tree);
667 __STC_LOG_FUNC_EXIT__;