Coverage Report

Created: 2026-04-12 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/process-title.c
Line
Count
Source
1
/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "env-util.h"
5
#include "process-title.h"
6
7
#ifdef HAVE_LIBBSD
8
#include <bsd/unistd.h>
9
#else
10
#include <unistd.h> /* FreeBSD */
11
#endif
12
13
static char *process_name = NULL;
14
static char *current_process_title;
15
static unsigned int process_title_counter = 0;
16
17
#ifdef HAVE_SETPROCTITLE
18
#  undef PROCTITLE_HACK
19
#endif
20
21
#ifdef PROCTITLE_HACK
22
23
#ifdef DEBUG
24
/* if there are problems with this approach, try to make sure we notice it */
25
#  define PROCTITLE_CLEAR_CHAR 0xab
26
#else
27
/* There are always race conditions when updating the process title. ps might
28
   read a partially written title. Try to at least minimize this by using NUL
29
   as the fill character, so ps won't show a large number of 0xab chars. */
30
0
#  define PROCTITLE_CLEAR_CHAR 0
31
#endif
32
33
static char *process_title;
34
static size_t process_title_len, process_title_clean_pos;
35
static void *argv_memblock, *environ_memblock;
36
static char *environ_dummy = NULL;
37
38
static void proctitle_hack_init(char *argv[], char *env[])
39
0
{
40
0
  char *last;
41
0
  unsigned int i;
42
0
  bool clear_env;
43
44
0
  i_assert(argv[0] != NULL);
45
46
  /* find the last argv or environment string. it should always be the
47
     last string in environ, but don't rely on it. this is what openssh
48
     does, so hopefully it's safe enough. */
49
0
  last = argv[0] + strlen(argv[0]) + 1;
50
0
  for (i = 1; argv[i] != NULL; i++) {
51
0
    if (argv[i] == last)
52
0
      last = argv[i] + strlen(argv[i]) + 1;
53
0
  }
54
0
  if (env[0] == NULL)
55
0
    clear_env = FALSE;
56
0
  else {
57
0
    clear_env = last == env[0];
58
0
    for (i = 0; env[i] != NULL; i++) {
59
0
      if (env[i] == last)
60
0
        last = env[i] + strlen(env[i]) + 1;
61
0
    }
62
0
  }
63
64
0
  process_title = argv[0];
65
0
  process_title_len = last - argv[0];
66
67
0
  if (clear_env) {
68
0
    memset(env[0], PROCTITLE_CLEAR_CHAR, last - env[0]);
69
0
    process_title_clean_pos = env[0] - process_title;
70
0
  } else {
71
0
    process_title_clean_pos = 0;
72
0
  }
73
0
}
74
75
static char **argv_dup(char *old_argv[], void **memblock_r)
76
0
{
77
  /* @UNSAFE */
78
0
  void *memblock, *memblock_end;
79
0
  char **new_argv;
80
0
  unsigned int i, count;
81
0
  size_t len, memblock_len = 0;
82
83
0
  for (count = 0; old_argv[count] != NULL; count++)
84
0
    memblock_len += strlen(old_argv[count]) + 1;
85
0
  memblock_len += sizeof(char *) * (count + 1);
86
87
0
  memblock = malloc(memblock_len);
88
0
  if (memblock == NULL)
89
0
    i_fatal_status(FATAL_OUTOFMEM, "malloc() failed: %m");
90
0
  *memblock_r = memblock;
91
0
  memblock_end = PTR_OFFSET(memblock, memblock_len);
92
93
0
  new_argv = memblock;
94
0
  memblock = PTR_OFFSET(memblock, sizeof(char *) * (count + 1));
95
96
0
  for (i = 0; i < count; i++) {
97
0
    new_argv[i] = memblock;
98
0
    len = strlen(old_argv[i]) + 1;
99
0
    memcpy(memblock, old_argv[i], len);
100
0
    memblock = PTR_OFFSET(memblock, len);
101
0
  }
102
0
  i_assert(memblock == memblock_end);
103
0
  new_argv[i] = NULL;
104
0
  return new_argv;
105
0
}
106
107
static void proctitle_hack_set(const char *title)
108
0
{
109
0
  size_t len = strlen(title);
110
111
  /* OS X wants two NULs */
112
0
  if (len >= process_title_len-1)
113
0
    len = process_title_len - 2;
114
115
0
  memcpy(process_title, title, len);
116
0
  process_title[len++] = '\0';
117
0
  process_title[len++] = '\0';
118
119
0
  if (len < process_title_clean_pos) {
120
0
    memset(process_title + len, PROCTITLE_CLEAR_CHAR,
121
0
           process_title_clean_pos - len);
122
0
    process_title_clean_pos = len;
123
0
  } else if (process_title_clean_pos != 0) {
124
0
    process_title_clean_pos = len;
125
0
  }
126
0
}
127
128
#endif
129
130
void process_title_init(int argc ATTR_UNUSED, char **argv[])
131
0
{
132
0
  process_name = NULL;
133
0
  process_title_counter = 0;
134
135
0
#ifdef PROCTITLE_HACK
136
0
  char ***environ_p = env_get_environ_p();
137
0
  char **orig_argv = *argv;
138
0
  char **orig_environ = *environ_p;
139
140
0
  *argv = argv_dup(orig_argv, &argv_memblock);
141
0
  *environ_p = argv_dup(orig_environ, &environ_memblock);
142
0
  proctitle_hack_init(orig_argv, orig_environ);
143
0
#endif
144
#ifdef HAVE_LIBBSD
145
  setproctitle_init(argc, *argv, *env_get_environ_p());
146
#endif
147
0
  process_name = (*argv)[0];
148
0
}
149
150
void process_title_set(const char *title)
151
0
{
152
0
  i_assert(process_name != NULL);
153
154
0
  process_title_counter++;
155
0
  i_free(current_process_title);
156
0
  current_process_title = i_strdup(title);
157
#ifdef HAVE_SETPROCTITLE
158
  if (title == NULL)
159
    setproctitle(NULL);
160
  else
161
    setproctitle("%s", title);
162
#elif defined(PROCTITLE_HACK)
163
0
  T_BEGIN {
164
0
    proctitle_hack_set(t_strconcat(process_name, " ", title, NULL));
165
0
  } T_END;
166
0
#endif
167
0
}
168
169
const char *process_title_get(void)
170
0
{
171
0
  return current_process_title;
172
0
}
173
174
unsigned int process_title_get_counter(void)
175
0
{
176
0
  return process_title_counter;
177
0
}
178
179
void process_title_deinit(void)
180
5.68k
{
181
5.68k
#ifdef PROCTITLE_HACK
182
5.68k
  char ***environ_p = env_get_environ_p();
183
184
5.68k
  free(argv_memblock);
185
5.68k
  free(environ_memblock);
186
187
  /* Environment is no longer usable. Make sure we won't crash in case
188
     some library's deinit function still calls getenv(). This code was
189
     mainly added because of GNUTLS where we don't really care about the
190
     getenv() call.
191
192
     Alternatively we could remove the free() calls above, but that would
193
     annoy memory leak checking tools. Also we could attempt to restore
194
     the environ_p to its original state, but that's a bit complicated. */
195
5.68k
  *environ_p = &environ_dummy;
196
5.68k
#endif
197
  i_free(current_process_title);
198
5.68k
}