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 #define HAVE_SETPROCTITLE (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)
43 #endif
44
45
46 #if !HAVE_SETPROCTITLE
47 #if (defined __linux || defined __APPLE__)
48
49 extern char **environ;
50
51 static struct {
52 /* original value */
53 const char *arg0;
54
55 /* title space available */
56 char *base, *end;
57
58 /* pointer to original nul character within base */
59 char *nul;
60
61 _Bool reset;
62 int error;
63 } SPT;
64
65
66 #ifndef SPT_MIN
67 #define SPT_MIN(a, b) (((a) < (b))? (a) : (b))
68 #endif
69
spt_min(size_t a,size_t b)70 static inline size_t spt_min(size_t a, size_t b) {
71 return SPT_MIN(a, b);
72 } /* spt_min() */
73
74
75 /*
76 * For discussion on the portability of the various methods, see
77 * http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
78 */
spt_clearenv(void)79 static int spt_clearenv(void) {
80 #if __GLIBC__
81 clearenv();
82
83 return 0;
84 #else
85 extern char **environ;
86 static char **tmp;
87
88 if (!(tmp = malloc(sizeof *tmp)))
89 return errno;
90
91 tmp[0] = NULL;
92 environ = tmp;
93
94 return 0;
95 #endif
96 } /* spt_clearenv() */
97
98
spt_copyenv(char * oldenv[])99 static int spt_copyenv(char *oldenv[]) {
100 extern char **environ;
101 char *eq;
102 int i, error;
103
104 if (environ != oldenv)
105 return 0;
106
107 if ((error = spt_clearenv()))
108 goto error;
109
110 for (i = 0; oldenv[i]; i++) {
111 if (!(eq = strchr(oldenv[i], '=')))
112 continue;
113
114 *eq = '\0';
115 error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0;
116 *eq = '=';
117
118 if (error)
119 goto error;
120 }
121
122 return 0;
123 error:
124 environ = oldenv;
125
126 return error;
127 } /* spt_copyenv() */
128
129
spt_copyargs(int argc,char * argv[])130 static int spt_copyargs(int argc, char *argv[]) {
131 char *tmp;
132 int i;
133
134 for (i = 1; i < argc || (i >= argc && argv[i]); i++) {
135 if (!argv[i])
136 continue;
137
138 if (!(tmp = strdup(argv[i])))
139 return errno;
140
141 argv[i] = tmp;
142 }
143
144 return 0;
145 } /* spt_copyargs() */
146
147
spt_init(int argc,char * argv[])148 void spt_init(int argc, char *argv[]) {
149 char **envp = environ;
150 char *base, *end, *nul, *tmp;
151 int i, error;
152
153 if (!(base = argv[0]))
154 return;
155
156 nul = &base[strlen(base)];
157 end = nul + 1;
158
159 for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
160 if (!argv[i] || argv[i] < end)
161 continue;
162
163 end = argv[i] + strlen(argv[i]) + 1;
164 }
165
166 for (i = 0; envp[i]; i++) {
167 if (envp[i] < end)
168 continue;
169
170 end = envp[i] + strlen(envp[i]) + 1;
171 }
172
173 if (!(SPT.arg0 = strdup(argv[0])))
174 goto syerr;
175
176 #if __GLIBC__
177 if (!(tmp = strdup(program_invocation_name)))
178 goto syerr;
179
180 program_invocation_name = tmp;
181
182 if (!(tmp = strdup(program_invocation_short_name)))
183 goto syerr;
184
185 program_invocation_short_name = tmp;
186 #elif __APPLE__
187 if (!(tmp = strdup(getprogname())))
188 goto syerr;
189
190 setprogname(tmp);
191 #endif
192
193
194 if ((error = spt_copyenv(envp)))
195 goto error;
196
197 if ((error = spt_copyargs(argc, argv)))
198 goto error;
199
200 SPT.nul = nul;
201 SPT.base = base;
202 SPT.end = end;
203
204 return;
205 syerr:
206 error = errno;
207 error:
208 SPT.error = error;
209 } /* spt_init() */
210
211
212 #ifndef SPT_MAXTITLE
213 #define SPT_MAXTITLE 255
214 #endif
215
setproctitle(const char * fmt,...)216 void setproctitle(const char *fmt, ...) {
217 char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */
218 va_list ap;
219 char *nul;
220 int len, error;
221
222 if (!SPT.base)
223 return;
224
225 if (fmt) {
226 va_start(ap, fmt);
227 len = vsnprintf(buf, sizeof buf, fmt, ap);
228 va_end(ap);
229 } else {
230 len = snprintf(buf, sizeof buf, "%s", SPT.arg0);
231 }
232
233 if (len <= 0)
234 { error = errno; goto error; }
235
236 if (!SPT.reset) {
237 memset(SPT.base, 0, SPT.end - SPT.base);
238 SPT.reset = 1;
239 } else {
240 memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base));
241 }
242
243 len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1);
244 memcpy(SPT.base, buf, len);
245 nul = &SPT.base[len];
246
247 if (nul < SPT.nul) {
248 *SPT.nul = '.';
249 } else if (nul == SPT.nul && &nul[1] < SPT.end) {
250 *SPT.nul = ' ';
251 *++nul = '\0';
252 }
253
254 return;
255 error:
256 SPT.error = error;
257 } /* setproctitle() */
258
259
260 #endif /* __linux || __APPLE__ */
261 #endif /* !HAVE_SETPROCTITLE */
262