Coverage Report

Created: 2026-07-16 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/src/proctitle.c
Line
Count
Source
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2007-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23
24
/* Proctitle management */
25
26
#include "conf.h"
27
28
#if PF_ARGV_TYPE == PF_ARGV_PSTAT
29
# ifdef HAVE_SYS_PSTAT_H
30
#  include <sys/pstat.h>
31
# else
32
#  undef PF_ARGV_TYPE
33
#  define PF_ARGV_TYPE PF_ARGV_WRITEABLE
34
# endif /* HAVE_SYS_PSTAT_H */
35
#endif /* PF_ARGV_PSTAT */
36
37
#if PF_ARGV_TYPE == PF_ARGV_PSSTRINGS
38
# ifndef HAVE_SYS_EXEC_H
39
#  undef PF_ARGV_TYPE
40
#  define PF_ARGV_TYPE PF_ARGV_WRITEABLE
41
# else
42
#  include <machine/vmparam.h>
43
#  include <sys/exec.h>
44
# endif /* HAVE_SYS_EXEC_H */
45
#endif /* PF_ARGV_PSSTRINGS */
46
47
#if defined(HAVE___PROGNAME)
48
extern char *__progname, *__progname_full;
49
#endif /* HAVE___PROGNAME */
50
extern char **environ;
51
52
static char **prog_argv = NULL;
53
static char *prog_last_argv = NULL;
54
55
static int prog_argc = -1;
56
static char proc_title_buf[BUFSIZ];
57
58
/* Other libraries/code may change the global environment, so we keep our
59
 * own copy of the pointer to memory that we allocate, for freeing up later.
60
 * This mitigates ASAN reports of memory leaks here.
61
 */
