diff --git a/Makefile.am b/Makefile.am index 115a07e89..2ebea8024 100644 --- a/Makefile.am +++ b/Makefile.am @@ -189,6 +189,7 @@ linux_platform_headers = \ generic/hostname.h \ generic/uname.h \ linux/CGroupUtils.h \ + linux/DockerMgr.h \ linux/Compat.h \ linux/GPU.h \ linux/HugePageMeter.h \ @@ -218,6 +219,7 @@ linux_platform_sources = \ generic/hostname.c \ generic/uname.c \ linux/CGroupUtils.c \ + linux/DockerMgr.c \ linux/Compat.c \ linux/GPU.c \ linux/HugePageMeter.c \ diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c index f352b8e2e..6b0e32297 100644 --- a/linux/CGroupUtils.c +++ b/linux/CGroupUtils.c @@ -515,7 +515,7 @@ char* CGroup_filterContainer(const char* cgroup) { } if (!s.pos) { - return xStrdup("/"); + return xStrdup(""); } s.buf = xCalloc(s.pos + 1, sizeof(char)); diff --git a/linux/DockerMgr.c b/linux/DockerMgr.c new file mode 100644 index 000000000..11dadd1df --- /dev/null +++ b/linux/DockerMgr.c @@ -0,0 +1,108 @@ +/* +htop - DockerMgr.c +Retrieves running Docker container names via the Docker API over the unix socket. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "linux/DockerMgr.h" + +#include +#include +#include +#include +#include +#include + +#include "Hashtable.h" +#include "XUtils.h" + +#define DOCKER_SOCKET "/var/run/docker.sock" +#define RESPONSE_SIZE 262144 +#define MAX_CONTAINER_NAME_LEN 25 + +/* ponytail: simplest possible cache; refresh by dropping cache on long IDs is out of scope */ +static Hashtable* nameCache = NULL; + +static unsigned int hashStr(const char* s) { + unsigned int h = 5381; + while (*s) + h = (h * 33) ^ (unsigned char)*s++; + return h; +} + +static Hashtable* getCache(void) { + if (!nameCache) + nameCache = Hashtable_new(16, true); + return nameCache; +} + +static char* queryName(const char* id) { + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return NULL; + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", DOCKER_SOCKET); + + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + close(fd); + return NULL; + } + + char req[256]; + int reqLen = snprintf(req, sizeof(req), + "GET /containers/%s/json HTTP/1.1\r\n" + "Host: localhost\r\n" + "Accept: application/json\r\n" + "Connection: close\r\n\r\n", id); + if (reqLen <= 0 || reqLen >= (int)sizeof(req) || write(fd, req, (size_t)reqLen) != reqLen) { + close(fd); + return NULL; + } + + char buf[RESPONSE_SIZE]; + size_t used = 0; + ssize_t rd; + while (used < sizeof(buf) - 1 && (rd = read(fd, buf + used, sizeof(buf) - 1 - used)) > 0) + used += (size_t)rd; + close(fd); + + if (used == 0) + return NULL; + buf[used] = '\0'; + + char* key = strstr(buf, "\"Name\":\""); + if (!key) + return NULL; + char* val = key + strlen("\"Name\":\""); + if (*val == '/') + val++; + char* end = strchr(val, '"'); + if (!end) + return NULL; + return xStrndup(val, (size_t)(end - val)); +} + +char* DockerMgr_getContainerName(const char* id) { + Hashtable* cache = getCache(); + ht_key_t key = hashStr(id); + + char* cached = Hashtable_get(cache, key); + if (cached) + return xStrdup(cached); + + char* name = queryName(id); + if (name) { + /* Limit the container name shown in the CONTAINER column to 25 characters */ + char* shortName = xStrndup(name, MAX_CONTAINER_NAME_LEN); + free(name); + Hashtable_put(cache, key, shortName); + return xStrdup(shortName); + } + return NULL; +} diff --git a/linux/DockerMgr.h b/linux/DockerMgr.h new file mode 100644 index 000000000..297a0b987 --- /dev/null +++ b/linux/DockerMgr.h @@ -0,0 +1,12 @@ +#ifndef HEADER_DockerMgr +#define HEADER_DockerMgr +/* +htop - DockerMgr.h +Retrieves running Docker container names via the Docker API over the unix socket. +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +char* DockerMgr_getContainerName(const char* id); + +#endif /* HEADER_DockerMgr */ diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index fc43baac3..06a8ddc10 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -40,6 +40,7 @@ in the source distribution for its full text. #include "Table.h" #include "UsersTable.h" #include "linux/CGroupUtils.h" +#include "linux/DockerMgr.h" #include "linux/Compat.h" #include "linux/GPU.h" #include "linux/LinuxMachine.h" @@ -1005,6 +1006,15 @@ static void LinuxProcessTable_readCGroupFile(LinuxProcess* process, openat_arg_t char* container_short = CGroup_filterContainer(process->cgroup); if (container_short) { + /* If this is a Docker container, try to resolve the real name via the Docker API */ + static const char* dockerPrefix = "!docker:"; + if (String_startsWith(container_short, dockerPrefix)) { + char* resolved = DockerMgr_getContainerName(container_short + strlen(dockerPrefix)); + if (resolved) { + free(container_short); + container_short = resolved; + } + } Row_updateFieldWidth(CONTAINER, strlen(container_short)); free_and_xStrdup(&process->container_short, container_short); free(container_short);