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);
105 static gboolean __proc_tree_foreach_print(gpointer key, gpointer value,
108 proc_key_s *proc_key = (proc_key_s *)key;
109 proc_value_s *proc_value = (proc_value_s *)value;
111 STC_LOGD("Proc pid [\033[1;33m%d\033[0;m] ppid [\033[1;35m%s\033[0;m] "
112 "cmdline [\033[0;34m%s\033[0;m]", proc_key->pid,
113 proc_value->status[PROC_STATUS_PPID], proc_value->cmdline);
118 static void __proc_tree_printall(void)
120 g_tree_foreach(proc_tree, __proc_tree_foreach_print, NULL);
123 static proc_value_s * __proc_tree_find_parent(proc_value_s *value)
125 proc_value_s *parent = NULL;
126 proc_value_s *lookup = value;
130 key.pid = atoi(lookup->status[PROC_STATUS_PPID]);
131 lookup = __proc_tree_lookup(&key);
138 STC_LOGD("\033[0;35mPARENT\033[0;m: tgid[\033[1;33m%s\033[0;m] pid[%s] "
139 "ppid[\033[1;35m%s\033[0;m] cmdline[\033[0;34m%s\033[0;m] name[%s]",
140 parent->status[PROC_STATUS_TGID], parent->status[PROC_STATUS_PID],
141 parent->status[PROC_STATUS_PPID], parent->cmdline,
142 parent->status[PROC_STATUS_NAME]);
148 static void __proc_tree_add(proc_key_s *key,
151 proc_value_s *lookup;
152 proc_value_s *parent;
154 if (proc_tree == NULL) {
155 STC_LOGE("tree is null");
159 lookup = g_tree_lookup(proc_tree, key);
162 STC_LOGD("LOOKUP: tgid[\033[1;33m%s\033[0;m] pid[%s] ppid[\033[1;35m%s\033[0;m] "
163 "cmdline[\033[0;34m%s\033[0;m] name[%s]", lookup->status[PROC_STATUS_TGID],
164 lookup->status[PROC_STATUS_PID], lookup->status[PROC_STATUS_PPID],
165 lookup->cmdline, lookup->status[PROC_STATUS_NAME]);
170 STC_LOGD("cmdline [%s] pid[%s] ppid[%s]", value->cmdline,
171 value->status[PROC_STATUS_PID], value->status[PROC_STATUS_PPID]);
173 g_tree_insert(proc_tree, key, value);
176 __proc_tree_printall();
178 parent = __proc_tree_find_parent(value);
180 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
181 parent->cmdline, parent->cmdline, STC_APP_TYPE_SERVICE);
183 stc_plugin_procfs_status_changed(STC_CMD_SET_SERVICE_LAUNCHED, key->pid,
184 value->cmdline, value->cmdline, STC_APP_TYPE_SERVICE);
187 static void __proc_tree_remove(const proc_key_s *key)
189 if (proc_tree == NULL) {
190 STC_LOGE("tree is null");
194 stc_plugin_procfs_status_changed(STC_CMD_SET_TERMINATED, key->pid, NULL,
195 NULL, STC_APP_TYPE_NONE);
197 g_tree_remove(proc_tree, key);
200 __proc_tree_printall();
203 static gboolean __check_excn(char *cmdline)
205 stc_error_e ret = STC_ERROR_NONE;
207 if (cmdline[0] == '(')
210 ret = stc_monitor_check_excn_by_cmdline(cmdline);
211 if (ret == STC_ERROR_UNINITIALIZED ||
212 ret == STC_ERROR_NO_DATA)
218 static void __open_nl_connector_sock(void)
220 __STC_LOG_FUNC_ENTER__;
221 GIOChannel *gio = NULL;
223 if (nl_connector_sock != -1 &&
224 nl_connector_gsource_id != 0) {
225 STC_LOGE("Socket is already open");
226 __STC_LOG_FUNC_EXIT__;
230 if (nl_connector_sock != -1) {
231 close(nl_connector_sock);
232 nl_connector_sock = -1;
235 if (nl_connector_gsource_id != 0) {
236 g_source_remove(nl_connector_gsource_id);
237 nl_connector_gsource_id = 0;
240 nl_connector_sock = create_netlink(NETLINK_CONNECTOR, CN_IDX_PROC);
241 if (nl_connector_sock == -1) {
242 __STC_LOG_FUNC_EXIT__;
246 gio = g_io_channel_unix_new(nl_connector_sock);
247 nl_connector_gsource_id =
248 g_io_add_watch(gio, G_IO_IN | G_IO_ERR | G_IO_HUP,
249 (GIOFunc) __process_nl_connector_message,
251 g_io_channel_unref(gio);
252 __STC_LOG_FUNC_EXIT__;
255 static void __close_nl_connector_sock(void)
257 __STC_LOG_FUNC_ENTER__;
258 if (nl_connector_sock != -1) {
259 close(nl_connector_sock);
260 nl_connector_sock = -1;
263 if (nl_connector_gsource_id != 0) {
264 g_source_remove(nl_connector_gsource_id);
265 nl_connector_gsource_id = 0;
267 __STC_LOG_FUNC_EXIT__;
270 static void __reopen_nl_connector_sock(void)
272 __close_nl_connector_sock();
273 __open_nl_connector_sock();
276 static void __process_event_fork(int tgid, int pid)
278 char cmdline[PROC_NAME_MAX] = {0, };
279 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
281 memset(status, 0x0, sizeof(status));
283 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
284 STC_ERROR_NONE == proc_get_status(pid, status)) {
286 if (__check_excn(cmdline)) {
288 STC_LOGD("[%s] monitoring is excepted", cmdline);
296 key = MALLOC0(proc_key_s, 1);
298 STC_LOGE("memory allocation failed");
302 value = MALLOC0(proc_value_s, 1);
304 STC_LOGE("memory allocation failed");
310 for (i = 0; i < PROC_STATUS_CNT; ++i)
311 g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
312 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
315 STC_LOGD("\033[1;34mFORK\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
316 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
317 STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
318 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
319 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
322 __proc_tree_add(key, value);
326 static void __process_event_exec(int tgid, int pid)
328 char cmdline[PROC_NAME_MAX] = {0, };
329 char status[PROC_STATUS_CNT][PROC_BUF_MAX];
331 memset(status, 0x0, sizeof(status));
333 if (STC_ERROR_NONE == proc_get_cmdline(pid, cmdline) &&
334 STC_ERROR_NONE == proc_get_status(pid, status)) {
336 if (__check_excn(cmdline)) {
338 STC_LOGD("[%s] monitoring is excepted", cmdline);
346 key = MALLOC0(proc_key_s, 1);
348 STC_LOGE("memory allocation failed");
352 value = MALLOC0(proc_value_s, 1);
354 STC_LOGE("memory allocation failed");
360 for (i = 0; i < PROC_STATUS_CNT; ++i)
361 g_strlcpy(value->status[i], status[i], sizeof(value->status[i]));
362 g_strlcpy(value->cmdline, cmdline, sizeof(value->cmdline));
365 STC_LOGD("\033[1;32mEXEC\033[0;m: tgid[\033[1;33m%d\033[0;m] ppid=[\033[1;35m%s\033[0;m] "
366 "cmdline[\033[0;34m%s\033[0;m] pid[%d]", tgid, status[PROC_STATUS_PPID], cmdline, pid);
367 STC_LOGD("STATUS: tgid[%s] pid[%s] ppid[%s] name[%s] state[%s] tracerpid[%s]",
368 status[PROC_STATUS_TGID], status[PROC_STATUS_PID], status[PROC_STATUS_PPID],
369 status[PROC_STATUS_NAME], status[PROC_STATUS_STATE], status[PROC_STATUS_TRACERPID]);
372 __proc_tree_add(key, value);
376 static void __process_event_exit(int tgid, int pid, int exit_code)
379 proc_value_s *lookup;
385 lookup = __proc_tree_lookup(&key);
386 if (lookup == NULL) /* unmonitored process */
390 STC_LOGD("\033[1;31mEXIT\033[0;m: tgid[\033[1;33m%d\033[0;m] "
391 "pid[%d] exitcode[\033[0;31m%d\033[0;m]", tgid, pid, exit_code);
393 __proc_tree_remove(&key);
396 static gboolean __process_nl_connector_message(GIOChannel *source,
397 GIOCondition condition,
401 int sock = g_io_channel_unix_get_fd(source);
402 nl_connector_proc_event_s msg;
404 if ((condition & G_IO_ERR) || (condition & G_IO_HUP) ||
405 (condition & G_IO_NVAL)) {
406 /* G_IO_ERR/G_IO_HUP/G_IO_NVAL received */
408 STC_LOGE("Netlink Connector socket received G_IO event, closing"
409 " socket. G_IO_ERR [%u], G_IO_HUP [%u], G_IO_NVAL [%u]",
410 (condition & G_IO_ERR), (condition & G_IO_HUP),
411 (condition & G_IO_NVAL));
412 __reopen_nl_connector_sock();
413 __STC_LOG_FUNC_EXIT__;
417 memset(&msg, 0, sizeof(nl_connector_proc_event_s));
419 ret = read(sock, &msg, sizeof(nl_connector_proc_event_s));
421 __STC_LOG_FUNC_EXIT__;
425 switch (msg.proc_ev.what) {
426 case PROC_EVENT_FORK:
427 __process_event_fork(msg.proc_ev.event_data.fork.child_tgid,
428 msg.proc_ev.event_data.fork.child_pid);
430 case PROC_EVENT_EXEC:
431 __process_event_exec(msg.proc_ev.event_data.exec.process_tgid,
432 msg.proc_ev.event_data.exec.process_pid);
434 case PROC_EVENT_EXIT:
435 __process_event_exit(msg.proc_ev.event_data.exit.process_tgid,
436 msg.proc_ev.event_data.exit.process_pid,
437 msg.proc_ev.event_data.exit.exit_code);
446 static int __subscribe_proc_events(void)
448 __STC_LOG_FUNC_ENTER__;
449 nl_connector_msg_s msg;
451 int sock = nl_connector_sock;
454 __STC_LOG_FUNC_EXIT__;
458 memset(&msg, 0, sizeof(nl_connector_msg_s));
460 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
461 msg.nl_hdr.nlmsg_pid = getpid();
462 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
464 msg.cn_msg.id.idx = CN_IDX_PROC;
465 msg.cn_msg.id.val = CN_VAL_PROC;
466 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
468 msg.cn_mcast = PROC_CN_MCAST_LISTEN;
470 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
472 STC_LOGE("Error sending netlink connector message");
473 __STC_LOG_FUNC_EXIT__;
477 __STC_LOG_FUNC_EXIT__;
481 static int __unsubscribe_proc_events(void)
483 __STC_LOG_FUNC_ENTER__;
484 nl_connector_msg_s msg;
486 int sock = nl_connector_sock;
489 __STC_LOG_FUNC_EXIT__;
493 memset(&msg, 0, sizeof(nl_connector_msg_s));
495 msg.nl_hdr.nlmsg_len = sizeof(nl_connector_msg_s);
496 msg.nl_hdr.nlmsg_pid = getpid();
497 msg.nl_hdr.nlmsg_type = NLMSG_DONE;
499 msg.cn_msg.id.idx = CN_IDX_PROC;
500 msg.cn_msg.id.val = CN_VAL_PROC;
501 msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
503 msg.cn_mcast = PROC_CN_MCAST_IGNORE;
505 ret = send(sock, &msg, sizeof(nl_connector_msg_s), 0);
507 STC_LOGE("Error sending netlink connector message");
508 __STC_LOG_FUNC_EXIT__;
512 __STC_LOG_FUNC_EXIT__;
516 int stc_plugin_procfs_initialize(void)
518 __STC_LOG_FUNC_ENTER__;
520 proc_tree = g_tree_new_full(__proc_tree_key_compare, NULL,
521 __proc_tree_key_free,
522 __proc_tree_value_free);
524 /* TODO: Fill proc tree with current procfs state */
526 __open_nl_connector_sock();
527 __subscribe_proc_events();
528 __STC_LOG_FUNC_EXIT__;
529 return STC_ERROR_NONE;
532 int stc_plugin_procfs_deinitialize(void)
534 __STC_LOG_FUNC_ENTER__;
536 if (nl_connector_sock == -1) {
537 STC_LOGD("socket already closed");
538 return STC_ERROR_NONE;
541 __unsubscribe_proc_events();
542 __close_nl_connector_sock();
544 g_tree_destroy(proc_tree);
547 __STC_LOG_FUNC_EXIT__;
548 return STC_ERROR_NONE;
551 stc_error_e stc_plugin_procfs_status_changed(stc_cmd_type_e cmd,
552 pid_t pid, const gchar *app_id, const gchar *pkg_id, stc_app_type_e app_type)
554 stc_error_e ret = STC_ERROR_NONE;
556 if ((pkg_id && app_id) && STC_DEBUG_LOG)
557 STC_LOGD("cmd [%d] pkgid [%s] appid [%s] pid[%d] type [%d]",
558 cmd, pkg_id, app_id, pid, app_type);
561 case STC_CMD_SET_FOREGRD:
563 stc_app_key_s app_key;
564 stc_app_value_s app_value;
565 stc_process_key_s proc_key;
566 stc_process_value_s proc_value;
568 memset(&app_key, 0, sizeof(stc_app_key_s));
569 memset(&app_value, 0, sizeof(stc_app_value_s));
570 memset(&proc_key, 0, sizeof(stc_process_key_s));
571 memset(&proc_value, 0, sizeof(stc_process_value_s));
573 app_key.pkg_id = g_strdup(pkg_id);
574 app_key.app_id = g_strdup(app_id);
576 app_value.type = app_type;
577 app_value.processes = NULL;
581 proc_value.ground = STC_APP_STATE_FOREGROUND;
583 stc_monitor_application_add(app_key, app_value);
584 stc_monitor_process_add(app_key, proc_key, proc_value);
585 stc_monitor_process_update_ground(app_key, proc_key,
586 STC_APP_STATE_FOREGROUND);
588 FREE(app_key.pkg_id);
589 FREE(app_key.app_id);
592 case STC_CMD_SET_BACKGRD:
594 stc_app_key_s app_key;
595 stc_app_value_s app_value;
596 stc_process_key_s proc_key;
597 stc_process_value_s proc_value;
599 memset(&app_key, 0, sizeof(stc_app_key_s));
600 memset(&app_value, 0, sizeof(stc_app_value_s));
601 memset(&proc_key, 0, sizeof(stc_process_key_s));
602 memset(&proc_value, 0, sizeof(stc_process_value_s));
604 app_key.pkg_id = g_strdup(pkg_id);
605 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
608 app_value.type = app_type;
609 app_value.processes = NULL;
613 proc_value.ground = STC_APP_STATE_BACKGROUND;
615 stc_monitor_application_add(app_key, app_value);
616 stc_monitor_process_add(app_key, proc_key, proc_value);
617 stc_monitor_process_update_ground(app_key, proc_key,
618 STC_APP_STATE_BACKGROUND);
620 FREE(app_key.pkg_id);
621 FREE(app_key.app_id);
624 case STC_CMD_SET_SERVICE_LAUNCHED:
626 stc_app_key_s app_key;
627 stc_app_value_s app_value;
628 stc_process_key_s proc_key;
629 stc_process_value_s proc_value;
631 memset(&app_key, 0, sizeof(stc_app_key_s));
632 memset(&app_value, 0, sizeof(stc_app_value_s));
633 memset(&proc_key, 0, sizeof(stc_process_key_s));
634 memset(&proc_value, 0, sizeof(stc_process_value_s));
636 app_key.pkg_id = g_strdup(pkg_id);
637 app_key.app_id = g_strconcat(app_id, STC_BACKGROUND_APP_SUFFIX,
640 app_value.type = app_type;
641 app_value.processes = NULL;
645 /* services will run always in background. */
646 proc_value.ground = STC_APP_STATE_BACKGROUND;
648 stc_monitor_application_add(app_key, app_value);
649 stc_monitor_process_add(app_key, proc_key, proc_value);
651 FREE(app_key.pkg_id);
652 g_free(app_key.app_id);
655 case STC_CMD_SET_TERMINATED:
657 stc_monitor_process_remove(pid);
661 STC_LOGE("Unhandled command");
662 ret = STC_ERROR_INVALID_PARAMETER;
668 API stc_plugin_procfs_s stc_plugin_procfs = {
670 stc_plugin_procfs_initialize,
671 .deinitialize_plugin =
672 stc_plugin_procfs_deinitialize,
673 .procfs_status_changed =
674 stc_plugin_procfs_status_changed