62
static char **proc_environ = NULL;
63
64
static unsigned int proc_flags = 0;
65
0
#define PR_PROCTITLE_FL_USE_STATIC    0x001
66
67
0
void pr_proctitle_init(int argc, char *argv[], char *envp[]) {
68
0
  register int i;
69
0
  register size_t envpsize;
70
0
  char **p;
71
72
  /* Move the environment so setproctitle can use the space. */
73
0
  for (i = envpsize = 0; envp[i] != NULL; i++) {
74
0
    envpsize += strlen(envp[i]) + 1;
75
0
  }
76
77
0
  p = (char **) calloc((i + 1), sizeof(char *));
78
0
  if (p != NULL) {
79
0
    register unsigned int j;
80
0
    environ = proc_environ = p;
81
82
0
    j = 0;
83
0
    for (i = 0; envp[i] != NULL; i++) {
84
0
      size_t envp_len;
85
86
0
      envp_len = strlen(envp[i]);
87
0
      if (envp_len > PR_TUNABLE_ENV_MAX) {
88
        /* Skip any environ variables that are too long. */
89
0
        continue;
90
0
      }
91
92
0
      proc_environ[j] = malloc(envp_len + 1);
93
0
      if (proc_environ[j] != NULL) {
94
0
        sstrncpy(proc_environ[j], envp[i], envp_len + 1);
95
0
        j++;
96
0
      }
97
0
    }
98
0
  }
99
100
0
  prog_argv = argv;
101
0
  prog_argc = argc;
102
103
0
  for (i = 0; i < prog_argc; i++) {
104
0
    if (!i || (prog_last_argv + 1 == argv[i])) {
105
0
      prog_last_argv = argv[i] + strlen(argv[i]);
106
0
    }
107
0
  }
108
109
0
  for (i = 0; envp[i] != NULL; i++) {
110
0
    if ((prog_last_argv + 1) == envp[i]) {
111
0
      prog_last_argv = envp[i] + strlen(envp[i]);
112
0
    }
113
0
  }
114
115
0
#if defined(HAVE___PROGNAME)
116
  /* Set the __progname and __progname_full variables so glibc and company
117
   * don't go nuts.
118
   */
119
0
  __progname = strdup("proftpd");
120
0
  __progname_full = strdup(argv[0]);
121
0
#endif /* HAVE___PROGNAME */
122
0
  memset(proc_title_buf, '\0', sizeof(proc_title_buf));
123
0
}
124
125
0
void pr_proctitle_free(void) {
126
#if defined(PR_USE_DEVEL)
127
  if (proc_environ != NULL) {
128
    register unsigned int i;
129
130
    for (i = 0; proc_environ[i] != NULL; i++) {
131
      free(proc_environ[i]);
132
    }
133
134
    free(proc_environ);
135
    proc_environ = NULL;
136
  }
137
138
# if defined(HAVE___PROGNAME)
139
  free(__progname);
140
  __progname = NULL;
141
  free(__progname_full);
142
  __progname_full = NULL;
143
# endif /* HAVE___PROGNAME */
144
#endif /* PR_USE_DEVEL */
145
0
}
146
147
0
void pr_proctitle_set_str(const char *str) {
148
0
#if !defined(HAVE_SETPROCTITLE)
149
0
  char *p;
150
0
  int i, procbuflen, maxlen = (prog_last_argv - prog_argv[0]) - 2;
151
152
# if PF_ARGV_TYPE == PF_ARGV_PSTAT
153
  union pstun pst;
154
# endif /* PF_ARGV_PSTAT */
155
156
0
  if (proc_flags & PR_PROCTITLE_FL_USE_STATIC) {
157
0
    return;
158
0
  }
159
160
0
  sstrncpy(proc_title_buf, str, sizeof(proc_title_buf));
161
0
  procbuflen = strlen(proc_title_buf);
162
163
# if PF_ARGV_TYPE == PF_ARGV_NEW
164
  /* We can just replace argv[] arguments.  Nice and easy. */
165
  prog_argv[0] = proc_title_buf;
166
  for (i = 1; i < prog_argc; i++) {
167
    prog_argv[i] = "";
168
  }
169
# endif /* PF_ARGV_NEW */
170
171
0
# if PF_ARGV_TYPE == PF_ARGV_WRITEABLE
172
  /* We can overwrite individual argv[] arguments.  Semi-nice. */
173
0
  snprintf(prog_argv[0], maxlen, "%s", proc_title_buf);
174
0
  p = &prog_argv[0][procbuflen];
175
176
0
  while (p < prog_last_argv) {
177
0
    *p++ = '\0';
178
0
  }
179
180
0
  for (i = 1; i < prog_argc; i++) {
181
0
    prog_argv[i] = "";
182
0
  }
183
184
0
# endif /* PF_ARGV_WRITEABLE */
185
186
# if PF_ARGV_TYPE == PF_ARGV_PSSTRINGS
187
  PS_STRINGS->ps_nargvstr = 1;
188
  PS_STRINGS->ps_argvstr = proc_title_buf;
189
# endif /* PF_ARGV_PSSTRINGS */
190
191
#else
192
  if (proc_flags & PR_PROCTITLE_FL_USE_STATIC) {
193
    return;
194
  }
195
196
  setproctitle("%s", str);
197
#endif /* HAVE_SETPROCTITLE */
198
0
}
199
200
/* Note that we deliberately do NOT use pr_vsnprintf() here, since truncation
201
 * of long strings is often normal for these entries; consider paths longer
202
 * than PR_TUNABLE_SCOREBOARD_BUFFER_SIZE (Issue#683).
203
 */
