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 "helper-net-cls.h"
32 #include "helper-procfs.h"
33 #include "stc-manager-plugin-monitor.h"
34 #include "stc-manager-plugin-exception.h"
42 char cmdline[PROC_NAME_MAX];
43 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
46 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
47 struct nlmsghdr nl_hdr;
48 struct __attribute__ ((__packed__)) {
50 enum proc_cn_mcast_op cn_mcast;
54 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
55 struct nlmsghdr nl_hdr;
56 struct __attribute__ ((__packed__)) {
58 struct proc_event proc_ev;
60 } nl_connector_proc_event_s;
62 static int nl_connector_sock = -1;
63 static guint nl_connector_gsource_id = 0;
64 static GTree *proc_tree;
66 static gboolean __process_nl_connector_message(GIOChannel *source,
67 GIOCondition condition,
70 static int __proc_tree_key_compare(gconstpointer a, gconstpointer b,
71 gpointer UNUSED user_data)
73 proc_key_s *key_a = (proc_key_s *)a;
74 proc_key_s *key_b = (proc_key_s *)b;
76 return key_a->pid - key_b->pid;
79 static void __proc_tree_value_free(gpointer data)
81 proc_value_s *value = (proc_value_s *)data;
86 static void __proc_tree_key_free(gpointer data)
88 proc_key_s *key = (proc_key_s *)data;
93 static proc_value_s * __proc_tree_lookup(const proc_key_s *key)
97 if (proc_tree == NULL) {
98 STC_LOGE("tree is null");
102 lookup = g_tree_lookup(proc_tree, key);
107 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
110 proc_key_s *proc_key = (proc_key_s *)key;
111 proc_value_s *proc_value = (proc_value_s *)value;
113 STC_LOGD("Proc pid [\033[1;33m%d\033[0;m] ppid [%s] "
114 "cmdline [\033[0;34m%s\033[0;m]", proc_key->pid,
115 proc_value->status[PROC_STATUS_PPID], proc_value->cmdline);
120 static void __proc_tree_printall(void)
122 g_tree_foreach(proc_tree, __proc_tree_foreach_print, NULL);
126 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
128 proc_value_s *parent = NULL;
129 proc_value_s *lookup = value;
133 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
134 lookup = __proc_tree_lookup(&key);
141 STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] "
142 "pid[%s] ppid[%s] cmdline[\033[0;34m%s\033[0;m] name[%s]",
143 parent->status[PROC_STATUS_TGID], parent->status[PROC_STATUS_PID],
144 parent->status[PROC_STATUS_PPID], parent->cmdline,
145 parent->status[PROC_STATUS_NAME]);
151 static void __proc_tree_add(proc_key_s *key, proc_value_s *value)
153 proc_value_s *lookup;
154 proc_value_s *parent;
156 if (proc_tree == NULL) {
157 STC_LOGE("tree is null");
161 if (key == NULL || value == NULL) {
163 STC_LOGE("invalid parameters");
167 lookup = g_tree_lookup(proc_tree, key);
171 proc_key_s *proc_key = MALLOC0(proc_key_s, 1);
172 if (proc_key == NULL) {
173 STC_LOGE("memory allocation failed");
177 proc_value_s *proc_value = MALLOC0(proc_value_s, 1);
178 if (proc_value == NULL) {
179 STC_LOGE("memory allocation failed");
184 memcpy(proc_key, key, sizeof(proc_key_s));
185 memcpy(proc_value, value, sizeof(proc_value_s));
187 g_tree_insert(proc_tree, proc_key, proc_value);
190 __proc_tree_printall();
193 parent = __proc_tree_find_parent(proc_value);
195 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
196 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
198 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
199 proc_value->cmdline, proc_value->cmdline, STC_APP_TYPE_SERVICE);
202 static void __proc_tree_remove(const proc_key_s *key,
203 const proc_value_s *value)
205 if (proc_tree == NULL) {
206 STC_LOGE("tree is null");
210 stc_plugin_procfs_status_changed(STC_CMD_SET_TERMINATED, key->pid,
211 value->cmdline, value->cmdline, STC_APP_TYPE_NONE);
213 g_tree_remove(proc_tree, key);
216 __proc_tree_printall();
220 static gboolean __check_excn(char *cmdline)
222 stc_error_e ret = STC_ERROR_NONE;
224 if (cmdline[0] == '(')
227 ret = stc_plugin_check_exception_by_cmdline(cmdline);
228 if (ret == STC_ERROR_UNINITIALIZED ||
229 ret == STC_ERROR_NO_DATA)
235 static void __open_nl_connector_sock(void)
237 __STC_LOG_FUNC_ENTER__;
238 GIOChannel *gio = NULL;
240 if (nl_connector_sock != -1 &&
241 nl_connector_gsource_id != 0) {
242 STC_LOGE("Socket is already open");
243 __STC_LOG_FUNC_EXIT__;
247 if (nl_connector_sock != -1) {
248 close(nl_connector_sock);
249 nl_connector_sock = -1;
252 if (nl_connector_gsource_id != 0) {
253 g_source_remove(nl_connector_gsource_id);
254 nl_connector_gsource_id = 0;
257 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
258 if (nl_connector_sock == -1) {
259 __STC_LOG_FUNC_EXIT__;
263 gio = g_io_channel_unix_new(nl_connector_sock);
264 nl_connector_gsource_id =
265 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
266 (GIOFunc) __process_nl_connector_message,
268 g_io_channel_unref(gio);
269 __STC_LOG_FUNC_EXIT__;
272 static void __close_nl_connector_sock(void)
274 __STC_LOG_FUNC_ENTER__;
275 if (nl_connector_sock != -1) {
276 close(nl_connector_sock);
277 nl_connector_sock = -1;
280 if (nl_connector_gsource_id != 0) {
281 g_source_remove(nl_connector_gsource_id);
282 nl_connector_gsource_id = 0;
284 __STC_LOG_FUNC_EXIT__;
287 static void __reopen_nl_connector_sock(void)
289 __close_nl_connector_sock();
290 __open_nl_connector_sock();
293 static void __process_event_fork(int tgid, int pid)
295 char cmdline[PROC_NAME_MAX] = {0, };
296 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
298 /* TODO: Add newly created thread to the process tasks */
302 memset(status, 0x0, sizeof(status));
304 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
305 STC_ERROR_NONE == proc_get_status(pid, status)) {
307 if (__check_excn(cmdline))
314 memset(&key, 0x0, sizeof(proc_key_s));
315 memset(&value, 0x0, sizeof(proc_value_s));
318 for (i = 0; i < PROC_STATUS_CNT; ++i)
319 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
320 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
323 STC_LOGD("\033[1;32mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] "
324 "ppid=[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
325 tgid, status[PROC_STATUS_PPID], cmdline, pid);
327 __proc_tree_add(&key, &value);
331 static void __process_event_exec(int tgid, int pid)
333 char cmdline[PROC_NAME_MAX] = {0, };
334 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
336 /* TODO: Add newly created thread to the process tasks */
340 memset(status, 0x0, sizeof(status));
342 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
343 STC_ERROR_NONE == proc_get_status(pid, status)) {
345 if (__check_excn(cmdline))
352 memset(&key, 0x0, sizeof(proc_key_s));
353 memset(&value, 0x0, sizeof(proc_value_s));
356 for (i = 0; i < PROC_STATUS_CNT; ++i)
357 g_strlcpy(value.status[i], status[i],
358 sizeof(value.status[i]));
359 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
362 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] "
363 "ppid[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
364 tgid, status[PROC_STATUS_PPID], cmdline, pid);
366 __proc_tree_add(&key, &value);
370 static void __process_event_exit(int tgid, int pid, int exit_code)
373 proc_value_s *lookup;
379 lookup = __proc_tree_lookup(&key);
380 if (lookup == NULL) /* unmonitored process */
384 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
385 "cmdline[\033[0;34m%s\033[0;m] pid[%d] exitcode[%d]",
386 tgid, lookup->cmdline, pid, exit_code);
388 __proc_tree_remove(&key, lookup);
391 static gboolean __process_nl_connector_message(GIOChannel *source,
392 GIOCondition condition,
396 int sock = g_io_channel_unix_get_fd(source);
397 nl_connector_proc_event_s msg;
399 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
400 (condition & G_IO_NVAL)) {
401 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
403 STC_LOGE("Netlink Connector socket received G_IO event, closing"
404 " socket. G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
405 (condition & G_IO_ERR), (condition & G_IO_HUP),
406 (condition & G_IO_NVAL));
407 __reopen_nl_connector_sock();
408 __STC_LOG_FUNC_EXIT__;
412 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
414 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
416 __STC_LOG_FUNC_EXIT__;
420 switch (msg.proc_ev.what) {
421 case PROC_EVENT_FORK:
422 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
423 msg.proc_ev.event_data.fork.child_pid);
425 case PROC_EVENT_EXEC:
426 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
427 msg.proc_ev.event_data.exec.process_pid);
429 case PROC_EVENT_EXIT:
430 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
431 msg.proc_ev.event_data.exit.process_pid,
432 msg.proc_ev.event_data.exit.exit_code);
441 static int __subscribe_proc_events(void)
443 __STC_LOG_FUNC_ENTER__;
444 nl_connector_msg_s msg;
446 int sock = nl_connector_sock;
449 __STC_LOG_FUNC_EXIT__;
450 return STC_ERROR_INVALID_PARAMETER;
453 memset(&msg, 0, sizeof(nl_connector_msg_s));
455 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
456 msg.nl_hdr.nlmsg_pid = getpid();
457 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
459 msg.cn_msg.id.idx = CN_IDX_PROC;
460 msg.cn_msg.id.val = CN_VAL_PROC;
461 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
463 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
465 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
467 STC_LOGE("Error sending netlink connector message");
468 __STC_LOG_FUNC_EXIT__;
469 return STC_ERROR_FAIL;
472 __STC_LOG_FUNC_EXIT__;
473 return STC_ERROR_NONE;
476 static int __unsubscribe_proc_events(void)
478 __STC_LOG_FUNC_ENTER__;
479 nl_connector_msg_s msg;
481 int sock = nl_connector_sock;
484 __STC_LOG_FUNC_EXIT__;
485 return STC_ERROR_INVALID_PARAMETER;
488 memset(&msg, 0, sizeof(nl_connector_msg_s));
490 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
491 msg.nl_hdr.nlmsg_pid = getpid();
492 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
494 msg.cn_msg.id.idx = CN_IDX_PROC;
495 msg.cn_msg.id.val = CN_VAL_PROC;
496 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
498 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
500 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
502 STC_LOGE("Error sending netlink connector message");
503 __STC_LOG_FUNC_EXIT__;
504 return STC_ERROR_FAIL;
507 __STC_LOG_FUNC_EXIT__;
508 return STC_ERROR_NONE;
511 static bool __process_pid_cb(pid_t pid, void *user_data)
513 char cmdline[PROC_NAME_MAX] = {0, };
514 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
516 memset(status, 0x0, sizeof(status));
518 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
519 STC_ERROR_NONE == proc_get_status(pid, status)) {
521 if (__check_excn(cmdline))
528 memset(&key, 0x0, sizeof(proc_key_s));
529 memset(&value, 0x0, sizeof(proc_value_s));
532 for (i = 0; i < PROC_STATUS_CNT; ++i)
533 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
534 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
536 __proc_tree_add(&key, &value);
542 int stc_plugin_procfs_initialize(void)
544 __STC_LOG_FUNC_ENTER__;
545 int ret = STC_ERROR_NONE;
547 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
548 __proc_tree_key_free,
549 __proc_tree_value_free);
551 /* TODO: Fill proc tree with current procfs state */
553 __open_nl_connector_sock();
554 ret = __subscribe_proc_events();
556 __STC_LOG_FUNC_EXIT__;
560 int stc_plugin_procfs_deinitialize(void)
562 __STC_LOG_FUNC_ENTER__;
563 int ret = STC_ERROR_NONE;
565 if (nl_connector_sock == -1) {
566 STC_LOGD("socket already closed");
567 return STC_ERROR_NONE;
570 ret = __unsubscribe_proc_events();
571 __close_nl_connector_sock();
573 g_tree_destroy(proc_tree);
576 __STC_LOG_FUNC_EXIT__;
580 stc_error_e stc_plugin_procfs_load(void)
582 __STC_LOG_FUNC_ENTER__;
584 proc_foreach_pid(__process_pid_cb, NULL);
586 __STC_LOG_FUNC_EXIT__;
587 return STC_ERROR_NONE;
590 stc_error_e stc_plugin_procfs_status_changed(stc_cmd_type_e cmd,
594 stc_app_type_e app_type)
596 stc_error_e ret = STC_ERROR_NONE;
598 if ((pkg_id && app_id) && STC_STAT_LOG)
599 STC_LOGD("cmd[%d] pkgid[%s] appid[%s] pid[%d] type[%d]",
600 cmd, pkg_id, app_id, pid, app_type);
603 case STC_CMD_SET_FOREGRD:
608 stc_app_value_s app_value;
609 stc_proc_value_s proc_value;
611 memset(&app_value, 0, sizeof(stc_app_value_s));
612 memset(&proc_value, 0, sizeof(stc_proc_value_s));
614 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
616 app_value.type = app_type;
617 app_value.state = STC_APP_STATE_FOREGROUND;
618 app_value.processes = NULL;
620 proc_value.pid = pid;
621 proc_value.ground = STC_APP_STATE_FOREGROUND;
623 bg_classid = get_classid_by_app_id(bg_app_id, FALSE);
624 fg_classid = get_classid_by_app_id(app_id, TRUE);
626 stc_plugin_monitor_add_app(fg_classid, app_id, pkg_id, app_value);
628 stc_plugin_monitor_move_proc(bg_classid, fg_classid);
630 stc_plugin_monitor_add_proc(fg_classid, app_id, proc_value);
631 stc_plugin_monitor_update_proc_ground(fg_classid, app_id, proc_value);
636 case STC_CMD_SET_BACKGRD:
641 stc_app_value_s app_value;
642 stc_proc_value_s proc_value;
644 memset(&app_value, 0, sizeof(stc_app_value_s));
645 memset(&proc_value, 0, sizeof(stc_proc_value_s));
647 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
649 app_value.type = app_type;
650 app_value.state = STC_APP_STATE_BACKGROUND;
651 app_value.processes = NULL;
653 proc_value.pid = pid;
654 proc_value.ground = STC_APP_STATE_BACKGROUND;
656 fg_classid = get_classid_by_app_id(app_id, FALSE);
657 bg_classid = get_classid_by_app_id(bg_app_id, TRUE);
659 stc_plugin_monitor_add_app(bg_classid, bg_app_id, pkg_id, app_value);
661 stc_plugin_monitor_move_proc(fg_classid, bg_classid);
663 stc_plugin_monitor_add_proc(bg_classid, bg_app_id, proc_value);
664 stc_plugin_monitor_update_proc_ground(bg_classid, bg_app_id, proc_value);
669 case STC_CMD_SET_SERVICE_LAUNCHED:
673 stc_app_value_s app_value;
674 stc_proc_value_s proc_value;
677 memset(&app_value, 0, sizeof(stc_app_value_s));
678 memset(&proc_value, 0, sizeof(stc_proc_value_s));
680 classid = get_classid_by_app_id(app_id, FALSE);
681 is_exist = stc_plugin_monitor_lookup_app(classid);
683 app_value.type = app_type;
684 app_value.state = STC_APP_STATE_FOREGROUND;
685 app_value.processes = NULL;
687 proc_value.pid = pid;
688 proc_value.ground = STC_APP_STATE_FOREGROUND;
690 stc_plugin_monitor_add_app(classid, app_id, pkg_id, app_value);
691 stc_plugin_monitor_add_proc(classid, app_id, proc_value);
693 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
694 classid = get_classid_by_app_id(bg_app_id, TRUE);
696 app_value.type = app_type;
697 app_value.state = STC_APP_STATE_BACKGROUND;
698 app_value.processes = NULL;
700 proc_value.pid = pid;
701 proc_value.ground = STC_APP_STATE_BACKGROUND;
703 stc_plugin_monitor_add_app(classid, bg_app_id, pkg_id, app_value);
704 stc_plugin_monitor_add_proc(classid, bg_app_id, proc_value);
710 case STC_CMD_SET_TERMINATED:
715 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
716 classid = get_classid_by_app_id(bg_app_id, FALSE);
718 if (classid == STC_UNKNOWN_CLASSID)
719 classid = get_classid_by_app_id(app_id, FALSE);
721 stc_plugin_monitor_remove_proc(classid, pid);
727 STC_LOGE("Unhandled command");
728 ret = STC_ERROR_INVALID_PARAMETER;
734 API stc_plugin_procfs_s stc_plugin_procfs = {
736 stc_plugin_procfs_initialize,
737 .deinitialize_plugin =
738 stc_plugin_procfs_deinitialize,
740 stc_plugin_procfs_load,
741 .procfs_status_changed =
742 stc_plugin_procfs_status_changed