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 | |
57 | extern char **environ; |
58 | |
59 | static 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 | |
78 | static 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 | */ |
87 | int 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 | |
105 | static 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 | |
164 | static 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 | */ |
191 | void 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; |
264 | syerr: |
265 | error = errno; |
266 | error: |
267 | SPT.error = error; |
268 | } /* spt_init() */ |
269 | |
270 | |
271 | #ifndef SPT_MAXTITLE |
272 | #define SPT_MAXTITLE 255 |
273 | #endif |
274 | |
275 | void 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; |
314 | error: |
315 | SPT.error = error; |
316 | } /* setproctitle() */ |
317 | |
318 | |
319 | #endif /* __linux || __APPLE__ */ |
320 | #endif /* !HAVE_SETPROCTITLE */ |
321 | |
322 | #ifdef SETPROCTITLE_TEST_MAIN |
323 | int 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 | |