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) {
161 ret = STC_ERROR_INVALID_PARAMETER;
165 if ((pkg_id && app_id) && STC_STAT_LOG)
166 STC_LOGD("cmd[%d] pkgid[%s] appid[%s] pid[%d] type[%d]",
167 cmd, pkg_id, app_id, pid, app_type);
170 case STC_CMD_SET_FOREGRD:
172 uint32_t fg_classid = STC_UNKNOWN_CLASSID;
173 uint32_t bg_classid = STC_UNKNOWN_CLASSID;
174 char *bg_app_id = NULL;
175 stc_app_value_s app_value;
176 stc_proc_value_s proc_value;
178 memset(&app_value, 0, sizeof(stc_app_value_s));
179 memset(&proc_value, 0, sizeof(stc_proc_value_s));
181 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
183 app_value.type = app_type;
184 app_value.state = STC_APP_STATE_FOREGROUND;
185 app_value.processes = NULL;
187 proc_value.pid = pid;
188 proc_value.ground = STC_APP_STATE_FOREGROUND;
190 bg_classid = get_classid_by_app_id(bg_app_id, FALSE);
191 fg_classid = get_classid_by_app_id(app_id, TRUE);
193 stc_plugin_monitor_add_app(fg_classid, app_id, pkg_id, app_value);
195 stc_plugin_monitor_move_proc(bg_classid, fg_classid);
197 stc_plugin_monitor_add_proc(fg_classid, app_id, proc_value);
198 stc_plugin_monitor_update_proc_ground(fg_classid, app_id, proc_value);
203 case STC_CMD_SET_BACKGRD:
205 uint32_t bg_classid = STC_UNKNOWN_CLASSID;
206 uint32_t fg_classid = STC_UNKNOWN_CLASSID;
207 char *bg_app_id = NULL;
208 stc_app_value_s app_value;
209 stc_proc_value_s proc_value;
211 memset(&app_value, 0, sizeof(stc_app_value_s));
212 memset(&proc_value, 0, sizeof(stc_proc_value_s));
214 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
216 app_value.type = app_type;
217 app_value.state = STC_APP_STATE_BACKGROUND;
218 app_value.processes = NULL;
220 proc_value.pid = pid;
221 proc_value.ground = STC_APP_STATE_BACKGROUND;
223 fg_classid = get_classid_by_app_id(app_id, FALSE);
224 bg_classid = get_classid_by_app_id(bg_app_id, TRUE);
226 stc_plugin_monitor_add_app(bg_classid, bg_app_id, pkg_id, app_value);
228 stc_plugin_monitor_move_proc(fg_classid, bg_classid);
230 stc_plugin_monitor_add_proc(bg_classid, bg_app_id, proc_value);
231 stc_plugin_monitor_update_proc_ground(bg_classid, bg_app_id, proc_value);
236 case STC_CMD_SET_APP_LAUNCHED:
238 uint32_t classid = STC_UNKNOWN_CLASSID;
239 stc_app_value_s app_value;
240 stc_proc_value_s proc_value;
242 memset(&app_value, 0, sizeof(stc_app_value_s));
243 memset(&proc_value, 0, sizeof(stc_proc_value_s));
245 classid = get_classid_by_app_id(app_id, FALSE);
247 app_value.type = app_type;
248 app_value.state = STC_APP_STATE_FOREGROUND;
249 app_value.processes = NULL;
251 proc_value.pid = pid;
252 proc_value.ground = STC_APP_STATE_FOREGROUND;
254 stc_plugin_monitor_add_app(classid, app_id, pkg_id, app_value);
255 stc_plugin_monitor_add_proc(classid, app_id, proc_value);
259 case STC_CMD_SET_SERVICE_LAUNCHED:
261 uint32_t classid = STC_UNKNOWN_CLASSID;
262 char *bg_app_id = NULL;
263 stc_app_value_s app_value;
264 stc_proc_value_s proc_value;
267 memset(&app_value, 0, sizeof(stc_app_value_s));
268 memset(&proc_value, 0, sizeof(stc_proc_value_s));
270 classid = get_classid_by_app_id(app_id, FALSE);
271 is_exist = stc_plugin_monitor_lookup_app(classid);
273 app_value.type = app_type;
274 app_value.state = STC_APP_STATE_FOREGROUND;
275 app_value.processes = NULL;
277 proc_value.pid = pid;
278 proc_value.ground = STC_APP_STATE_FOREGROUND;
280 stc_plugin_monitor_add_app(classid, app_id, pkg_id, app_value);
281 stc_plugin_monitor_add_proc(classid, app_id, proc_value);
283 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
284 classid = get_classid_by_app_id(bg_app_id, TRUE);
286 app_value.type = app_type;
287 app_value.state = STC_APP_STATE_BACKGROUND;
288 app_value.processes = NULL;
290 proc_value.pid = pid;
291 proc_value.ground = STC_APP_STATE_BACKGROUND;
293 stc_plugin_monitor_add_app(classid, bg_app_id, pkg_id, app_value);
294 stc_plugin_monitor_add_proc(classid, bg_app_id, proc_value);
300 case STC_CMD_SET_TERMINATED:
302 uint32_t classid = STC_UNKNOWN_CLASSID;
303 char *bg_app_id = NULL;
305 if (app_type == STC_APP_TYPE_NONE) {
306 bg_app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX, NULL);
307 classid = get_classid_by_app_id(bg_app_id, FALSE);
310 if (classid == STC_UNKNOWN_CLASSID)
311 classid = get_classid_by_app_id(bg_app_id, FALSE);
313 stc_plugin_monitor_remove_proc(classid, pid);
319 STC_LOGE("Unhandled command");
320 ret = STC_ERROR_INVALID_PARAMETER;
326 static void __proc_tree_add(proc_key_s *key, proc_value_s *value)
328 proc_value_s *lookup;
329 proc_value_s *parent;
331 if (proc_tree == NULL) {
332 STC_LOGE("tree is null");
336 if (key == NULL || value == NULL) {
338 STC_LOGE("invalid parameters");
342 lookup = g_tree_lookup(proc_tree, key);
346 proc_key_s *proc_key = MALLOC0(proc_key_s, 1);
347 if (proc_key == NULL) {
348 STC_LOGE("memory allocation failed");
352 proc_value_s *proc_value = MALLOC0(proc_value_s, 1);
353 if (proc_value == NULL) {
354 STC_LOGE("memory allocation failed");
359 memcpy(proc_key, key, sizeof(proc_key_s));
360 memcpy(proc_value, value, sizeof(proc_value_s));
362 g_tree_insert(proc_tree, proc_key, proc_value);
365 __proc_tree_printall();
368 parent = __proc_tree_find_parent(proc_value);
370 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
371 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
373 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, proc_key->pid,
374 proc_value->cmdline, proc_value->cmdline, STC_APP_TYPE_SERVICE);
377 static void __proc_tree_remove(const proc_key_s *key,
378 const proc_value_s *value)
380 if (proc_tree == NULL) {
381 STC_LOGE("tree is null");
385 stc_plugin_procfs_status_changed(STC_CMD_SET_TERMINATED, key->pid,
386 value->cmdline, value->cmdline, STC_APP_TYPE_NONE);
388 g_tree_remove(proc_tree, key);
391 __proc_tree_printall();
395 static gboolean __check_excn(char *cmdline)
397 stc_error_e ret = STC_ERROR_NONE;
399 if (cmdline[0] == '(')
402 ret = stc_plugin_check_exception_by_cmdline(cmdline);
403 if (ret == STC_ERROR_UNINITIALIZED ||
404 ret == STC_ERROR_NO_DATA)
410 static void __open_nl_connector_sock(void)
412 __STC_LOG_FUNC_ENTER__;
413 GIOChannel *gio = NULL;
415 if (nl_connector_sock != -1 &&
416 nl_connector_gsource_id != 0) {
417 STC_LOGE("Socket is already open");
418 __STC_LOG_FUNC_EXIT__;
422 if (nl_connector_sock != -1) {
423 close(nl_connector_sock);
424 nl_connector_sock = -1;
427 if (nl_connector_gsource_id != 0) {
428 g_source_remove(nl_connector_gsource_id);
429 nl_connector_gsource_id = 0;
432 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
433 if (nl_connector_sock == -1) {
434 __STC_LOG_FUNC_EXIT__;
438 gio = g_io_channel_unix_new(nl_connector_sock);
439 nl_connector_gsource_id =
440 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
441 (GIOFunc) __process_nl_connector_message,
443 g_io_channel_unref(gio);
444 __STC_LOG_FUNC_EXIT__;
447 static void __close_nl_connector_sock(void)
449 __STC_LOG_FUNC_ENTER__;
450 if (nl_connector_sock != -1) {
451 close(nl_connector_sock);
452 nl_connector_sock = -1;
455 if (nl_connector_gsource_id != 0) {
456 g_source_remove(nl_connector_gsource_id);
457 nl_connector_gsource_id = 0;
459 __STC_LOG_FUNC_EXIT__;
462 static void __reopen_nl_connector_sock(void)
464 __close_nl_connector_sock();
465 __open_nl_connector_sock();
468 static void __process_event_fork(int tgid, int pid)
470 char cmdline[PROC_NAME_MAX] = {0, };
471 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
473 /* TODO: Add newly created thread to the process tasks */
477 memset(status, 0x0, sizeof(status));
479 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
480 STC_ERROR_NONE == proc_get_status(pid, status)) {
482 if (__check_excn(cmdline))
489 memset(&key, 0x0, sizeof(proc_key_s));
490 memset(&value, 0x0, sizeof(proc_value_s));
493 for (i = 0; i < PROC_STATUS_CNT; ++i)
494 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
495 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
498 STC_LOGD("\033[1;32mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] "
499 "ppid[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
500 tgid, status[PROC_STATUS_PPID], cmdline, pid);
502 __proc_tree_add(&key, &value);
506 static void __process_event_exec(int tgid, int pid)
508 char cmdline[PROC_NAME_MAX] = {0, };
509 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
511 /* TODO: Add newly created thread to the process tasks */
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],
533 sizeof(value.status[i]));
534 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
537 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] "
538 "ppid[%s] cmdline[\033[0;34m%s\033[0;m] pid[%d]",
539 tgid, status[PROC_STATUS_PPID], cmdline, pid);
541 __proc_tree_add(&key, &value);
545 static void __process_event_exit(int tgid, int pid, int exit_code)
548 proc_value_s *lookup;
554 lookup = __proc_tree_lookup(&key);
555 if (lookup == NULL) /* unmonitored process */
559 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
560 "cmdline[\033[0;34m%s\033[0;m] pid[%d] exitcode[%d]",
561 tgid, lookup->cmdline, pid, exit_code);
563 __proc_tree_remove(&key, lookup);
566 static gboolean __process_nl_connector_message(GIOChannel *source,
567 GIOCondition condition,
571 int sock = g_io_channel_unix_get_fd(source);
572 nl_connector_proc_event_s msg;
574 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
575 (condition & G_IO_NVAL)) {
576 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
578 STC_LOGE("Netlink Connector socket received G_IO event, closing"
579 " socket. G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
580 (condition & G_IO_ERR), (condition & G_IO_HUP),
581 (condition & G_IO_NVAL));
582 __reopen_nl_connector_sock();
583 __STC_LOG_FUNC_EXIT__;
587 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
589 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
591 __STC_LOG_FUNC_EXIT__;
595 switch (msg.proc_ev.what) {
596 case PROC_EVENT_FORK:
597 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
598 msg.proc_ev.event_data.fork.child_pid);
600 case PROC_EVENT_EXEC:
601 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
602 msg.proc_ev.event_data.exec.process_pid);
604 case PROC_EVENT_EXIT:
605 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
606 msg.proc_ev.event_data.exit.process_pid,
607 msg.proc_ev.event_data.exit.exit_code);
616 static int __subscribe_proc_events(void)
618 __STC_LOG_FUNC_ENTER__;
619 nl_connector_msg_s msg;
621 int sock = nl_connector_sock;
624 __STC_LOG_FUNC_EXIT__;
625 return STC_ERROR_INVALID_PARAMETER;
628 memset(&msg, 0, sizeof(nl_connector_msg_s));
630 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
631 msg.nl_hdr.nlmsg_pid = getpid();
632 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
634 msg.cn_msg.id.idx = CN_IDX_PROC;
635 msg.cn_msg.id.val = CN_VAL_PROC;
636 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
638 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
640 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
642 STC_LOGE("Error sending netlink connector message");
643 __STC_LOG_FUNC_EXIT__;
644 return STC_ERROR_FAIL;
647 __STC_LOG_FUNC_EXIT__;
648 return STC_ERROR_NONE;
651 static int __unsubscribe_proc_events(void)
653 __STC_LOG_FUNC_ENTER__;
654 nl_connector_msg_s msg;
656 int sock = nl_connector_sock;
659 __STC_LOG_FUNC_EXIT__;
660 return STC_ERROR_INVALID_PARAMETER;
663 memset(&msg, 0, sizeof(nl_connector_msg_s));
665 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
666 msg.nl_hdr.nlmsg_pid = getpid();
667 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
669 msg.cn_msg.id.idx = CN_IDX_PROC;
670 msg.cn_msg.id.val = CN_VAL_PROC;
671 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
673 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
675 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
677 STC_LOGE("Error sending netlink connector message");
678 __STC_LOG_FUNC_EXIT__;
679 return STC_ERROR_FAIL;
682 __STC_LOG_FUNC_EXIT__;
683 return STC_ERROR_NONE;
686 static bool __process_pid_cb(pid_t pid, void *user_data)
688 char cmdline[PROC_NAME_MAX] = {0, };
689 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
691 memset(status, 0x0, sizeof(status));
693 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
694 STC_ERROR_NONE == proc_get_status(pid, status)) {
696 if (__check_excn(cmdline))
703 memset(&key, 0x0, sizeof(proc_key_s));
704 memset(&value, 0x0, sizeof(proc_value_s));
707 for (i = 0; i < PROC_STATUS_CNT; ++i)
708 g_strlcpy(value.status[i], status[i], sizeof(value.status[i]));
709 g_strlcpy(value.cmdline, cmdline, sizeof(value.cmdline));
711 __proc_tree_add(&key, &value);
717 int stc_plugin_procfs_initialize(void)
719 __STC_LOG_FUNC_ENTER__;
720 int ret = STC_ERROR_NONE;
722 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
723 __proc_tree_key_free,
724 __proc_tree_value_free);
726 /* TODO: Fill proc tree with current procfs state */
728 __open_nl_connector_sock();
729 ret = __subscribe_proc_events();
731 __STC_LOG_FUNC_EXIT__;
735 int stc_plugin_procfs_deinitialize(void)
737 __STC_LOG_FUNC_ENTER__;
738 int ret = STC_ERROR_NONE;
740 if (nl_connector_sock == -1) {
741 STC_LOGD("socket already closed");
742 return STC_ERROR_NONE;
745 ret = __unsubscribe_proc_events();
746 __close_nl_connector_sock();
748 g_tree_destroy(proc_tree);
751 __STC_LOG_FUNC_EXIT__;
755 stc_error_e stc_plugin_procfs_load(void)
757 __STC_LOG_FUNC_ENTER__;
759 proc_foreach_pid(__process_pid_cb, NULL);
761 __STC_LOG_FUNC_EXIT__;
762 return STC_ERROR_NONE;
765 API stc_plugin_procfs_s stc_plugin_procfs = {
767 stc_plugin_procfs_initialize,
768 .deinitialize_plugin =
769 stc_plugin_procfs_deinitialize,
771 stc_plugin_procfs_load,
772 .procfs_status_changed =
773 stc_plugin_procfs_status_changed