Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/guacd/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <guacamole/mem.h>
#include <guacamole/parser.h>
#include <guacamole/plugin.h>
#include <guacamole/proctitle.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
Expand Down Expand Up @@ -106,6 +107,10 @@ static int __write_all(int fd, char* buffer, int length) {
*/
static void* guacd_connection_write_thread(void* data) {

/* Thread name conn-write: forwards data from the connected user to the
* connection's child process. */
guac_thread_name_set("conn-write");

guacd_connection_io_thread_params* params = (guacd_connection_io_thread_params*) data;
char buffer[8192];

Expand All @@ -132,6 +137,10 @@ static void* guacd_connection_write_thread(void* data) {

void* guacd_connection_io_thread(void* data) {

/* Thread name conn-read: forwards data from the connection's child
* process back to the connected user. */
guac_thread_name_set("conn-read");

guacd_connection_io_thread_params* params = (guacd_connection_io_thread_params*) data;
char buffer[8192];

Expand Down Expand Up @@ -372,6 +381,10 @@ static int guacd_route_connection(guacd_proc_map* map, guac_socket* socket) {

void* guacd_connection_thread(void* data) {

/* Thread name conn-route: performs the protocol handshake for a new
* client connection and routes it to a connection process. */
guac_thread_name_set("conn-route");

guacd_connection_thread_params* params = (guacd_connection_thread_params*) data;

guacd_proc_map* map = params->map;
Expand Down Expand Up @@ -409,4 +422,3 @@ void* guacd_connection_thread(void* data) {
return NULL;

}

4 changes: 3 additions & 1 deletion src/guacd/daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "proc-map.h"

#include <guacamole/mem.h>
#include <guacamole/proctitle.h>

#ifdef ENABLE_SSL
#include <openssl/ssl.h>
Expand Down Expand Up @@ -296,6 +297,8 @@ static void stop_process_callback(guacd_proc* proc, void* data) {

int main(int argc, char* argv[]) {

guac_process_title_init(argc, argv);

/* Server */
int socket_fd;
struct addrinfo* addresses;
Expand Down Expand Up @@ -614,4 +617,3 @@ int main(int argc, char* argv[]) {
return 0;

}

14 changes: 14 additions & 0 deletions src/guacd/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <guacamole/mem.h>
#include <guacamole/parser.h>
#include <guacamole/plugin.h>
#include <guacamole/proctitle.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
Expand Down Expand Up @@ -80,6 +81,10 @@ typedef struct guacd_user_thread_params {
*/
static void* guacd_user_thread(void* data) {

/* Thread name user-conn: manages a single user's connection lifecycle
* from handshake through disconnect. */
guac_thread_name_set("user-conn");

guacd_user_thread_params* params = (guacd_user_thread_params*) data;
guacd_proc* proc = params->proc;
guac_client* client = proc->client;
Expand Down Expand Up @@ -213,6 +218,10 @@ typedef struct guacd_client_free {
*/
static void* guacd_client_free_thread(void* data) {

/* Thread name client-free: frees a guac_client in the background,
* bounded by a timeout in case the free handler hangs. */
guac_thread_name_set("client-free");

guacd_client_free* free_operation = (guacd_client_free*) data;

/* Attempt to free client (this may never return if the client is
Expand Down Expand Up @@ -326,6 +335,11 @@ static void guacd_exec_proc(guacd_proc* proc, const char* protocol) {

int result = 1;

/* Label the new child process. guac_process_title_set() also writes the
* main thread's comm, which is the current thread here, so a separate
* guac_thread_name_set() call would just duplicate the work. */
guac_process_title_set(protocol);

/* Set process group ID to match PID */
if (setpgid(0, 0)) {
guacd_log(GUAC_LOG_ERROR, "Cannot set PGID for connection process: %s",
Expand Down
3 changes: 2 additions & 1 deletion src/libguac/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ libguacinc_HEADERS = \
guacamole/plugin.h \
guacamole/pool.h \
guacamole/pool-types.h \
guacamole/proctitle.h \
guacamole/protocol.h \
guacamole/protocol-constants.h \
guacamole/protocol-types.h \
Expand Down Expand Up @@ -155,6 +156,7 @@ libguac_la_SOURCES = \
palette.c \
parser.c \
pool.c \
proctitle.c \
protocol.c \
raw_encoder.c \
recording.c \
Expand Down Expand Up @@ -209,4 +211,3 @@ libguac_la_LDFLAGS = \
@VORBIS_LIBS@ \
@WEBP_LIBS@ \
@WINSOCK_LIBS@

5 changes: 5 additions & 0 deletions src/libguac/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "guacamole/layer.h"
#include "guacamole/plugin.h"
#include "guacamole/pool.h"
#include "guacamole/proctitle.h"
#include "guacamole/protocol.h"
#include "guacamole/rwlock.h"
#include "guacamole/socket.h"
Expand Down Expand Up @@ -240,6 +241,10 @@ static void guac_client_promote_pending_users(guac_client* client) {
*/
static void* guac_client_pending_users_thread(void* data) {

/* Thread name user-pending: periodically promotes pending users into
* the active connection. */
guac_thread_name_set("user-pending");

guac_client* client = (guac_client*) data;

while (client->state == GUAC_CLIENT_RUNNING) {
Expand Down
5 changes: 5 additions & 0 deletions src/libguac/display-render-thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "guacamole/display.h"
#include "guacamole/flag.h"
#include "guacamole/mem.h"
#include "guacamole/proctitle.h"
#include "guacamole/timestamp.h"

/**
Expand Down Expand Up @@ -62,6 +63,10 @@
*/
static void* guac_display_render_loop(void* data) {

/* Thread name display-render: drives the display render loop, flushing
* completed frames to the client. */
guac_thread_name_set("display-render");

guac_display_render_thread* render_thread = (guac_display_render_thread*) data;
guac_display* display = render_thread->display;
guac_client* client = display->client;
Expand Down
5 changes: 5 additions & 0 deletions src/libguac/display-worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "guacamole/display.h"
#include "guacamole/fifo.h"
#include "guacamole/layer.h"
#include "guacamole/proctitle.h"
#include "guacamole/protocol-types.h"
#include "guacamole/protocol.h"
#include "guacamole/rect.h"
Expand Down Expand Up @@ -293,6 +294,10 @@ static int LFR_guac_display_layer_should_use_webp(guac_display_layer* layer,

void* guac_display_worker_thread(void* data) {

/* Thread name display-wrk: one worker in the display pool; encodes and
* sends graphical updates for dirty layer regions. */
guac_thread_name_set("display-wrk");

int framerate;
int has_outstanding_frames = 0;

Expand Down
167 changes: 167 additions & 0 deletions src/libguac/guacamole/proctitle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef _GUAC_PROCTITLE_H
#define _GUAC_PROCTITLE_H

/**
* @file proctitle.h
*
* These functions allow guacd and its per-connection child processes to
* show meaningful process and thread names in tools such as `ps`, `top`,
* `htop`, and `gdb`. Process titles can be updated at runtime to identify
* the active connection (for example, `vnc user@example.com:5900`), while
* worker threads can be assigned short descriptive names (for example,
* `display-wrk` or `user-input`).
*
* Process titles are visible through normal process-listing interfaces and
* should be treated as local-observable metadata. Callers must not include
* passwords, tokens, keys, or other secrets.
*
* Process title support operates on a single argv/environ memory region
* captured once at process startup by guac_process_title_init(). The captured
* buffer address and size are stored as process-wide static state and
* reused by all subsequent title updates. Writes are serialized with a
* static mutex so callers can invoke from any thread.
*
* Linux exposes argv+environ as a contiguous block whose contents are
* returned through `/proc/<pid>/cmdline`. Process title updates work by:
* - Capturing that block's address and length at startup.
* - Moving `environ` to a heap copy so `getenv()` continues to work
* after the original storage is overwritten.
* - Reusing the captured block as a writable title buffer, NUL-padding
* any unused bytes.
*
* Thread names are exposed through Linux's thread `comm` mechanism:
* - `prctl(PR_SET_NAME)` updates the calling thread's name
* (15 characters plus NUL). Used by guac_thread_name_set().
*
* - Writing `/proc/self/task/<tid>/comm` updates the name of any
* thread in the process. guac_process_title_set() uses this to update
* the main thread so process-oriented tools such as `top` and
* `ps -e` display the active connection.
*
* --- When to call ---
*
* guac_process_title_init:
* Call once from main() before creating threads and before any
* code modifies `environ`.
*
* guac_process_title_set:
* Call any time after initialization. Typically used once per
* child process after the connection type and target are known.
*
* guac_thread_name_set:
* Call at thread startup to assign a descriptive worker name.
*/

/**
* Recommended buffer size for formatting a process title before passing it
* to guac_process_title_set(). Fits a protocol name, user, host, and port in
* typical deployments.
*/
#define GUAC_PROCESS_TITLE_BUFSIZE 256

/**
* Initializes process title support by capturing the writable argv/environ
* region and moving `environ` to the heap. Must be called from main()
* before any other thread starts and before anything else reuses argv or
* environ. Safe to call more than once (subsequent calls no-op) and safe
* with bad args (no-op).
*
* If the heap environ copy fails to allocate, leaves environ in place and
* disables cmdline updates: later guac_process_title_set() calls still update
* the main-thread comm.
*
* @param argc
* The argument count as passed to main().
*
* @param argv
* The argument vector as passed to main().
*/
void guac_process_title_init(int argc, char** argv);

/**
* Updates the process title: both /proc/<pid>/cmdline (the COMMAND column
* in `ps`/`htop`) and the *main thread's* short comm (visible in `top`,
* `ps -e`). The calling thread's own comm is left alone: use
* guac_thread_name_set() for that.
*
* If guac_process_title_init() was not called successfully, the cmdline part
* is skipped and only the comm write is attempted.
*
* @param title
* The new title. Truncated to fit the captured argv region (cmdline)
* and to 15 chars for the main thread comm. NULL no-ops.
*/
void guac_process_title_set(const char* title);

/**
* Convenience wrapper around guac_process_title_set() that formats a network
* connection title in the form "<protocol> [user@]host[:port]". Falls back to
* "unknown-host" when host is NULL or empty, and omits the "user@" or ":port"
* portion when the respective argument is NULL or empty. The port is taken as
* a string so callers with either a numeric or named port can use it; numeric
* ports should be converted with guac_itoa_safe() first.
*
* The username is partially masked before display (e.g. "bbennett" becomes
* "bb****tt") so the full target account name is not exposed in world-readable
* process listings. Short or non-printable-ASCII usernames are masked in full.
* This is obfuscation to reduce casual disclosure, not an access control.
*
* @param protocol
* Short protocol label, e.g. "vnc", "rdp", "ssh". A NULL value no-ops.
*
* @param user
* The connecting username, or NULL/empty to omit the "user@" portion. The
* value is partially masked before it appears in the process title.
*
* @param host
* The target hostname, or NULL/empty to substitute "unknown-host".
*
* @param port
* The target port as a string, or NULL/empty to omit the ":port" portion.
*/
void guac_process_title_set_endpoint(const char* protocol, const char* user,
const char* host, const char* port);

/**
* Sets the *calling* thread's short name (visible in
* /proc/<pid>/task/<tid>/comm, `top -H`, `ps -L`, ...)
* via prctl(PR_SET_NAME).
*
* Thread-safe: the underlying prctl targets only the calling thread.
*
* Note: threads spawned later by the calling thread inherit
* this name as their initial comm. If you call into a library
* that creates its own workers, those workers will appear with
* the parent's name until they rename themselves.
*
* Each call site is preceded by a comment of the form
* "Thread name <name>: <description>". To list every named thread
* and what it does, search the source. The description may wrap onto
* the following line, so include one line of trailing context:
*
* grep -rn -A1 "Thread name " src/
*
* @param name
* The new thread name. Truncated to 15 chars + NULL.
*/
void guac_thread_name_set(const char* name);

#endif
Loading
Loading