Coverage Report

Created: 2025-08-24 06:03

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