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-iface-procfs.h"
31 #include "stc-helper-net-cls.h"
32 #include "stc-helper-procfs.h"
33 #include "stc-helper-nl.h"
34 #include "stc-plugin-monitor.h"
35 #include "stc-plugin-exception.h"
43 char cmdline[PROC_NAME_MAX];
44 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
47 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
48 struct nlmsghdr nl_hdr;
49 struct __attribute__ ((__packed__)) {
51 enum proc_cn_mcast_op cn_mcast;
55 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
56 struct nlmsghdr nl_hdr;
57 struct __attribute__ ((__packed__)) {
59 struct proc_event proc_ev;
61 } nl_connector_proc_event_s;
63 static int nl_connector_sock = -1;
64 static guint nl_connector_gsource_id = 0;
65 static GTree *proc_tree;
67 static gboolean __process_nl_connector_message(GIOChannel *source,
68 GIOCondition condition,
71 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
72 gpointer UNUSED user_data)
74 proc_key_s *key_a = (proc_key_s *)a;
75 proc_key_s *key_b = (proc_key_s *)b;
77 return key_a->pid - key_b->pid;
80 static void __proc_tree_value_free(gpointer data)
82 proc_value_s *value = (proc_value_s *)data;
87 static void __proc_tree_key_free(gpointer data)
89 proc_key_s *key = (proc_key_s *)data;
94 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
98 if (proc_tree == NULL) {
99 STC_LOGE("tree is null");
103 lookup = g_tree_lookup(proc_tree, key);
108 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
111 proc_key_s *proc_key = (proc_key_s *)key;
112 proc_value_s *proc_value = (proc_value_s *)value;
114 STC_LOGD("Proc pid [\033[1;33m%d\033[0;m] ppid [%s] "
115 "cmdline [\033[0;34m%s\033[0;m]", proc_key->pid,
116 proc_value->status[PROC_STATUS_PPID], proc_value->cmdline);
121 static void __proc_tree_printall(void)
123 g_tree_foreach(proc_tree, __proc_tree_foreach_print, NULL);
127 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
129 proc_value_s *parent = NULL;
130 proc_value_s *lookup = value;
134 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
135 lookup = __proc_tree_lookup(&key);
142 STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] "
143 "pid[%s] ppid[%s] cmdline[\033[0;34m%s\033[0;m] name[%s]",
144 parent->status[PROC_STATUS_TGID], parent->status[PROC_STATUS_PID],
145 parent->status[PROC_STATUS_PPID], parent->cmdline,
146 parent->status[PROC_STATUS_NAME]);
152 stc_error_e stc_plugin_procfs_status_changed(stc_cmd_type_e cmd,
156 stc_app_type_e app_type)
158 stc_error_e ret = STC_ERROR_NONE;
160 if ((pkg_id && app_id) && STC_STAT_LOG)
161 STC_LOGD("cmd[%d] pkgid[%s] appid[%s] pid[%d] type[%d]",
162 cmd, pkg_id, app_id, pid, app_type);
165 case STC_CMD_SET_FOREGRD:
167 uint32_t fg_classid = STC_UNKNOWN_CLASSID;
168 uint32_t bg_classid = STC_UNKNOWN_CLASSID;
169 char *bg_app_id = NULL;
170 stc_app_value_s app_value;
171 stc_proc_value_s proc_value;
173 memset(&app_value, 0, sizeof(stc_app_value_s));
174 memset(&proc_value, 0, sizeof(stc_proc_value_s));
176 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
178 app_value.type = app_type;
179 app_value.state = STC_APP_STATE_FOREGROUND;
180 app_value.processes = NULL;
182 proc_value.pid = pid;
183 proc_value.ground = STC_APP_STATE_FOREGROUND;
185 bg_classid = get_classid_by_app_id(bg_app_id, FALSE);
186 fg_classid = get_classid_by_app_id(app_id, TRUE);
188 stc_plugin_monitor_add_app(fg_classid, app_id, pkg_id, app_value);
190 stc_plugin_monitor_move_proc(bg_classid, fg_classid);
192 stc_plugin_monitor_add_proc(fg_classid, app_id, proc_value);
193 stc_plugin_monitor_update_proc_ground(fg_classid, app_id, proc_value);
198 case STC_CMD_SET_BACKGRD:
200 uint32_t bg_classid = STC_UNKNOWN_CLASSID;
201 uint32_t fg_classid = STC_UNKNOWN_CLASSID;
202 char *bg_app_id = NULL;
203 stc_app_value_s app_value;
204 stc_proc_value_s proc_value;
206 memset(&app_value, 0, sizeof(stc_app_value_s));
207 memset(&proc_value, 0, sizeof(stc_proc_value_s));
209 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
211 app_value.type = app_type;
212 app_value.state = STC_APP_STATE_BACKGROUND;
213 app_value.processes = NULL;
215 proc_value.pid = pid;
216 proc_value.ground = STC_APP_STATE_BACKGROUND;
218 fg_classid = get_classid_by_app_id(app_id, FALSE);
219 bg_classid = get_classid_by_app_id(bg_app_id, TRUE);
221 stc_plugin_monitor_add_app(bg_classid, bg_app_id, pkg_id, app_value);
223 stc_plugin_monitor_move_proc(fg_classid, bg_classid);
225 stc_plugin_monitor_add_proc(bg_classid, bg_app_id, proc_value);
226 stc_plugin_monitor_update_proc_ground(bg_classid, bg_app_id, proc_value);
231 case STC_CMD_SET_APP_LAUNCHED:
233 uint32_t classid = STC_UNKNOWN_CLASSID;
234 stc_app_value_s app_value;
235 stc_proc_value_s proc_value;
237 memset(&app_value, 0, sizeof(stc_app_value_s));
238 memset(&proc_value, 0, sizeof(stc_proc_value_s));
240 classid = get_classid_by_app_id(app_id, FALSE);
242 app_value.type = app_type;
243 app_value.state = STC_APP_STATE_FOREGROUND;
244 app_value.processes = NULL;
246 proc_value.pid = pid;
247 proc_value.ground = STC_APP_STATE_FOREGROUND;
249 stc_plugin_monitor_add_app(classid, app_id, pkg_id, app_value);
250 stc_plugin_monitor_add_proc(classid, app_id, proc_value);
254 case STC_CMD_SET_SERVICE_LAUNCHED:
256 uint32_t classid = STC_UNKNOWN_CLASSID;
257 char *bg_app_id = NULL;
258 stc_app_value_s app_value;
259 stc_proc_value_s proc_value;
262 memset(&app_value, 0, sizeof(stc_app_value_s));
263 memset(&proc_value, 0, sizeof(stc_proc_value_s));
265 classid = get_classid_by_app_id(app_id, FALSE);
266 is_exist = stc_plugin_monitor_lookup_app(classid);
268 app_value.type = app_type;
269 app_value.state = STC_APP_STATE_FOREGROUND;
270 app_value.processes = NULL;
272 proc_value.pid = pid;
273 proc_value.ground = STC_APP_STATE_FOREGROUND;
275 stc_plugin_monitor_add_app(classid, app_id, pkg_id, app_value);
276 stc_plugin_monitor_add_proc(classid, app_id, proc_value);
278 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
279 classid = get_classid_by_app_id(bg_app_id, TRUE);
281 app_value.type = app_type;
282 app_value.state = STC_APP_STATE_BACKGROUND;
283 app_value.processes = NULL;
285 proc_value.pid = pid;
286 proc_value.ground = STC_APP_STATE_BACKGROUND;
288 stc_plugin_monitor_add_app(classid, bg_app_id, pkg_id, app_value);
289 stc_plugin_monitor_add_proc(classid, bg_app_id, proc_value);
295 case STC_CMD_SET_TERMINATED:
297 uint32_t classid = STC_UNKNOWN_CLASSID;
298 char *bg_app_id = NULL;
300 if (app_type == STC_APP_TYPE_NONE) {
301 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
302 classid = get_classid_by_app_id(bg_app_id, FALSE);
305 if (classid == STC_UNKNOWN_CLASSID)
306 classid = get_classid_by_app_id(bg_app_id, FALSE);
308 stc_plugin_monitor_remove_proc(classid, pid);
314 STC_LOGE("Unhandled command");
315 ret = STC_ERROR_INVALID_PARAMETER;
321 static void __proc_tree_add(proc_key_s *key, proc_value_s *value)
323 proc_value_s *lookup;
324 proc_value_s *parent;
326 if (proc_tree == NULL) {
327 STC_LOGE("tree is null");
331 if (key == NULL || value == NULL) {
333 STC_LOGE("invalid parameters");
337 lookup = g_tree_lookup(proc_tree, key);
341 proc_key_s *proc_key = MALLOC0(proc_key_s, 1);
342 if (proc_key == NULL) {
343 STC_LOGE("memory allocation failed");
347 proc_value_s *proc_value = MALLOC0(proc_value_s, 1);
348 if (proc_value == NULL) {
349 STC_LOGE("memory allocation failed");
354 memcpy(proc_key, key, sizeof(proc_key_s));
355 memcpy(proc_value, value, sizeof(proc_value_s));
357 g_tree_insert(proc_tree, proc_key, proc_value);
360 __proc_tree_printall();
363 parent = __proc_tree_find_parent(proc_value);
365 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
366 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
368 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
369 proc_value->cmdline, proc_value->cmdline, STC_APP_TYPE_SERVICE);
372 static void __proc_tree_remove(const proc_key_s *key,
373 const proc_value_s *value)
375 if (proc_tree == NULL) {
376 STC_LOGE("tree is null");
380 stc_plugin_procfs_status_changed(STC_CMD_SET_TERMINATED, key->pid,
381 value->cmdline, value->cmdline, STC_APP_TYPE_NONE);
383 g_tree_remove(proc_tree, key);
386 __proc_tree_printall();
390 static gboolean __check_excn(char *cmdline)
392 stc_error_e ret = STC_ERROR_NONE;
394 if (cmdline[0] == '(')
397 ret = stc_plugin_check_exception_by_cmdline(cmdline);
398 if (ret == STC_ERROR_UNINITIALIZED ||
399 ret == STC_ERROR_NO_DATA)
405 static void __open_nl_connector_sock(void)
407 __STC_LOG_FUNC_ENTER__;
408 GIOChannel *gio = NULL;
410 if (nl_connector_sock != -1 &&
411 nl_connector_gsource_id != 0) {
412 STC_LOGE("Socket is already open");
413 __STC_LOG_FUNC_EXIT__;
417 if (nl_connector_sock != -1) {
418 close(nl_connector_sock);
419 nl_connector_sock = -1;
422 if (nl_connector_gsource_id != 0) {
423 g_source_remove(nl_connector_gsource_id);
424 nl_connector_gsource_id = 0;
427 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
428 if (nl_connector_sock == -1) {
429 __STC_LOG_FUNC_EXIT__;
433 gio = g_io_channel_unix_new(nl_connector_sock);
434 nl_connector_gsource_id =
435 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
436 (GIOFunc) __process_nl_connector_message,
438 g_io_channel_unref(gio);
439 __STC_LOG_FUNC_EXIT__;
442 static void __close_nl_connector_sock(void)
444 __STC_LOG_FUNC_ENTER__;
445 if (nl_connector_sock != -1) {
446 close(nl_connector_sock);
447 nl_connector_sock = -1;
450 if (nl_connector_gsource_id != 0) {
451 g_source_remove(nl_connector_gsource_id);
452 nl_connector_gsource_id = 0;
454 __STC_LOG_FUNC_EXIT__;
457 static void __reopen_nl_connector_sock(void)
459 __close_nl_connector_sock();
460 __open_nl_connector_sock();
463 static void __process_event_fork(int tgid, int pid)
465 char cmdline[PROC_NAME_MAX] = {0, };
466 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
468 /* TODO: Add newly created thread to the process tasks */
472 memset(status, 0x0, sizeof(status));
474 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
475 STC_ERROR_NONE == proc_get_status(pid, status)) {
477 if (__check_excn(cmdline))
484 memset(&key, 0x0, sizeof(proc_key_s));
485 memset(&value, 0x0, sizeof(proc_value_s));
488 for (i = 0; i < PROC_STATUS_CNT; ++i)
489 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
490 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
493 STC_LOGD("\033[1;32mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] "
494 "ppid[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
495 tgid, status[PROC_STATUS_PPID], cmdline, pid);
497 __proc_tree_add(&key, &value);
501 static void __process_event_exec(int tgid, int pid)
503 char cmdline[PROC_NAME_MAX] = {0, };
504 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
506 /* TODO: Add newly created thread to the process tasks */
510 memset(status, 0x0, sizeof(status));
512 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
513 STC_ERROR_NONE == proc_get_status(pid, status)) {
515 if (__check_excn(cmdline))
522 memset(&key, 0x0, sizeof(proc_key_s));
523 memset(&value, 0x0, sizeof(proc_value_s));
526 for (i = 0; i < PROC_STATUS_CNT; ++i)
527 g_strlcpy(value.status[i], status[i],
528 sizeof(value.status[i]));
529 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
532 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] "
533 "ppid[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
534 tgid, status[PROC_STATUS_PPID], cmdline, pid);
536 __proc_tree_add(&key, &value);
540 static void __process_event_exit(int tgid, int pid, int exit_code)
543 proc_value_s *lookup;
549 lookup = __proc_tree_lookup(&key);
550 if (lookup == NULL) /* unmonitored process */
554 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
555 "cmdline[\033[0;34m%s\033[0;m] pid[%d] exitcode[%d]",
556 tgid, lookup->cmdline, pid, exit_code);
558 __proc_tree_remove(&key, lookup);
561 static gboolean __process_nl_connector_message(GIOChannel *source,
562 GIOCondition condition,
566 int sock = g_io_channel_unix_get_fd(source);
567 nl_connector_proc_event_s msg;
569 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
570 (condition & G_IO_NVAL)) {
571 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
573 STC_LOGE("Netlink Connector socket received G_IO event, closing"
574 " socket. G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
575 (condition & G_IO_ERR), (condition & G_IO_HUP),
576 (condition & G_IO_NVAL));
577 __reopen_nl_connector_sock();
578 __STC_LOG_FUNC_EXIT__;
582 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
584 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
586 __STC_LOG_FUNC_EXIT__;
590 switch (msg.proc_ev.what) {
591 case PROC_EVENT_FORK:
592 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
593 msg.proc_ev.event_data.fork.child_pid);
595 case PROC_EVENT_EXEC:
596 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
597 msg.proc_ev.event_data.exec.process_pid);
599 case PROC_EVENT_EXIT:
600 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
601 msg.proc_ev.event_data.exit.process_pid,
602 msg.proc_ev.event_data.exit.exit_code);
611 static int __subscribe_proc_events(void)
613 __STC_LOG_FUNC_ENTER__;
614 nl_connector_msg_s msg;
616 int sock = nl_connector_sock;
619 __STC_LOG_FUNC_EXIT__;
620 return STC_ERROR_INVALID_PARAMETER;
623 memset(&msg, 0, sizeof(nl_connector_msg_s));
625 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
626 msg.nl_hdr.nlmsg_pid = getpid();
627 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
629 msg.cn_msg.id.idx = CN_IDX_PROC;
630 msg.cn_msg.id.val = CN_VAL_PROC;
631 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
633 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
635 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
637 STC_LOGE("Error sending netlink connector message");
638 __STC_LOG_FUNC_EXIT__;
639 return STC_ERROR_FAIL;
642 __STC_LOG_FUNC_EXIT__;
643 return STC_ERROR_NONE;
646 static int __unsubscribe_proc_events(void)
648 __STC_LOG_FUNC_ENTER__;
649 nl_connector_msg_s msg;
651 int sock = nl_connector_sock;
654 __STC_LOG_FUNC_EXIT__;
655 return STC_ERROR_INVALID_PARAMETER;
658 memset(&msg, 0, sizeof(nl_connector_msg_s));
660 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
661 msg.nl_hdr.nlmsg_pid = getpid();
662 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
664 msg.cn_msg.id.idx = CN_IDX_PROC;
665 msg.cn_msg.id.val = CN_VAL_PROC;
666 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
668 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
670 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
672 STC_LOGE("Error sending netlink connector message");
673 __STC_LOG_FUNC_EXIT__;
674 return STC_ERROR_FAIL;
677 __STC_LOG_FUNC_EXIT__;
678 return STC_ERROR_NONE;
681 static bool __process_pid_cb(pid_t pid, void *user_data)
683 char cmdline[PROC_NAME_MAX] = {0, };
684 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
686 memset(status, 0x0, sizeof(status));
688 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
689 STC_ERROR_NONE == proc_get_status(pid, status)) {
691 if (__check_excn(cmdline))
698 memset(&key, 0x0, sizeof(proc_key_s));
699 memset(&value, 0x0, sizeof(proc_value_s));
702 for (i = 0; i < PROC_STATUS_CNT; ++i)
703 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
704 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
706 __proc_tree_add(&key, &value);
712 int stc_plugin_procfs_initialize(void)
714 __STC_LOG_FUNC_ENTER__;
715 int ret = STC_ERROR_NONE;
717 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
718 __proc_tree_key_free,
719 __proc_tree_value_free);
721 /* TODO: Fill proc tree with current procfs state */
723 __open_nl_connector_sock();
724 ret = __subscribe_proc_events();
726 __STC_LOG_FUNC_EXIT__;
730 int stc_plugin_procfs_deinitialize(void)
732 __STC_LOG_FUNC_ENTER__;
733 int ret = STC_ERROR_NONE;
735 if (nl_connector_sock == -1) {
736 STC_LOGD("socket already closed");
737 return STC_ERROR_NONE;
740 ret = __unsubscribe_proc_events();
741 __close_nl_connector_sock();
743 g_tree_destroy(proc_tree);
746 __STC_LOG_FUNC_EXIT__;
750 stc_error_e stc_plugin_procfs_load(void)
752 __STC_LOG_FUNC_ENTER__;
754 proc_foreach_pid(__process_pid_cb, NULL);
756 __STC_LOG_FUNC_EXIT__;
757 return STC_ERROR_NONE;
760 API stc_plugin_procfs_s stc_plugin_procfs = {
762 stc_plugin_procfs_initialize,
763 .deinitialize_plugin =
764 stc_plugin_procfs_deinitialize,
766 stc_plugin_procfs_load,
767 .procfs_status_changed =
768 stc_plugin_procfs_status_changed