Skip to content

Commit ceb9109

Browse files
committed
lib:replace: Implement setproctitle() based on PRCTL_MM_MAP
This should work on Linux. It requires CAP_SYS_RESOURCE, but our daemons run as root anyway. Signed-off-by: Andreas Schneider <[email protected]> Reviewed-by: Martin Schwenke <[email protected]> Autobuild-User(master): Andreas Schneider <[email protected]> Autobuild-Date(master): Wed Oct 22 08:03:35 UTC 2025 on atb-devel-224
1 parent 298be05 commit ceb9109

File tree

1 file changed

+179
-0
lines changed

1 file changed

+179
-0
lines changed

lib/replace/replace.c

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
#include <sys/syscall.h>
3838
#endif
3939

40+
#ifdef HAVE_SYS_PRCTL_H
41+
#include <sys/prctl.h>
42+
#endif
43+
4044
#ifdef _WIN32
4145
#define mkdir(d,m) _mkdir(d)
4246
#endif
@@ -944,8 +948,183 @@ int rep_usleep(useconds_t sec)
944948
#ifndef HAVE_SETPROCTITLE
945949
void rep_setproctitle(const char *fmt, ...)
946950
{
951+
#if defined(HAVE_PRCTL) && defined(PR_SET_MM_MAP) && defined(__NR_brk)
952+
/*
953+
* Implementation based on setproctitle from lcx under LGPL-2.1+
954+
* https://github.com/lxc/lxc/
955+
*
956+
* Using PR_SET_MM_MAP requires CAP_SYS_RESOURCE.
957+
*/
958+
static char *proctitle = NULL;
959+
char *tmp_proctitle = NULL;
960+
char buf[2048] = {0};
961+
char title[2048] = {0};
962+
va_list ap;
963+
char *ptr = NULL;
964+
FILE *f = NULL;
965+
size_t title_len;
966+
int ret = 0;
967+
struct prctl_mm_map prctl_map = {
968+
.exe_fd = -1,
969+
};
970+
long brk_val = 0;
971+
/* See `man proc_pid_stat.5` */
972+
unsigned long start_code = 0; // 26
973+
unsigned long end_code = 0; // 27
974+
unsigned long start_stack = 0; // 28
975+
976+
unsigned long start_data = 0; // 45
977+
unsigned long end_data = 0; // 46
978+
unsigned long start_brk = 0; // 47
979+
unsigned long arg_start = 0; // 48
980+
unsigned long arg_end = 0; // 49
981+
unsigned long env_start = 0; // 50
982+
unsigned long env_end = 0; // 51
983+
984+
f = fopen("/proc/self/stat", "r");
985+
if (f == NULL) {
986+
return;
987+
}
988+
989+
ptr = fgets(buf, sizeof(buf), f);
990+
fclose(f);
991+
if (ptr == NULL) {
992+
return;
993+
}
994+
995+
/*
996+
* Find the last ')' to skip the comm field which can contain spaces and
997+
* maybe ')'.
998+
*/
999+
ptr = strrchr(buf, ')');
1000+
if (ptr == NULL || ptr[1] == '\0') {
1001+
return;
1002+
}
1003+
ptr += 2; // Skip ') '
1004+
1005+
/* See `man proc_pid_stat.5` */
1006+
ret = sscanf(
1007+
ptr,
1008+
"%*c " // 3 (state)
1009+
"%*d " // 4 (ppid)
1010+
"%*d " // 5 (pgrp)
1011+
"%*d " // 6 (session)
1012+
"%*d " // 7 (tty_nr)
1013+
"%*d " // 8 (tpgid)
1014+
"%*u " // 9 (flags)
1015+
"%*u " // 10 (minflt)
1016+
"%*u " // 11 (cminflt)
1017+
"%*u " // 12 (majflt)
1018+
"%*u " // 13 (cmajflt)
1019+
"%*u " // 14 (utime)
1020+
"%*u " // 15 (stime)
1021+
"%*d " // 16 (cutime)
1022+
"%*d " // 17 (cstime)
1023+
"%*d " // 18 (priority)
1024+
"%*d " // 19 (nice)
1025+
"%*d " // 20 (num_threads)
1026+
"%*d " // 21 (itrealvalue)
1027+
"%*u " // 22 (starttime)
1028+
"%*u " // 23 (vsize)
1029+
"%*d " // 24 (rss)
1030+
"%*u " // 25 (rsslim)
1031+
"%lu " // 26 (start_code)
1032+
"%lu " // 27 (end_code)
1033+
"%lu " // 28 (start_stack)
1034+
"%*u " // 29 (kstkesp)
1035+
"%*u " // 30 (kstkeip)
1036+
"%*u " // 31 (signal)
1037+
"%*u " // 32 (blocked)
1038+
"%*u " // 33 (sigignore)
1039+
"%*u " // 34 (sigcatch)
1040+
"%*u " // 35 (wchan)
1041+
"%*u " // 36 (nswap)
1042+
"%*u " // 37 (cnswap)
1043+
"%*d " // 38 (exit_signal)
1044+
"%*d " // 39 (processor)
1045+
"%*d " // 40 (rt_priority)
1046+
"%*u " // 41 (policy)
1047+
"%*u " // 42 (delayacct_blkio_ticks)
1048+
"%*u " // 43 (guest_time)
1049+
"%*d " // 44 (cguest_time)
1050+
"%lu " // 45 (start_data)
1051+
"%lu " // 46 (end_data)
1052+
"%lu " // 47 (start_brk)
1053+
"%lu " // 48 (arg_start)
1054+
"%lu " // 49 (arg_end)
1055+
"%lu " // 50 (env_start)
1056+
"%lu", // 51 (env_end)
1057+
&start_code, // 26
1058+
&end_code, // 27
1059+
&start_stack, // 28
1060+
&start_data, // 45
1061+
&end_data, // 46
1062+
&start_brk, // 47
1063+
&arg_start, // 48
1064+
&arg_end, // 49
1065+
&env_start, // 50
1066+
&env_end // 51
1067+
);
1068+
if (ret != 10) {
1069+
return;
1070+
}
1071+
1072+
va_start(ap, fmt);
1073+
ret = vsnprintf(title, sizeof(title), fmt, ap);
1074+
va_end(ap);
1075+
if (ret <= 0) {
1076+
return;
1077+
}
1078+
/*
1079+
* Include the null byte here, because in the calculations below
1080+
* we want to have room for it.
1081+
*/
1082+
title_len = ret + 1;
1083+
1084+
/* This will leak memory */
1085+
tmp_proctitle = realloc(proctitle, title_len);
1086+
if (tmp_proctitle == NULL) {
1087+
return;
1088+
}
1089+
proctitle = tmp_proctitle;
1090+
1091+
arg_start = (uint64_t)proctitle;
1092+
arg_end = arg_start + title_len;
1093+
1094+
brk_val = syscall(__NR_brk, 0);
1095+
if (brk_val < 0) {
1096+
return;
1097+
}
1098+
1099+
prctl_map = (struct prctl_mm_map) {
1100+
.start_code = start_code,
1101+
.end_code = end_code,
1102+
.start_stack = start_stack,
1103+
.start_data = start_data,
1104+
.end_data = end_data,
1105+
.start_brk = start_brk,
1106+
.brk = brk_val,
1107+
.arg_start = arg_start,
1108+
.arg_end = arg_end,
1109+
.env_start = env_start,
1110+
.env_end = env_end,
1111+
.auxv = NULL,
1112+
.auxv_size = 0,
1113+
.exe_fd = -1,
1114+
};
1115+
1116+
ret = prctl(PR_SET_MM,
1117+
PR_SET_MM_MAP,
1118+
(long)&prctl_map,
1119+
sizeof(prctl_map),
1120+
0);
1121+
if (ret == 0) {
1122+
strlcpy((char *)arg_start, title, title_len);
1123+
}
1124+
#endif /* HAVE_PRCTL */
9471125
}
9481126
#endif
1127+
9491128
#ifndef HAVE_SETPROCTITLE_INIT
9501129
void rep_setproctitle_init(int argc, char *argv[], char *envp[])
9511130
{

0 commit comments

Comments
 (0)