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-plugin-procfs.h"
31 #include "stc-monitor.h"
32 #include "helper-net-cls.h"
33 #include "helper-procfs.h"
41 char cmdline[PROC_NAME_MAX];
42 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
45 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
46 struct nlmsghdr nl_hdr;
47 struct __attribute__ ((__packed__)) {
49 enum proc_cn_mcast_op cn_mcast;
53 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
54 struct nlmsghdr nl_hdr;
55 struct __attribute__ ((__packed__)) {
57 struct proc_event proc_ev;
59 } nl_connector_proc_event_s;
61 static int nl_connector_sock = -1;
62 static guint nl_connector_gsource_id = 0;
63 static GTree *proc_tree;
65 static gboolean __process_nl_connector_message(GIOChannel *source,
66 GIOCondition condition,
69 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
70 gpointer UNUSED user_data)
72 proc_key_s *key_a = (proc_key_s *)a;
73 proc_key_s *key_b = (proc_key_s *)b;
75 return key_a->pid - key_b->pid;
78 static void __proc_tree_value_free(gpointer data)
80 proc_value_s *value = (proc_value_s *)data;
85 static void __proc_tree_key_free(gpointer data)
87 proc_key_s *key = (proc_key_s *)data;
92 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
96 if (proc_tree == NULL) {
97 STC_LOGE("tree is null");
101 lookup = g_tree_lookup(proc_tree, key);
106 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
109 proc_key_s *proc_key = (proc_key_s *)key;
110 proc_value_s *proc_value = (proc_value_s *)value;
112 STC_LOGD("Proc pid [\033[1;33m%d\033[0;m] ppid [%s] "
113 "cmdline [\033[0;34m%s\033[0;m]", proc_key->pid,
114 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);
125 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
127 proc_value_s *parent = NULL;
128 proc_value_s *lookup = value;
132 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
133 lookup = __proc_tree_lookup(&key);
138 if (STC_DEBUG_LOG && STC_STAT_LOG) {
140 STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] "
141 "pid[%s] ppid[%s] cmdline[\033[0;34m%s\033[0;m] name[%s]",
142 parent->status[PROC_STATUS_TGID], parent->status[PROC_STATUS_PID],
143 parent->status[PROC_STATUS_PPID], parent->cmdline,
144 parent->status[PROC_STATUS_NAME]);
150 static void __proc_tree_add(proc_key_s *key, proc_value_s *value)
152 proc_value_s *lookup;
153 proc_value_s *parent;
155 if (proc_tree == NULL) {
156 STC_LOGE("tree is null");
160 if (key == NULL || value == NULL) {
161 if (STC_DEBUG_LOG && STC_STAT_LOG)
162 STC_LOGE("invalid parameters");
166 lookup = g_tree_lookup(proc_tree, key);
170 proc_key_s *proc_key = MALLOC0(proc_key_s, 1);
171 if (proc_key == NULL) {
172 STC_LOGE("memory allocation failed");
176 proc_value_s *proc_value = MALLOC0(proc_value_s, 1);
177 if (proc_value == NULL) {
178 STC_LOGE("memory allocation failed");
183 memcpy(proc_key, key, sizeof(proc_key_s));
184 memcpy(proc_value, value, sizeof(proc_value_s));
186 g_tree_insert(proc_tree, proc_key, proc_value);
189 __proc_tree_printall();
192 parent = __proc_tree_find_parent(proc_value);
194 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
195 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
197 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
198 proc_value->cmdline, proc_value->cmdline, STC_APP_TYPE_SERVICE);
201 static void __proc_tree_remove(const proc_key_s *key,
202 const proc_value_s *value)
204 if (proc_tree == NULL) {
205 STC_LOGE("tree is null");
209 stc_plugin_procfs_status_changed(STC_CMD_SET_TERMINATED, key->pid,
210 value->cmdline, value->cmdline, STC_APP_TYPE_NONE);
212 g_tree_remove(proc_tree, key);
215 __proc_tree_printall();
219 static gboolean __check_excn(char *cmdline)
221 stc_error_e ret = STC_ERROR_NONE;
223 if (cmdline[0] == '(')
226 ret = stc_monitor_check_excn_by_cmdline(cmdline);
227 if (ret == STC_ERROR_UNINITIALIZED ||
228 ret == STC_ERROR_NO_DATA)
234 static void __open_nl_connector_sock(void)
236 __STC_LOG_FUNC_ENTER__;
237 GIOChannel *gio = NULL;
239 if (nl_connector_sock != -1 &&
240 nl_connector_gsource_id != 0) {
241 STC_LOGE("Socket is already open");
242 __STC_LOG_FUNC_EXIT__;
246 if (nl_connector_sock != -1) {
247 close(nl_connector_sock);
248 nl_connector_sock = -1;
251 if (nl_connector_gsource_id != 0) {
252 g_source_remove(nl_connector_gsource_id);
253 nl_connector_gsource_id = 0;
256 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
257 if (nl_connector_sock == -1) {
258 __STC_LOG_FUNC_EXIT__;
262 gio = g_io_channel_unix_new(nl_connector_sock);
263 nl_connector_gsource_id =
264 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
265 (GIOFunc) __process_nl_connector_message,
267 g_io_channel_unref(gio);
268 __STC_LOG_FUNC_EXIT__;
271 static void __close_nl_connector_sock(void)
273 __STC_LOG_FUNC_ENTER__;
274 if (nl_connector_sock != -1) {
275 close(nl_connector_sock);
276 nl_connector_sock = -1;
279 if (nl_connector_gsource_id != 0) {
280 g_source_remove(nl_connector_gsource_id);
281 nl_connector_gsource_id = 0;
283 __STC_LOG_FUNC_EXIT__;
286 static void __reopen_nl_connector_sock(void)
288 __close_nl_connector_sock();
289 __open_nl_connector_sock();
292 static void __process_event_fork(int tgid, int pid)
294 char cmdline[PROC_NAME_MAX] = {0, };
295 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
297 /* TODO: Add newly created thread to the process tasks */
301 memset(status, 0x0, sizeof(status));
303 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
304 STC_ERROR_NONE == proc_get_status(pid, status)) {
306 if (__check_excn(cmdline))
313 memset(&key, 0x0, sizeof(proc_key_s));
314 memset(&value, 0x0, sizeof(proc_value_s));
317 for (i = 0; i < PROC_STATUS_CNT; ++i)
318 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
319 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
321 if (STC_DEBUG_LOG && STC_STAT_LOG)
322 STC_LOGD("\033[1;32mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] "
323 "ppid=[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
324 tgid, status[PROC_STATUS_PPID], cmdline, pid);
326 __proc_tree_add(&key, &value);
330 static void __process_event_exec(int tgid, int pid)
332 char cmdline[PROC_NAME_MAX] = {0, };
333 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
335 /* TODO: Add newly created thread to the process tasks */
339 memset(status, 0x0, sizeof(status));
341 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
342 STC_ERROR_NONE == proc_get_status(pid, status)) {
344 if (__check_excn(cmdline))
351 memset(&key, 0x0, sizeof(proc_key_s));
352 memset(&value, 0x0, sizeof(proc_value_s));
355 for (i = 0; i < PROC_STATUS_CNT; ++i)
356 g_strlcpy(value.status[i], status[i],
357 sizeof(value.status[i]));
358 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
360 if (STC_DEBUG_LOG && STC_STAT_LOG)
361 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] "
362 "ppid[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
363 tgid, status[PROC_STATUS_PPID], cmdline, pid);
365 __proc_tree_add(&key, &value);
369 static void __process_event_exit(int tgid, int pid, int exit_code)
372 proc_value_s *lookup;
378 lookup = __proc_tree_lookup(&key);
379 if (lookup == NULL) /* unmonitored process */
382 if (STC_DEBUG_LOG && STC_STAT_LOG)
383 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
384 "cmdline[\033[0;34m%s\033[0;m] pid[%d] exitcode[%d]",
385 tgid, lookup->cmdline, pid, exit_code);
387 __proc_tree_remove(&key, lookup);
390 static gboolean __process_nl_connector_message(GIOChannel *source,
391 GIOCondition condition,
395 int sock = g_io_channel_unix_get_fd(source);
396 nl_connector_proc_event_s msg;
398 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
399 (condition & G_IO_NVAL)) {
400 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
402 STC_LOGE("Netlink Connector socket received G_IO event, closing"
403 " socket. G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
404 (condition & G_IO_ERR), (condition & G_IO_HUP),
405 (condition & G_IO_NVAL));
406 __reopen_nl_connector_sock();
407 __STC_LOG_FUNC_EXIT__;
411 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
413 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
415 __STC_LOG_FUNC_EXIT__;
419 switch (msg.proc_ev.what) {
420 case PROC_EVENT_FORK:
421 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
422 msg.proc_ev.event_data.fork.child_pid);
424 case PROC_EVENT_EXEC:
425 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
426 msg.proc_ev.event_data.exec.process_pid);
428 case PROC_EVENT_EXIT:
429 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
430 msg.proc_ev.event_data.exit.process_pid,
431 msg.proc_ev.event_data.exit.exit_code);
440 static int __subscribe_proc_events(void)
442 __STC_LOG_FUNC_ENTER__;
443 nl_connector_msg_s msg;
445 int sock = nl_connector_sock;
448 __STC_LOG_FUNC_EXIT__;
452 memset(&msg, 0, sizeof(nl_connector_msg_s));
454 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
455 msg.nl_hdr.nlmsg_pid = getpid();
456 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
458 msg.cn_msg.id.idx = CN_IDX_PROC;
459 msg.cn_msg.id.val = CN_VAL_PROC;
460 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
462 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
464 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
466 STC_LOGE("Error sending netlink connector message");
467 __STC_LOG_FUNC_EXIT__;
471 __STC_LOG_FUNC_EXIT__;
475 static int __unsubscribe_proc_events(void)
477 __STC_LOG_FUNC_ENTER__;
478 nl_connector_msg_s msg;
480 int sock = nl_connector_sock;
483 __STC_LOG_FUNC_EXIT__;
487 memset(&msg, 0, sizeof(nl_connector_msg_s));
489 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
490 msg.nl_hdr.nlmsg_pid = getpid();
491 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
493 msg.cn_msg.id.idx = CN_IDX_PROC;
494 msg.cn_msg.id.val = CN_VAL_PROC;
495 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
497 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
499 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
501 STC_LOGE("Error sending netlink connector message");
502 __STC_LOG_FUNC_EXIT__;
506 __STC_LOG_FUNC_EXIT__;
510 static bool __process_pid_cb(pid_t pid, void *user_data)
512 char cmdline[PROC_NAME_MAX] = {0, };
513 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
515 memset(status, 0x0, sizeof(status));
517 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
518 STC_ERROR_NONE == proc_get_status(pid, status)) {
520 if (__check_excn(cmdline))
527 memset(&key, 0x0, sizeof(proc_key_s));
528 memset(&value, 0x0, sizeof(proc_value_s));
531 for (i = 0; i < PROC_STATUS_CNT; ++i)
532 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
533 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
535 __proc_tree_add(&key, &value);
541 int stc_plugin_procfs_initialize(void)
543 __STC_LOG_FUNC_ENTER__;
545 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
546 __proc_tree_key_free,
547 __proc_tree_value_free);
549 /* TODO: Fill proc tree with current procfs state */
551 __open_nl_connector_sock();
552 __subscribe_proc_events();
553 __STC_LOG_FUNC_EXIT__;
554 return STC_ERROR_NONE;
557 int stc_plugin_procfs_deinitialize(void)
559 __STC_LOG_FUNC_ENTER__;
561 if (nl_connector_sock == -1) {
562 STC_LOGD("socket already closed");
563 return STC_ERROR_NONE;
566 __unsubscribe_proc_events();
567 __close_nl_connector_sock();
569 g_tree_destroy(proc_tree);
572 __STC_LOG_FUNC_EXIT__;
573 return STC_ERROR_NONE;
576 stc_error_e stc_plugin_procfs_load(void)
578 __STC_LOG_FUNC_ENTER__;
580 proc_foreach_pid(__process_pid_cb, NULL);
582 __STC_LOG_FUNC_EXIT__;
583 return STC_ERROR_NONE;
586 stc_error_e stc_plugin_procfs_status_changed(stc_cmd_type_e cmd,
590 stc_app_type_e app_type)
592 stc_error_e ret = STC_ERROR_NONE;
594 if ((pkg_id && app_id) && STC_DEBUG_LOG && STC_STAT_LOG)
595 STC_LOGD("cmd[%d] pkgid[%s] appid[%s] pid[%d] type[%d]",
596 cmd, pkg_id, app_id, pid, app_type);
599 case STC_CMD_SET_FOREGRD:
604 stc_app_value_s app_value;
605 stc_proc_value_s proc_value;
607 memset(&app_value, 0, sizeof(stc_app_value_s));
608 memset(&proc_value, 0, sizeof(stc_proc_value_s));
610 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
612 app_value.type = app_type;
613 app_value.state = STC_APP_STATE_FOREGROUND;
614 app_value.processes = NULL;
616 proc_value.pid = pid;
617 proc_value.ground = STC_APP_STATE_FOREGROUND;
619 bg_classid = get_classid_by_app_id(bg_app_id, FALSE);
620 fg_classid = get_classid_by_app_id(app_id, TRUE);
622 stc_monitor_app_add(fg_classid, app_id, pkg_id, app_value);
624 stc_monitor_proc_move(bg_classid, fg_classid);
626 stc_monitor_proc_add(fg_classid, app_id, proc_value);
627 stc_monitor_proc_update_ground(fg_classid, app_id, proc_value);
632 case STC_CMD_SET_BACKGRD:
637 stc_app_value_s app_value;
638 stc_proc_value_s proc_value;
640 memset(&app_value, 0, sizeof(stc_app_value_s));
641 memset(&proc_value, 0, sizeof(stc_proc_value_s));
643 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
645 app_value.type = app_type;
646 app_value.state = STC_APP_STATE_BACKGROUND;
647 app_value.processes = NULL;
649 proc_value.pid = pid;
650 proc_value.ground = STC_APP_STATE_BACKGROUND;
652 fg_classid = get_classid_by_app_id(app_id, FALSE);
653 bg_classid = get_classid_by_app_id(bg_app_id, TRUE);
655 stc_monitor_app_add(bg_classid, bg_app_id, pkg_id, app_value);
657 stc_monitor_proc_move(fg_classid, bg_classid);
659 stc_monitor_proc_add(bg_classid, bg_app_id, proc_value);
660 stc_monitor_proc_update_ground(bg_classid, bg_app_id, proc_value);
665 case STC_CMD_SET_SERVICE_LAUNCHED:
669 stc_app_value_s app_value;
670 stc_proc_value_s proc_value;
673 memset(&app_value, 0, sizeof(stc_app_value_s));
674 memset(&proc_value, 0, sizeof(stc_proc_value_s));
676 classid = get_classid_by_app_id(app_id, FALSE);
677 is_exist = stc_monitor_app_lookup(classid);
679 app_value.type = app_type;
680 app_value.state = STC_APP_STATE_FOREGROUND;
681 app_value.processes = NULL;
683 proc_value.pid = pid;
684 proc_value.ground = STC_APP_STATE_FOREGROUND;
686 stc_monitor_app_add(classid, app_id, pkg_id, app_value);
687 stc_monitor_proc_add(classid, app_id, proc_value);
689 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
690 classid = get_classid_by_app_id(bg_app_id, TRUE);
692 app_value.type = app_type;
693 app_value.state = STC_APP_STATE_BACKGROUND;
694 app_value.processes = NULL;
696 proc_value.pid = pid;
697 proc_value.ground = STC_APP_STATE_BACKGROUND;
699 stc_monitor_app_add(classid, bg_app_id, pkg_id, app_value);
700 stc_monitor_proc_add(classid, bg_app_id, proc_value);
706 case STC_CMD_SET_TERMINATED:
711 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
712 classid = get_classid_by_app_id(bg_app_id, FALSE);
714 if (classid == STC_UNKNOWN_CLASSID)
715 classid = get_classid_by_app_id(app_id, FALSE);
717 stc_monitor_proc_remove(classid, pid);
723 STC_LOGE("Unhandled command");
724 ret = STC_ERROR_INVALID_PARAMETER;
730 API stc_plugin_procfs_s stc_plugin_procfs = {
732 stc_plugin_procfs_initialize,
733 .deinitialize_plugin =
734 stc_plugin_procfs_deinitialize,
736 stc_plugin_procfs_load,
737 .procfs_status_changed =
738 stc_plugin_procfs_status_changed