204
0
void pr_proctitle_set(const char *fmt, ...) {
205
0
  va_list msg;
206
207
0
#if !defined(HAVE_SETPROCTITLE)
208
# if PF_ARGV_TYPE == PF_ARGV_PSTAT
209
  union pstun pst;
210
# endif /* PF_ARGV_PSTAT */
211
0
  char *p;
212
0
  int i, procbuflen, maxlen = (prog_last_argv - prog_argv[0]) - 2;
213
0
#endif /* HAVE_SETPROCTITLE */
214
215
0
  if (proc_flags & PR_PROCTITLE_FL_USE_STATIC) {
216
0
    return;
217
0
  }
218
219
0
  if (fmt == NULL) {
220
0
    return;
221
0
  }
222
223
0
  va_start(msg, fmt);
224
225
0
  memset(proc_title_buf, 0, sizeof(proc_title_buf));
226
227
#if defined(HAVE_SETPROCTITLE)
228
# if __FreeBSD__ >= 4 && !defined(FREEBSD4_0) && !defined(FREEBSD4_1)
229
  /* FreeBSD's setproctitle() automatically prepends the process name. */
230
  vsnprintf(proc_title_buf, sizeof(proc_title_buf)-1, fmt, msg);
231
232
# else /* FREEBSD4 */
233
  /* Manually append the process name for non-FreeBSD platforms. */
234
  snprintf(proc_title_buf, sizeof(proc_title_buf)-1, "%s", "proftpd: ");
235
  vsnprintf(proc_title_buf + strlen(proc_title_buf),
236
    sizeof(proc_title_buf)-1 - strlen(proc_title_buf), fmt, msg);
237
238
# endif /* FREEBSD4 */
239
  setproctitle("%s", proc_title_buf);
240
241
#else /* HAVE_SETPROCTITLE */
242
  /* Manually append the process name for non-setproctitle() platforms. */
243
0
  snprintf(proc_title_buf, sizeof(proc_title_buf)-1, "%s", "proftpd: ");
244
0
  vsnprintf(proc_title_buf + strlen(proc_title_buf),
245
0
    sizeof(proc_title_buf)-1 - strlen(proc_title_buf), fmt, msg);
246
247
0
#endif /* HAVE_SETPROCTITLE */
248
249
0
  va_end(msg);
250
251
#if defined(HAVE_SETPROCTITLE)
252
  return;
253
#else
254
0
  procbuflen = strlen(proc_title_buf);
255
256
# if PF_ARGV_TYPE == PF_ARGV_NEW
257
  /* We can just replace argv[] arguments.  Nice and easy. */
258
  prog_argv[0] = proc_title_buf;
259
  for (i = 1; i < prog_argc; i++) {
260
    prog_argv[i] = "";
261
  }
262
# endif /* PF_ARGV_NEW */
263
264
0
# if PF_ARGV_TYPE == PF_ARGV_WRITEABLE
265
  /* We can overwrite individual argv[] arguments.  Semi-nice. */
266
0
  snprintf(prog_argv[0], maxlen, "%s", proc_title_buf);
267
0
  p = &prog_argv[0][procbuflen];
268
269
0
  while (p < prog_last_argv) {
270
0
    *p++ = '\0';
271
0
  }
272
273
0
  for (i = 1; i < prog_argc; i++) {
274
0
    prog_argv[i] = "";
275
0
  }
276
277
0
# endif /* PF_ARGV_WRITEABLE */
278
279
# if PF_ARGV_TYPE == PF_ARGV_PSTAT
280
  pst.pst_command = proc_title_buf;
281
  pstat(PSTAT_SETCMD, pst, procbuflen, 0, 0);
282
283
# endif /* PF_ARGV_PSTAT */
284
285
# if PF_ARGV_TYPE == PF_ARGV_PSSTRINGS
286
  PS_STRINGS->ps_nargvstr = 1;
287
  PS_STRINGS->ps_argvstr = proc_title_buf;
288
# endif /* PF_ARGV_PSSTRINGS */
289
0
#endif /* HAVE_SETPROCTITLE */
290
0
}
291
292
0
void pr_proctitle_set_static_str(const char *buf) {
293
0
  if (buf != NULL) {
294
0
    pr_proctitle_set_str(buf);
295
0
    proc_flags |= PR_PROCTITLE_FL_USE_STATIC;
296
297
0
  } else {
298
    /* Reset. */
299
0
    proc_flags = 0;
300
0
  }
301
0
}
302
303
0
int pr_proctitle_get(char *buf, size_t bufsz) {
304
0
  if (buf == NULL ||
305
0
      bufsz == 0) {
306
307
    /* If the caller didn't provide an input buffer, they are simply
308
     * querying for the length of the current process title.  Easy enough
309
     * to acquiesce to that request.
310
     */
311
0
    return strlen(proc_title_buf);
312
0
  }
313
314
0
  sstrncpy(buf, proc_title_buf, bufsz);
315
0
  return strlen(buf);
316
0
}