1/* ==========================================================================
2 * setproctitle.c - Linux/Darwin setproctitle.
3 * --------------------------------------------------------------------------
4 * Copyright (C) 2010 William Ahern
5 * Copyright (C) 2013 Salvatore Sanfilippo
6 * Copyright (C) 2013 Stam He
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to permit
13 * persons to whom the Software is furnished to do so, subject to the
14 * following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
22 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 * ==========================================================================
27 */
28#ifndef _GNU_SOURCE
29#define _GNU_SOURCE
30#endif
31
32#include <stddef.h> /* NULL size_t */
33#include <stdarg.h> /* va_list va_start va_end */
34#include <stdlib.h> /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */
35#include <stdio.h> /* vsnprintf(3) snprintf(3) */
36
37#include <string.h> /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */
38
39#include <errno.h> /* errno program_invocation_name program_invocation_short_name */
40
41#if !defined(HAVE_SETPROCTITLE)
42#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__)
43#define HAVE_SETPROCTITLE 1
44#else
45#define HAVE_SETPROCTITLE 0
46#endif
47#endif
48
49
50#if !HAVE_SETPROCTITLE
51#if (defined __linux || defined __APPLE__)
52
53#ifdef __GLIBC__
54#define HAVE_CLEARENV
55#endif
56
57extern char **environ;
58
59static struct {
60 /* original value */
61 const char *arg0;
62
63 /* title space available */
64 char *base, *end;
65
66 /* pointer to original nul character within base */
67 char *nul;
68
69 _Bool reset;
70 int error;
71} SPT;
72
73
74#ifndef SPT_MIN
75#define SPT_MIN(a, b) (((a) < (b))? (a) : (b))
76#endif
77
78static inline size_t spt_min(size_t a, size_t b) {
79 return SPT_MIN(a, b);
80} /* spt_min() */
81
82
83/*
84 * For discussion on the portability of the various methods, see
85 * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
86 */
87int spt_clearenv(void) {
88#ifdef HAVE_CLEARENV
89 return clearenv();
90#else
91 extern char **environ;
92 static char **tmp;
93
94 if (!(tmp = malloc(sizeof *tmp)))
95 return errno;
96
97 tmp[0] = NULL;
98 environ = tmp;
99
100 return 0;
101#endif
102} /* spt_clearenv() */
103
104
105static int spt_copyenv(int envc, char *oldenv[]) {
106 extern char **environ;
107 char **envcopy = NULL;
108 char *eq;
109 int i, error;
110 int envsize;
111
112 if (environ != oldenv)
113 return 0;
114
115 /* Copy environ into envcopy before clearing it. Shallow copy is
116 * enough as clearenv() only clears the environ array.
117 */
118 envsize = (envc + 1) * sizeof(char *);
119 envcopy = malloc(envsize);
120 if (!envcopy)
121 return ENOMEM;
122 memcpy(envcopy, oldenv, envsize);
123
124 /* Note that the state after clearenv() failure is undefined, but we'll
125 * just assume an error means it was left unchanged.
126 */
127 if ((error = spt_clearenv())) {
128 environ = oldenv;
129 free(envcopy);
130 return error;
131 }
132
133 /* Set environ from envcopy */
134 for (i = 0; envcopy[i]; i++) {
135 if (!(eq = strchr(envcopy[i], '=')))
136 continue;
137
138 *eq = '\0';
139 error = (0 != setenv(envcopy[i], eq + 1, 1))? errno : 0;
140 *eq = '=';
141
142 /* On error, do our best to restore state */
143 if (error) {
144#ifdef HAVE_CLEARENV
145 /* We don't assume it is safe to free environ, so we
146 * may leak it. As clearenv() was shallow using envcopy
147 * here is safe.
148 */
149 environ = envcopy;
150#else
151 free(envcopy);
152 free(environ); /* Safe to free, we have just alloc'd it */
153 environ = oldenv;
154#endif
155 return error;
156 }
157 }
158
159 free(envcopy);
160 return 0;
161} /* spt_copyenv() */
162
163
164static int spt_copyargs(int argc, char *argv[]) {
165 char *tmp;
166 int i;
167
168 for (i = 1; i < argc || (i >= argc && argv[i]); i++) {
169 if (!argv[i])
170 continue;
171
172 if (!(tmp = strdup(argv[i])))
173 return errno;
174
175 argv[i] = tmp;
176 }
177
178 return 0;
179} /* spt_copyargs() */
180
181/* Initialize and populate SPT to allow a future setproctitle()
182 * call.
183 *
184 * As setproctitle() basically needs to overwrite argv[0], we're
185 * trying to determine what is the largest contiguous block
186 * starting at argv[0] we can use for this purpose.
187 *
188 * As this range will overwrite some or all of the argv and environ
189 * strings, a deep copy of these two arrays is performed.
190 */
191void spt_init(int argc, char *argv[]) {
192 char **envp = environ;
193 char *base, *end, *nul, *tmp;
194 int i, error, envc;
195
196 if (!(base = argv[0]))
197 return;
198
199 /* We start with end pointing at the end of argv[0] */
200 nul = &base[strlen(base)];
201 end = nul + 1;
202
203 /* Attempt to extend end as far as we can, while making sure
204 * that the range between base and end is only allocated to
205 * argv, or anything that immediately follows argv (presumably
206 * envp).
207 */
208 for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
209 if (!argv[i] || argv[i] < end)
210 continue;
211
212 if (end >= argv[i] && end <= argv[i] + strlen(argv[i]))
213 end = argv[i] + strlen(argv[i]) + 1;
214 }
215
216 /* In case the envp array was not an immediate extension to argv,
217 * scan it explicitly.
218 */
219 for (i = 0; envp[i]; i++) {
220 if (envp[i] < end)
221 continue;
222
223 if (end >= envp[i] && end <= envp[i] + strlen(envp[i]))
224 end = envp[i] + strlen(envp[i]) + 1;
225 }
226 envc = i;
227
228 /* We're going to deep copy argv[], but argv[0] will still point to
229 * the old memory for the purpose of updating the title so we need
230 * to keep the original value elsewhere.
231 */
232 if (!(SPT.arg0 = strdup(argv[0])))
233 goto syerr;
234
235#if __linux__
236 if (!(tmp = strdup(program_invocation_name)))
237 goto syerr;
238
239 program_invocation_name = tmp;
240
241 if (!(tmp = strdup(program_invocation_short_name)))
242 goto syerr;
243
244 program_invocation_short_name = tmp;
245#elif __APPLE__
246 if (!(tmp = strdup(getprogname())))
247 goto syerr;
248
249 setprogname(tmp);
250#endif
251
252 /* Now make a full deep copy of the environment and argv[] */
253 if ((error = spt_copyenv(envc, envp)))
254 goto error;
255
256 if ((error = spt_copyargs(argc, argv)))
257 goto error;
258
259 SPT.nul = nul;
260 SPT.base = base;
261 SPT.end = end;
262
263 return;
264syerr:
265 error = errno;
266error:
267 SPT.error = error;
268} /* spt_init() */
269
270
271#ifndef SPT_MAXTITLE
272#define SPT_MAXTITLE 255
273#endif
274
275void setproctitle(const char *fmt, ...) {
276 char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */
277 va_list ap;
278 char *nul;
279 int len, error;
280
281 if (!SPT.base)
282 return;
283
284 if (fmt) {
285 va_start(ap, fmt);
286 len = vsnprintf(buf, sizeof buf, fmt, ap);
287 va_end(ap);
288 } else {
289 len = snprintf(buf, sizeof buf, "%s", SPT.arg0);
290 }
291
292 if (len <= 0)
293 { error = errno; goto error; }
294
295 if (!SPT.reset) {
296 memset(SPT.base, 0, SPT.end - SPT.base);
297 SPT.reset = 1;
298 } else {
299 memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base));
300 }
301
302 len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1);
303 memcpy(SPT.base, buf, len);
304 nul = &SPT.base[len];
305
306 if (nul < SPT.nul) {
307 *SPT.nul = '.';
308 } else if (nul == SPT.nul && &nul[1] < SPT.end) {
309 *SPT.nul = ' ';
310 *++nul = '\0';
311 }
312
313 return;
314error:
315 SPT.error = error;
316} /* setproctitle() */
317
318
319#endif /* __linux || __APPLE__ */
320#endif /* !HAVE_SETPROCTITLE */
321
322#ifdef SETPROCTITLE_TEST_MAIN
323int main(int argc, char *argv[]) {
324 spt_init(argc, argv);
325
326 printf("SPT.arg0: [%p] '%s'\n", SPT.arg0, SPT.arg0);
327 printf("SPT.base: [%p] '%s'\n", SPT.base, SPT.base);
328 printf("SPT.end: [%p] (%d bytes after base)'\n", SPT.end, (int) (SPT.end - SPT.base));
329 return 0;
330}
331#endif
332