]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
lib:replace: Implement setproctitle() based on PRCTL_MM_MAP
authorAndreas Schneider <asn@samba.org>
Fri, 17 Oct 2025 08:16:30 +0000 (10:16 +0200)
committerAndreas Schneider <asn@cryptomilk.org>
Wed, 22 Oct 2025 08:03:35 +0000 (08:03 +0000)
This should work on Linux. It requires CAP_SYS_RESOURCE, but our daemons
run as root anyway.

Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Martin Schwenke <martin@meltin.net>
Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
Autobuild-Date(master): Wed Oct 22 08:03:35 UTC 2025 on atb-devel-224

lib/replace/replace.c

index 7c7c70450ea68060cae3c3c579bc318bb219e579..e8ff990832263d43588c3cdc2a7e88380ae12c9c 100644 (file)
 #include <sys/syscall.h>
 #endif
 
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
+
 #ifdef _WIN32
 #define mkdir(d,m) _mkdir(d)
 #endif
@@ -944,8 +948,183 @@ int rep_usleep(useconds_t sec)
 #ifndef HAVE_SETPROCTITLE
 void rep_setproctitle(const char *fmt, ...)
 {
+#if defined(HAVE_PRCTL) && defined(PR_SET_MM_MAP) && defined(__NR_brk)
+       /*
+        * Implementation based on setproctitle from lcx under LGPL-2.1+
+        * https://github.com/lxc/lxc/
+        *
+        * Using PR_SET_MM_MAP requires CAP_SYS_RESOURCE.
+        */
+       static char *proctitle = NULL;
+       char *tmp_proctitle = NULL;
+       char buf[2048] = {0};
+       char title[2048] = {0};
+       va_list ap;
+       char *ptr = NULL;
+       FILE *f = NULL;
+       size_t title_len;
+       int ret = 0;
+       struct prctl_mm_map prctl_map = {
+               .exe_fd = -1,
+       };
+       long brk_val = 0;
+       /* See `man proc_pid_stat.5` */
+       unsigned long start_code = 0;            // 26
+       unsigned long end_code = 0;              // 27
+       unsigned long start_stack = 0;           // 28
+
+       unsigned long start_data = 0;            // 45
+       unsigned long end_data = 0;              // 46
+       unsigned long start_brk = 0;             // 47
+       unsigned long arg_start = 0;             // 48
+       unsigned long arg_end = 0;               // 49
+       unsigned long env_start = 0;             // 50
+       unsigned long env_end = 0;               // 51
+
+       f = fopen("/proc/self/stat", "r");
+       if (f == NULL) {
+               return;
+       }
+
+       ptr = fgets(buf, sizeof(buf), f);
+       fclose(f);
+       if (ptr == NULL) {
+               return;
+       }
+
+       /*
+        * Find the last ')' to skip the comm field which can contain spaces and
+        * maybe ')'.
+        */
+       ptr = strrchr(buf, ')');
+       if (ptr == NULL || ptr[1] == '\0') {
+               return;
+       }
+       ptr += 2; // Skip ') '
+
+       /* See `man proc_pid_stat.5` */
+       ret = sscanf(
+               ptr,
+               "%*c "        // 3 (state)
+               "%*d "        // 4 (ppid)
+               "%*d "        // 5 (pgrp)
+               "%*d "        // 6 (session)
+               "%*d "        // 7 (tty_nr)
+               "%*d "        // 8 (tpgid)
+               "%*u "        // 9 (flags)
+               "%*u "        // 10 (minflt)
+               "%*u "        // 11 (cminflt)
+               "%*u "        // 12 (majflt)
+               "%*u "        // 13 (cmajflt)
+               "%*u "        // 14 (utime)
+               "%*u "        // 15 (stime)
+               "%*d "        // 16 (cutime)
+               "%*d "        // 17 (cstime)
+               "%*d "        // 18 (priority)
+               "%*d "        // 19 (nice)
+               "%*d "        // 20 (num_threads)
+               "%*d "        // 21 (itrealvalue)
+               "%*u "        // 22 (starttime)
+               "%*u "        // 23 (vsize)
+               "%*d "        // 24 (rss)
+               "%*u "        // 25 (rsslim)
+               "%lu "        // 26 (start_code)
+               "%lu "        // 27 (end_code)
+               "%lu "        // 28 (start_stack)
+               "%*u "        // 29 (kstkesp)
+               "%*u "        // 30 (kstkeip)
+               "%*u "        // 31 (signal)
+               "%*u "        // 32 (blocked)
+               "%*u "        // 33 (sigignore)
+               "%*u "        // 34 (sigcatch)
+               "%*u "        // 35 (wchan)
+               "%*u "        // 36 (nswap)
+               "%*u "        // 37 (cnswap)
+               "%*d "        // 38 (exit_signal)
+               "%*d "        // 39 (processor)
+               "%*d "        // 40 (rt_priority)
+               "%*u "        // 41 (policy)
+               "%*u "        // 42 (delayacct_blkio_ticks)
+               "%*u "        // 43 (guest_time)
+               "%*d "        // 44 (cguest_time)
+               "%lu "        // 45 (start_data)
+               "%lu "        // 46 (end_data)
+               "%lu "        // 47 (start_brk)
+               "%lu "        // 48 (arg_start)
+               "%lu "        // 49 (arg_end)
+               "%lu "        // 50 (env_start)
+               "%lu",        // 51 (env_end)
+               &start_code,  // 26
+               &end_code,    // 27
+               &start_stack, // 28
+               &start_data,  // 45
+               &end_data,    // 46
+               &start_brk,   // 47
+               &arg_start,   // 48
+               &arg_end,     // 49
+               &env_start,   // 50
+               &env_end      // 51
+       );
+       if (ret != 10) {
+               return;
+       }
+
+       va_start(ap, fmt);
+       ret = vsnprintf(title, sizeof(title), fmt, ap);
+       va_end(ap);
+       if (ret <= 0) {
+               return;
+       }
+       /*
+        * Include the null byte here, because in the calculations below
+        * we want to have room for it.
+        */
+       title_len = ret + 1;
+
+       /* This will leak memory */
+       tmp_proctitle = realloc(proctitle, title_len);
+       if (tmp_proctitle == NULL) {
+               return;
+       }
+       proctitle = tmp_proctitle;
+
+       arg_start = (uint64_t)proctitle;
+       arg_end = arg_start + title_len;
+
+       brk_val = syscall(__NR_brk, 0);
+       if (brk_val < 0) {
+               return;
+       }
+
+       prctl_map = (struct prctl_mm_map) {
+               .start_code = start_code,
+               .end_code = end_code,
+               .start_stack = start_stack,
+               .start_data = start_data,
+               .end_data = end_data,
+               .start_brk = start_brk,
+               .brk = brk_val,
+               .arg_start = arg_start,
+               .arg_end = arg_end,
+               .env_start = env_start,
+               .env_end = env_end,
+               .auxv = NULL,
+               .auxv_size = 0,
+               .exe_fd = -1,
+       };
+
+       ret = prctl(PR_SET_MM,
+                   PR_SET_MM_MAP,
+                   (long)&prctl_map,
+                   sizeof(prctl_map),
+                   0);
+       if (ret == 0) {
+               strlcpy((char *)arg_start, title, title_len);
+       }
+#endif /* HAVE_PRCTL */
 }
 #endif
+
 #ifndef HAVE_SETPROCTITLE_INIT
 void rep_setproctitle_init(int argc, char *argv[], char *envp[])
 {