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-procfs.h"
40 char cmdline[PROC_NAME_MAX];
41 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
44 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
45 struct nlmsghdr nl_hdr;
46 struct __attribute__ ((__packed__)) {
48 enum proc_cn_mcast_op cn_mcast;
52 typedef struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
53 struct nlmsghdr nl_hdr;
54 struct __attribute__ ((__packed__)) {
56 struct proc_event proc_ev;
58 } nl_connector_proc_event_s;
60 static int nl_connector_sock = -1;
61 static guint nl_connector_gsource_id = 0;
62 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 [\033[1;35m%s\033[0;m] "
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);
140 STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] pid[%s] "
141 "ppid[\033[1;35m%s\033[0;m] 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) {
162 STC_LOGE("invalid parameters");
166 lookup = g_tree_lookup(proc_tree, key);
169 STC_LOGD("LOOKUP: tgid[\033[1;33m%s\033[0;m] pid[%s] ppid[\033[1;35m%s\033[0;m] "
170 "cmdline[\033[0;34m%s\033[0;m] name[%s]", lookup->status[PROC_STATUS_TGID],
171 lookup->status[PROC_STATUS_PID], lookup->status[PROC_STATUS_PPID],
172 lookup->cmdline, lookup->status[PROC_STATUS_NAME]);
176 proc_key_s *proc_key = MALLOC0(proc_key_s, 1);
177 if (proc_key == NULL) {
178 STC_LOGE("memory allocation failed");
182 proc_value_s *proc_value = MALLOC0(proc_value_s, 1);
183 if (proc_value == NULL) {
184 STC_LOGE("memory allocation failed");
189 memcpy(proc_key, key, sizeof(proc_key_s));
190 memcpy(proc_value, value, sizeof(proc_value_s));
193 STC_LOGD("cmdline [%s] pid[%s] ppid[%s]", value->cmdline,
194 value->status[PROC_STATUS_PID], value->status[PROC_STATUS_PPID]);
196 g_tree_insert(proc_tree, proc_key, proc_value);
199 __proc_tree_printall();
202 parent = __proc_tree_find_parent(proc_value);
204 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
205 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
207 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
208 proc_value->cmdline, proc_value->cmdline, STC_APP_TYPE_SERVICE);
211 static void __proc_tree_remove(const proc_key_s *key)
213 if (proc_tree == NULL) {
214 STC_LOGE("tree is null");
218 stc_plugin_procfs_status_changed(STC_CMD_SET_TERMINATED, key->pid, NULL,
219 NULL, STC_APP_TYPE_NONE);
221 g_tree_remove(proc_tree, key);
224 __proc_tree_printall();
228 static gboolean __check_excn(char *cmdline)
230 stc_error_e ret = STC_ERROR_NONE;
232 if (cmdline[0] == '(')
235 ret = stc_monitor_check_excn_by_cmdline(cmdline);
236 if (ret == STC_ERROR_UNINITIALIZED ||
237 ret == STC_ERROR_NO_DATA)
243 static void __open_nl_connector_sock(void)
245 __STC_LOG_FUNC_ENTER__;
246 GIOChannel *gio = NULL;
248 if (nl_connector_sock != -1 &&
249 nl_connector_gsource_id != 0) {
250 STC_LOGE("Socket is already open");
251 __STC_LOG_FUNC_EXIT__;
255 if (nl_connector_sock != -1) {
256 close(nl_connector_sock);
257 nl_connector_sock = -1;
260 if (nl_connector_gsource_id != 0) {
261 g_source_remove(nl_connector_gsource_id);
262 nl_connector_gsource_id = 0;
265 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
266 if (nl_connector_sock == -1) {
267 __STC_LOG_FUNC_EXIT__;
271 gio = g_io_channel_unix_new(nl_connector_sock);
272 nl_connector_gsource_id =
273 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
274 (GIOFunc) __process_nl_connector_message,
276 g_io_channel_unref(gio);
277 __STC_LOG_FUNC_EXIT__;
280 static void __close_nl_connector_sock(void)
282 __STC_LOG_FUNC_ENTER__;
283 if (nl_connector_sock != -1) {
284 close(nl_connector_sock);
285 nl_connector_sock = -1;
288 if (nl_connector_gsource_id != 0) {
289 g_source_remove(nl_connector_gsource_id);
290 nl_connector_gsource_id = 0;
292 __STC_LOG_FUNC_EXIT__;
295 static void __reopen_nl_connector_sock(void)
297 __close_nl_connector_sock();
298 __open_nl_connector_sock();
301 static void __process_event_fork(int tgid, int pid)
303 char cmdline[PROC_NAME_MAX] = {0, };
304 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
306 /* TODO: Add newly created thread to the process tasks */
310 memset(status, 0x0, sizeof(status));
312 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
313 STC_ERROR_NONE == proc_get_status(pid, status)) {
315 if (__check_excn(cmdline)) {
317 STC_LOGD("[%s] monitoring is excepted", cmdline);
325 memset(&key, 0x0, sizeof(proc_key_s));
326 memset(&value, 0x0, sizeof(proc_value_s));
329 for (i = 0; i < PROC_STATUS_CNT; ++i)
330 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
331 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
334 STC_LOGD("\033[1;34mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
335 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
336 STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
337 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
338 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
341 __proc_tree_add(&key, &value);
345 static void __process_event_exec(int tgid, int pid)
347 char cmdline[PROC_NAME_MAX] = {0, };
348 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
350 /* TODO: Add newly created thread to the process tasks */
354 memset(status, 0x0, sizeof(status));
356 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
357 STC_ERROR_NONE == proc_get_status(pid, status)) {
359 if (__check_excn(cmdline)) {
361 STC_LOGD("[%s] monitoring is excepted", cmdline);
369 memset(&key, 0x0, sizeof(proc_key_s));
370 memset(&value, 0x0, sizeof(proc_value_s));
373 for (i = 0; i < PROC_STATUS_CNT; ++i)
374 g_strlcpy(value.status[i], status[i],
375 sizeof(value.status[i]));
376 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
379 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
380 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
381 STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
382 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
383 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
386 __proc_tree_add(&key, &value);
390 static void __process_event_exit(int tgid, int pid, int exit_code)
393 proc_value_s *lookup;
399 lookup = __proc_tree_lookup(&key);
400 if (lookup == NULL) /* unmonitored process */
404 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
405 "pid[%d] exitcode[\033[0;31m%d\033[0;m]", tgid, pid, exit_code);
407 __proc_tree_remove(&key);
410 static gboolean __process_nl_connector_message(GIOChannel *source,
411 GIOCondition condition,
415 int sock = g_io_channel_unix_get_fd(source);
416 nl_connector_proc_event_s msg;
418 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
419 (condition & G_IO_NVAL)) {
420 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
422 STC_LOGE("Netlink Connector socket received G_IO event, closing"
423 " socket. G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
424 (condition & G_IO_ERR), (condition & G_IO_HUP),
425 (condition & G_IO_NVAL));
426 __reopen_nl_connector_sock();
427 __STC_LOG_FUNC_EXIT__;
431 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
433 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
435 __STC_LOG_FUNC_EXIT__;
439 switch (msg.proc_ev.what) {
440 case PROC_EVENT_FORK:
441 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
442 msg.proc_ev.event_data.fork.child_pid);
444 case PROC_EVENT_EXEC:
445 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
446 msg.proc_ev.event_data.exec.process_pid);
448 case PROC_EVENT_EXIT:
449 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
450 msg.proc_ev.event_data.exit.process_pid,
451 msg.proc_ev.event_data.exit.exit_code);
460 static int __subscribe_proc_events(void)
462 __STC_LOG_FUNC_ENTER__;
463 nl_connector_msg_s msg;
465 int sock = nl_connector_sock;
468 __STC_LOG_FUNC_EXIT__;
472 memset(&msg, 0, sizeof(nl_connector_msg_s));
474 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
475 msg.nl_hdr.nlmsg_pid = getpid();
476 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
478 msg.cn_msg.id.idx = CN_IDX_PROC;
479 msg.cn_msg.id.val = CN_VAL_PROC;
480 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
482 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
484 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
486 STC_LOGE("Error sending netlink connector message");
487 __STC_LOG_FUNC_EXIT__;
491 __STC_LOG_FUNC_EXIT__;
495 static int __unsubscribe_proc_events(void)
497 __STC_LOG_FUNC_ENTER__;
498 nl_connector_msg_s msg;
500 int sock = nl_connector_sock;
503 __STC_LOG_FUNC_EXIT__;
507 memset(&msg, 0, sizeof(nl_connector_msg_s));
509 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
510 msg.nl_hdr.nlmsg_pid = getpid();
511 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
513 msg.cn_msg.id.idx = CN_IDX_PROC;
514 msg.cn_msg.id.val = CN_VAL_PROC;
515 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
517 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
519 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
521 STC_LOGE("Error sending netlink connector message");
522 __STC_LOG_FUNC_EXIT__;
526 __STC_LOG_FUNC_EXIT__;
530 static bool __process_pid_cb(pid_t pid, void *user_data)
532 char cmdline[PROC_NAME_MAX] = {0, };
533 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
535 memset(status, 0x0, sizeof(status));
537 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
538 STC_ERROR_NONE == proc_get_status(pid, status)) {
540 if (__check_excn(cmdline))
547 memset(&key, 0x0, sizeof(proc_key_s));
548 memset(&value, 0x0, sizeof(proc_value_s));
551 for (i = 0; i < PROC_STATUS_CNT; ++i)
552 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
553 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
555 __proc_tree_add(&key, &value);
561 int stc_plugin_procfs_initialize(void)
563 __STC_LOG_FUNC_ENTER__;
565 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
566 __proc_tree_key_free,
567 __proc_tree_value_free);
569 /* TODO: Fill proc tree with current procfs state */
571 __open_nl_connector_sock();
572 __subscribe_proc_events();
573 __STC_LOG_FUNC_EXIT__;
574 return STC_ERROR_NONE;
577 int stc_plugin_procfs_deinitialize(void)
579 __STC_LOG_FUNC_ENTER__;
581 if (nl_connector_sock == -1) {
582 STC_LOGD("socket already closed");
583 return STC_ERROR_NONE;
586 __unsubscribe_proc_events();
587 __close_nl_connector_sock();
589 g_tree_destroy(proc_tree);
592 __STC_LOG_FUNC_EXIT__;
593 return STC_ERROR_NONE;
596 stc_error_e stc_plugin_procfs_load(void)
598 __STC_LOG_FUNC_ENTER__;
600 proc_foreach_pid(__process_pid_cb, NULL);
602 __STC_LOG_FUNC_EXIT__;
603 return STC_ERROR_NONE;
606 stc_error_e stc_plugin_procfs_status_changed(stc_cmd_type_e cmd, pid_t pid,
609 stc_app_type_e app_type)
611 stc_error_e ret = STC_ERROR_NONE;
613 if ((pkg_id && app_id) && STC_DEBUG_LOG)
614 STC_LOGD("cmd [%d] pkgid [%s] appid [%s] pid[%d] type [%d]",
615 cmd, pkg_id, app_id, pid, app_type);
618 case STC_CMD_SET_FOREGRD:
620 stc_app_key_s app_key;
621 stc_app_value_s app_value;
622 stc_process_key_s proc_key;
623 stc_process_value_s proc_value;
625 memset(&app_key, 0, sizeof(stc_app_key_s));
626 memset(&app_value, 0, sizeof(stc_app_value_s));
627 memset(&proc_key, 0, sizeof(stc_process_key_s));
628 memset(&proc_value, 0, sizeof(stc_process_value_s));
630 app_key.pkg_id = g_strdup(pkg_id);
631 app_key.app_id = g_strdup(app_id);
633 app_value.type = app_type;
634 app_value.processes = NULL;
638 proc_value.ground = STC_APP_STATE_FOREGROUND;
640 stc_monitor_application_add(app_key, app_value);
641 stc_monitor_process_add(app_key, proc_key, proc_value);
642 stc_monitor_process_update_ground(app_key, proc_key,
643 STC_APP_STATE_FOREGROUND);
645 FREE(app_key.pkg_id);
646 FREE(app_key.app_id);
649 case STC_CMD_SET_BACKGRD:
651 stc_app_key_s app_key;
652 stc_app_value_s app_value;
653 stc_process_key_s proc_key;
654 stc_process_value_s proc_value;
656 memset(&app_key, 0, sizeof(stc_app_key_s));
657 memset(&app_value, 0, sizeof(stc_app_value_s));
658 memset(&proc_key, 0, sizeof(stc_process_key_s));
659 memset(&proc_value, 0, sizeof(stc_process_value_s));
661 app_key.pkg_id = g_strdup(pkg_id);
662 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
665 app_value.type = app_type;
666 app_value.processes = NULL;
670 proc_value.ground = STC_APP_STATE_BACKGROUND;
672 stc_monitor_application_add(app_key, app_value);
673 stc_monitor_process_add(app_key, proc_key, proc_value);
674 stc_monitor_process_update_ground(app_key, proc_key,
675 STC_APP_STATE_BACKGROUND);
677 FREE(app_key.pkg_id);
678 FREE(app_key.app_id);
681 case STC_CMD_SET_SERVICE_LAUNCHED:
683 stc_app_key_s app_key;
684 stc_app_value_s app_value;
685 stc_process_key_s proc_key;
686 stc_process_value_s proc_value;
688 memset(&app_key, 0, sizeof(stc_app_key_s));
689 memset(&app_value, 0, sizeof(stc_app_value_s));
690 memset(&proc_key, 0, sizeof(stc_process_key_s));
691 memset(&proc_value, 0, sizeof(stc_process_value_s));
693 app_key.pkg_id = g_strdup(pkg_id);
694 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
697 app_value.type = app_type;
698 app_value.processes = NULL;
702 /* services will run always in background. */
703 proc_value.ground = STC_APP_STATE_BACKGROUND;
705 stc_monitor_application_add(app_key, app_value);
706 stc_monitor_process_add(app_key, proc_key, proc_value);
708 FREE(app_key.pkg_id);
709 g_free(app_key.app_id);
712 case STC_CMD_SET_TERMINATED:
714 stc_monitor_process_remove(pid);
718 STC_LOGE("Unhandled command");
719 ret = STC_ERROR_INVALID_PARAMETER;
725 API stc_plugin_procfs_s stc_plugin_procfs = {
727 stc_plugin_procfs_initialize,
728 .deinitialize_plugin =
729 stc_plugin_procfs_deinitialize,
731 stc_plugin_procfs_load,
732 .procfs_status_changed =
733 stc_plugin_procfs_status_changed