From c0f6fb054d2f16518d047a6adf7e8aa81eff5403 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Fri, 20 Oct 2023 15:20:45 -0400 Subject: [PATCH] Introduce LTTNG_UST_APP_PATH environment variable Introduce an environment to specify a path under which unix sockets used for the communication between the application (tracee) instrumented with `liblttng-ust` and the LTTng session and consumer daemons (part of the LTTng-tools project) are located. When `$LTTNG_UST_APP_PATH` is specified, only this path is considered for connecting to a session daemon. Setting this environment variable disables connection to root and per-user session daemons. The `$LTTNG_UST_APP_PATH` target directory must exist and be accessible by the user before the application is executed for tracing to work. This environment variable affects the Java and Python agents in the same way. This environment variable on the LTTng-UST application side is meant to be used with a new LTTNG_UST_CTL_PATH on the lttng sessiond side. Signed-off-by: Mathieu Desnoyers Change-Id: I4784f4565514a9771827603bd0bebabbeb37a7ad --- doc/man/lttng-ust.3.txt | 19 ++- src/common/getenv.c | 1 + .../agent/client/LttngTcpSessiondClient.java | 47 ++++-- src/lib/lttng-ust/lttng-ust-comm.c | 152 +++++++++++++++++- src/python-lttngust/lttngust/agent.py | 34 +++- 5 files changed, 218 insertions(+), 35 deletions(-) diff --git a/doc/man/lttng-ust.3.txt b/doc/man/lttng-ust.3.txt index ad1fbbf4..08138896 100644 --- a/doc/man/lttng-ust.3.txt +++ b/doc/man/lttng-ust.3.txt @@ -1482,15 +1482,22 @@ int main(int argc, char* argv[]) ENVIRONMENT VARIABLES --------------------- +`LTTNG_UST_APP_PATH`:: + Path under which unix sockets used for the communication between + the application (tracee) instrumented with `liblttng-ust` and the + LTTng session and consumer daemons (part of the LTTng-tools project) + are located. When `$LTTNG_UST_APP_PATH` is specified, only this path + is considered for connecting to a session daemon. The + `$LTTNG_UST_APP_PATH` target directory must exist and be accessible + by the user before the application is executed for tracing to work. + Setting this environment variable disables connection to root and + per-user session daemons. + `LTTNG_HOME`:: Alternative user's home directory. This variable is useful when the user running the instrumented application has a non-writable home - directory. -+ -Unix sockets used for the communication between `liblttng-ust` and the -LTTng session and consumer daemons (part of the LTTng-tools project) -are located in a specific directory under `$LTTNG_HOME` (or `$HOME` if -`$LTTNG_HOME` is not set). + directory. This path is where unix sockets for communication with + the per-user session daemon are located. `LTTNG_UST_ALLOW_BLOCKING`:: If set, allow the application to retry event tracing when there's diff --git a/src/common/getenv.c b/src/common/getenv.c index 55e6ad7c..7f7b8534 100644 --- a/src/common/getenv.c +++ b/src/common/getenv.c @@ -49,6 +49,7 @@ static struct lttng_env lttng_env[] = { { "LTTNG_UST_ALLOW_BLOCKING", LTTNG_ENV_SECURE, NULL, }, { "HOME", LTTNG_ENV_SECURE, NULL, }, { "LTTNG_HOME", LTTNG_ENV_SECURE, NULL, }, + { "LTTNG_UST_APP_PATH", LTTNG_ENV_SECURE, NULL, }, }; static diff --git a/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java b/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java index cb84087a..95376044 100644 --- a/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java +++ b/src/lib/lttng-ust-java-agent/java/lttng-ust-agent-common/org/lttng/ust/agent/client/LttngTcpSessiondClient.java @@ -170,20 +170,32 @@ public class LttngTcpSessiondClient implements Runnable { } private void connectToSessiond() throws IOException { - int rootPort = getPortFromFile(ROOT_PORT_FILE); - int userPort = getPortFromFile(getHomePath() + USER_PORT_FILE); + int portToUse; /* - * Check for the edge case of both files existing but pointing to the - * same port. In this case, let the root client handle it. + * The environment variable LTTNG_UST_APP_PATH disables + * connection to per-user and root session daemons. */ - if ((rootPort != 0) && (rootPort == userPort) && (!isRoot)) { - log("User and root config files both point to port " + rootPort + - ". Letting the root client handle it."); - throw new IOException(); - } + String lttngUstAppPath = getUstAppPath(); + + if (lttngUstAppPath != null) { + portToUse = getPortFromFile(lttngUstAppPath + USER_PORT_FILE); + } else { + int rootPort = getPortFromFile(ROOT_PORT_FILE); + int userPort = getPortFromFile(getHomePath() + USER_PORT_FILE); + + /* + * Check for the edge case of both files existing but pointing to the + * same port. In this case, let the root client handle it. + */ + if ((rootPort != 0) && (rootPort == userPort) && (!isRoot)) { + log("User and root config files both point to port " + rootPort + + ". Letting the root client handle it."); + throw new IOException(); + } - int portToUse = (isRoot ? rootPort : userPort); + portToUse = (isRoot ? rootPort : userPort); + } if (portToUse == 0) { /* No session daemon available. Stop and retry later. */ @@ -195,17 +207,20 @@ public class LttngTcpSessiondClient implements Runnable { this.outToSessiond = new DataOutputStream(sessiondSock.getOutputStream()); } + private static String getUstAppPath() { + return System.getenv("LTTNG_UST_APP_PATH"); + } + private static String getHomePath() { /* * The environment variable LTTNG_HOME overrides HOME if - * defined. + * set. */ - String homePath = System.getenv("LTTNG_HOME"); - - if (homePath == null) { - homePath = System.getProperty("user.home"); + String lttngHomePath = System.getenv("LTTNG_HOME"); + if (lttngHomePath != null) { + return lttngHomePath; } - return homePath; + return System.getProperty("user.home"); } /** diff --git a/src/lib/lttng-ust/lttng-ust-comm.c b/src/lib/lttng-ust/lttng-ust-comm.c index ccf2a9b8..229e457a 100644 --- a/src/lib/lttng-ust/lttng-ust-comm.c +++ b/src/lib/lttng-ust/lttng-ust-comm.c @@ -234,10 +234,10 @@ void ust_unlock(void) */ static sem_t constructor_wait; /* - * Doing this for both the global and local sessiond. + * Doing this for the ust_app, global and local sessiond. */ enum { - sem_count_initial_value = 4, + sem_count_initial_value = 6, }; static int sem_count = sem_count_initial_value; @@ -264,8 +264,15 @@ struct sock_info { int socket; int notify_socket; + /* + * If wait_shm_is_file is true, use standard open to open and + * create the shared memory used for waiting on session daemon. + * Otherwise, use shm_open to create this file. + */ + bool wait_shm_is_file; char wait_shm_path[PATH_MAX]; char *wait_shm_mmap; + /* Keep track of lazy state dump not performed yet. */ int statedump_pending; int initial_statedump_done; @@ -274,6 +281,26 @@ struct sock_info { }; /* Socket from app (connect) to session daemon (listen) for communication */ +static struct sock_info ust_app = { + .name = "ust_app", + .multi_user = true, + + .root_handle = -1, + .registration_done = 0, + .allowed = 0, + .thread_active = 0, + + .socket = -1, + .notify_socket = -1, + + .wait_shm_is_file = true, + + .statedump_pending = 0, + .initial_statedump_done = 0, + .procname[0] = '\0' +}; + + static struct sock_info global_apps = { .name = "global", .multi_user = true, @@ -287,6 +314,7 @@ static struct sock_info global_apps = { .socket = -1, .notify_socket = -1, + .wait_shm_is_file = false, .wait_shm_path = "/" LTTNG_UST_WAIT_FILENAME, .statedump_pending = 0, @@ -294,8 +322,6 @@ static struct sock_info global_apps = { .procname[0] = '\0' }; -/* TODO: allow global_apps_sock_path override */ - static struct sock_info local_apps = { .name = "local", .multi_user = false, @@ -307,6 +333,8 @@ static struct sock_info local_apps = { .socket = -1, .notify_socket = -1, + .wait_shm_is_file = false, + .statedump_pending = 0, .initial_statedump_done = 0, .procname[0] = '\0' @@ -383,6 +411,12 @@ const char *get_lttng_home_dir(void) return (const char *) lttng_ust_getenv("HOME"); } +static +const char *get_lttng_ust_app_path(void) +{ + return (const char *) lttng_ust_getenv("LTTNG_UST_APP_PATH"); +} + /* * Force a read (imply TLS allocation for dlopen) of TLS variables. */ @@ -480,12 +514,70 @@ void print_cmd(int cmd, int handle) lttng_ust_obj_get_name(handle), handle); } +static +int setup_ust_apps(void) +{ + const char *ust_app_path; + int ret = 0; + uid_t uid; + + assert(!ust_app.wait_shm_mmap); + + uid = getuid(); + /* + * Disallow ust apps tracing for setuid binaries, because we + * cannot use the environment variables anyway. + */ + if (uid != geteuid()) { + DBG("UST app tracing disabled for setuid binary."); + assert(ust_app.allowed == 0); + ret = 0; + goto end; + } + ust_app_path = get_lttng_ust_app_path(); + if (!ust_app_path) { + DBG("LTTNG_UST_APP_PATH environment variable not set."); + assert(ust_app.allowed == 0); + ret = -ENOENT; + goto end; + } + /* + * The LTTNG_UST_APP_PATH env. var. disables global and local + * sessiond connections. + */ + ust_app.allowed = 1; + snprintf(ust_app.sock_path, PATH_MAX, "%s/%s", + ust_app_path, LTTNG_UST_SOCK_FILENAME); + snprintf(ust_app.wait_shm_path, PATH_MAX, "%s/%s", + ust_app_path, + LTTNG_UST_WAIT_FILENAME); + + ust_app.wait_shm_mmap = get_map_shm(&ust_app); + if (!ust_app.wait_shm_mmap) { + WARN("Unable to get map shm for ust_app. Disabling LTTng-UST ust_app tracing."); + ust_app.allowed = 0; + ret = -EIO; + goto end; + } + + lttng_pthread_getname_np(ust_app.procname, LTTNG_UST_CONTEXT_PROCNAME_LEN); +end: + return ret; +} + static int setup_global_apps(void) { int ret = 0; assert(!global_apps.wait_shm_mmap); + /* + * The LTTNG_UST_APP_PATH env. var. disables global sessiond + * connections. + */ + if (ust_app.allowed) + return 0; + global_apps.wait_shm_mmap = get_map_shm(&global_apps); if (!global_apps.wait_shm_mmap) { WARN("Unable to get map shm for global apps. Disabling LTTng-UST global tracing."); @@ -499,6 +591,7 @@ int setup_global_apps(void) error: return ret; } + static int setup_local_apps(void) { @@ -508,6 +601,13 @@ int setup_local_apps(void) assert(!local_apps.wait_shm_mmap); + /* + * The LTTNG_UST_APP_PATH env. var. disables local sessiond + * connections. + */ + if (ust_app.allowed) + return 0; + uid = getuid(); /* * Disallow per-user tracing for setuid binaries. @@ -1520,6 +1620,15 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting) } } +static +int wait_shm_open(struct sock_info *sock_info, int flags, mode_t mode) +{ + if (sock_info->wait_shm_is_file) + return open(sock_info->wait_shm_path, flags, mode); + else + return shm_open(sock_info->wait_shm_path, flags, mode); +} + /* * Using fork to set umask in the child process (not multi-thread safe). * We deal with the shm_open vs ftruncate race (happening when the @@ -1538,7 +1647,7 @@ int get_wait_shm(struct sock_info *sock_info, size_t mmap_size) /* * Try to open read-only. */ - wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0); + wait_shm_fd = wait_shm_open(sock_info, O_RDONLY, 0); if (wait_shm_fd >= 0) { int32_t tmp_read; ssize_t len; @@ -1598,7 +1707,7 @@ open_write: /* * Try to open read-only again after creation. */ - wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0); + wait_shm_fd = wait_shm_open(sock_info, O_RDONLY, 0); if (wait_shm_fd < 0) { /* * Real-only open did not work. It's a failure @@ -1625,7 +1734,7 @@ open_write: * We don't do an exclusive open, because we allow other * processes to create+ftruncate it concurrently. */ - wait_shm_fd = shm_open(sock_info->wait_shm_path, + wait_shm_fd = wait_shm_open(sock_info, O_RDWR | O_CREAT, create_mode); if (wait_shm_fd >= 0) { ret = ftruncate(wait_shm_fd, mmap_size); @@ -2258,6 +2367,11 @@ void lttng_ust_ctor(void) PERROR("sem_init"); } + ret = setup_ust_apps(); + if (ret) { + assert(ust_app.allowed == 0); + DBG("ust_app setup returned %d", ret); + } ret = setup_global_apps(); if (ret) { assert(global_apps.allowed == 0); @@ -2290,6 +2404,19 @@ void lttng_ust_ctor(void) ERR("pthread_attr_setdetachstate: %s", strerror(ret)); } + if (ust_app.allowed) { + pthread_mutex_lock(&ust_exit_mutex); + ret = pthread_create(&ust_app.ust_listener, &thread_attr, + ust_listener_thread, &ust_app); + if (ret) { + ERR("pthread_create ust_app: %s", strerror(ret)); + } + ust_app.thread_active = 1; + pthread_mutex_unlock(&ust_exit_mutex); + } else { + handle_register_done(&ust_app); + } + if (global_apps.allowed) { pthread_mutex_lock(&ust_exit_mutex); ret = pthread_create(&global_apps.ust_listener, &thread_attr, @@ -2369,8 +2496,10 @@ void lttng_ust_ctor(void) static void lttng_ust_cleanup(int exiting) { + cleanup_sock_info(&ust_app, exiting); cleanup_sock_info(&global_apps, exiting); cleanup_sock_info(&local_apps, exiting); + ust_app.allowed = 0; local_apps.allowed = 0; global_apps.allowed = 0; /* @@ -2420,6 +2549,15 @@ void lttng_ust_exit(void) pthread_mutex_lock(&ust_exit_mutex); /* cancel threads */ + if (ust_app.thread_active) { + ret = pthread_cancel(ust_app.ust_listener); + if (ret) { + ERR("Error cancelling ust listener thread: %s", + strerror(ret)); + } else { + ust_app.thread_active = 0; + } + } if (global_apps.thread_active) { ret = pthread_cancel(global_apps.ust_listener); if (ret) { diff --git a/src/python-lttngust/lttngust/agent.py b/src/python-lttngust/lttngust/agent.py index 66dbbd5e..69caad69 100644 --- a/src/python-lttngust/lttngust/agent.py +++ b/src/python-lttngust/lttngust/agent.py @@ -266,6 +266,8 @@ def _get_port_from_file(path): return port +def _get_ust_app_path(): + return os.getenv('LTTNG_UST_APP_PATH') def _get_user_home_path(): # $LTTNG_HOME overrides $HOME if it exists @@ -298,15 +300,25 @@ def _init_threads(): 'lttng'.encode().decode() _initialized = True - sys_port = _get_port_from_file('/var/run/lttng/agent.port') - user_port_file = os.path.join(_get_user_home_path(), '.lttng', 'agent.port') - user_port = _get_port_from_file(user_port_file) + + # The LTTNG_UST_APP_PATH environment variables disables connections + # to the global and per-user session daemons. + if _get_ust_app_path() is not None: + ust_app_port_file = os.path.join(_get_ust_app_path(), 'agent.port') + ust_app_port = _get_port_from_file(ust_app_port_file) + sys_port = None + user_port = None + dbg._pdebug('ust_app session daemon port: {}'.format(ust_app_port)) + else: + sys_port = _get_port_from_file('/var/run/lttng/agent.port') + user_port_file = os.path.join(_get_user_home_path(), '.lttng', 'agent.port') + user_port = _get_port_from_file(user_port_file) + dbg._pdebug('system session daemon port: {}'.format(sys_port)) + dbg._pdebug('user session daemon port: {}'.format(user_port)) + reg_queue = queue.Queue() reg_expecting = 0 - dbg._pdebug('system session daemon port: {}'.format(sys_port)) - dbg._pdebug('user session daemon port: {}'.format(user_port)) - if sys_port == user_port and sys_port is not None: # The two session daemon ports are the same. This is not normal. # Connect to only one. @@ -314,6 +326,16 @@ def _init_threads(): sys_port = None try: + if ust_app_port is not None: + dbg._pdebug('creating ust_app client thread') + t = threading.Thread(target=_client_thread_target, + args=('ust_app', ust_app_port, reg_queue)) + t.name = 'ust_app' + t.daemon = True + t.start() + dbg._pdebug('created and started ust_app client thread') + reg_expecting += 1 + if sys_port is not None: dbg._pdebug('creating system client thread') t = threading.Thread(target=_client_thread_target, -- 2.39.5