1 | /*-------------------------------------------------------------------- |
2 | * ps_status.c |
3 | * |
4 | * Routines to support changing the ps display of PostgreSQL backends |
5 | * to contain some useful information. Mechanism differs wildly across |
6 | * platforms. |
7 | * |
8 | * src/backend/utils/misc/ps_status.c |
9 | * |
10 | * Copyright (c) 2000-2019, PostgreSQL Global Development Group |
11 | * various details abducted from various places |
12 | *-------------------------------------------------------------------- |
13 | */ |
14 | |
15 | #include "postgres.h" |
16 | |
17 | #include <unistd.h> |
18 | #ifdef HAVE_SYS_PSTAT_H |
19 | #include <sys/pstat.h> /* for HP-UX */ |
20 | #endif |
21 | #ifdef HAVE_PS_STRINGS |
22 | #include <machine/vmparam.h> /* for old BSD */ |
23 | #include <sys/exec.h> |
24 | #endif |
25 | #if defined(__darwin__) |
26 | #include <crt_externs.h> |
27 | #endif |
28 | |
29 | #include "libpq/libpq.h" |
30 | #include "miscadmin.h" |
31 | #include "utils/ps_status.h" |
32 | #include "utils/guc.h" |
33 | |
34 | extern char **environ; |
35 | bool update_process_title = true; |
36 | |
37 | |
38 | /* |
39 | * Alternative ways of updating ps display: |
40 | * |
41 | * PS_USE_SETPROCTITLE_FAST |
42 | * use the function setproctitle_fast(const char *, ...) |
43 | * (newer FreeBSD systems) |
44 | * PS_USE_SETPROCTITLE |
45 | * use the function setproctitle(const char *, ...) |
46 | * (newer BSD systems) |
47 | * PS_USE_PSTAT |
48 | * use the pstat(PSTAT_SETCMD, ) |
49 | * (HPUX) |
50 | * PS_USE_PS_STRINGS |
51 | * assign PS_STRINGS->ps_argvstr = "string" |
52 | * (some BSD systems) |
53 | * PS_USE_CHANGE_ARGV |
54 | * assign argv[0] = "string" |
55 | * (some other BSD systems) |
56 | * PS_USE_CLOBBER_ARGV |
57 | * write over the argv and environment area |
58 | * (Linux and most SysV-like systems) |
59 | * PS_USE_WIN32 |
60 | * push the string out as the name of a Windows event |
61 | * PS_USE_NONE |
62 | * don't update ps display |
63 | * (This is the default, as it is safest.) |
64 | */ |
65 | #if defined(HAVE_SETPROCTITLE_FAST) |
66 | #define PS_USE_SETPROCTITLE_FAST |
67 | #elif defined(HAVE_SETPROCTITLE) |
68 | #define PS_USE_SETPROCTITLE |
69 | #elif defined(HAVE_PSTAT) && defined(PSTAT_SETCMD) |
70 | #define PS_USE_PSTAT |
71 | #elif defined(HAVE_PS_STRINGS) |
72 | #define PS_USE_PS_STRINGS |
73 | #elif (defined(BSD) || defined(__hurd__)) && !defined(__darwin__) |
74 | #define PS_USE_CHANGE_ARGV |
75 | #elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(__svr5__) || defined(__darwin__) |
76 | #define PS_USE_CLOBBER_ARGV |
77 | #elif defined(WIN32) |
78 | #define PS_USE_WIN32 |
79 | #else |
80 | #define PS_USE_NONE |
81 | #endif |
82 | |
83 | |
84 | /* Different systems want the buffer padded differently */ |
85 | #if defined(_AIX) || defined(__linux__) || defined(__darwin__) |
86 | #define PS_PADDING '\0' |
87 | #else |
88 | #define PS_PADDING ' ' |
89 | #endif |
90 | |
91 | |
92 | #ifndef PS_USE_CLOBBER_ARGV |
93 | /* all but one option need a buffer to write their ps line in */ |
94 | #define PS_BUFFER_SIZE 256 |
95 | static char ps_buffer[PS_BUFFER_SIZE]; |
96 | static const size_t ps_buffer_size = PS_BUFFER_SIZE; |
97 | #else /* PS_USE_CLOBBER_ARGV */ |
98 | static char *ps_buffer; /* will point to argv area */ |
99 | static size_t ps_buffer_size; /* space determined at run time */ |
100 | static size_t last_status_len; /* use to minimize length of clobber */ |
101 | #endif /* PS_USE_CLOBBER_ARGV */ |
102 | |
103 | static size_t ps_buffer_cur_len; /* nominal strlen(ps_buffer) */ |
104 | |
105 | static size_t ps_buffer_fixed_size; /* size of the constant prefix */ |
106 | |
107 | /* save the original argv[] location here */ |
108 | static int save_argc; |
109 | static char **save_argv; |
110 | |
111 | |
112 | /* |
113 | * Call this early in startup to save the original argc/argv values. |
114 | * If needed, we make a copy of the original argv[] array to preserve it |
115 | * from being clobbered by subsequent ps_display actions. |
116 | * |
117 | * (The original argv[] will not be overwritten by this routine, but may be |
118 | * overwritten during init_ps_display. Also, the physical location of the |
119 | * environment strings may be moved, so this should be called before any code |
120 | * that might try to hang onto a getenv() result.) |
121 | * |
122 | * Note that in case of failure this cannot call elog() as that is not |
123 | * initialized yet. We rely on write_stderr() instead. |
124 | */ |
125 | char ** |
126 | save_ps_display_args(int argc, char **argv) |
127 | { |
128 | save_argc = argc; |
129 | save_argv = argv; |
130 | |
131 | #if defined(PS_USE_CLOBBER_ARGV) |
132 | |
133 | /* |
134 | * If we're going to overwrite the argv area, count the available space. |
135 | * Also move the environment to make additional room. |
136 | */ |
137 | { |
138 | char *end_of_area = NULL; |
139 | char **new_environ; |
140 | int i; |
141 | |
142 | /* |
143 | * check for contiguous argv strings |
144 | */ |
145 | for (i = 0; i < argc; i++) |
146 | { |
147 | if (i == 0 || end_of_area + 1 == argv[i]) |
148 | end_of_area = argv[i] + strlen(argv[i]); |
149 | } |
150 | |
151 | if (end_of_area == NULL) /* probably can't happen? */ |
152 | { |
153 | ps_buffer = NULL; |
154 | ps_buffer_size = 0; |
155 | return argv; |
156 | } |
157 | |
158 | /* |
159 | * check for contiguous environ strings following argv |
160 | */ |
161 | for (i = 0; environ[i] != NULL; i++) |
162 | { |
163 | if (end_of_area + 1 == environ[i]) |
164 | end_of_area = environ[i] + strlen(environ[i]); |
165 | } |
166 | |
167 | ps_buffer = argv[0]; |
168 | last_status_len = ps_buffer_size = end_of_area - argv[0]; |
169 | |
170 | /* |
171 | * move the environment out of the way |
172 | */ |
173 | new_environ = (char **) malloc((i + 1) * sizeof(char *)); |
174 | if (!new_environ) |
175 | { |
176 | write_stderr("out of memory\n" ); |
177 | exit(1); |
178 | } |
179 | for (i = 0; environ[i] != NULL; i++) |
180 | { |
181 | new_environ[i] = strdup(environ[i]); |
182 | if (!new_environ[i]) |
183 | { |
184 | write_stderr("out of memory\n" ); |
185 | exit(1); |
186 | } |
187 | } |
188 | new_environ[i] = NULL; |
189 | environ = new_environ; |
190 | } |
191 | #endif /* PS_USE_CLOBBER_ARGV */ |
192 | |
193 | #if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) |
194 | |
195 | /* |
196 | * If we're going to change the original argv[] then make a copy for |
197 | * argument parsing purposes. |
198 | * |
199 | * (NB: do NOT think to remove the copying of argv[], even though |
200 | * postmaster.c finishes looking at argv[] long before we ever consider |
201 | * changing the ps display. On some platforms, getopt() keeps pointers |
202 | * into the argv array, and will get horribly confused when it is |
203 | * re-called to analyze a subprocess' argument string if the argv storage |
204 | * has been clobbered meanwhile. Other platforms have other dependencies |
205 | * on argv[]. |
206 | */ |
207 | { |
208 | char **new_argv; |
209 | int i; |
210 | |
211 | new_argv = (char **) malloc((argc + 1) * sizeof(char *)); |
212 | if (!new_argv) |
213 | { |
214 | write_stderr("out of memory\n" ); |
215 | exit(1); |
216 | } |
217 | for (i = 0; i < argc; i++) |
218 | { |
219 | new_argv[i] = strdup(argv[i]); |
220 | if (!new_argv[i]) |
221 | { |
222 | write_stderr("out of memory\n" ); |
223 | exit(1); |
224 | } |
225 | } |
226 | new_argv[argc] = NULL; |
227 | |
228 | #if defined(__darwin__) |
229 | |
230 | /* |
231 | * macOS (and perhaps other NeXT-derived platforms?) has a static copy |
232 | * of the argv pointer, which we may fix like so: |
233 | */ |
234 | *_NSGetArgv() = new_argv; |
235 | #endif |
236 | |
237 | argv = new_argv; |
238 | } |
239 | #endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ |
240 | |
241 | return argv; |
242 | } |
243 | |
244 | /* |
245 | * Call this once during subprocess startup to set the identification |
246 | * values. At this point, the original argv[] array may be overwritten. |
247 | */ |
248 | void |
249 | init_ps_display(const char *username, const char *dbname, |
250 | const char *host_info, const char *initial_str) |
251 | { |
252 | Assert(username); |
253 | Assert(dbname); |
254 | Assert(host_info); |
255 | |
256 | #ifndef PS_USE_NONE |
257 | /* no ps display for stand-alone backend */ |
258 | if (!IsUnderPostmaster) |
259 | return; |
260 | |
261 | /* no ps display if you didn't call save_ps_display_args() */ |
262 | if (!save_argv) |
263 | return; |
264 | |
265 | #ifdef PS_USE_CLOBBER_ARGV |
266 | /* If ps_buffer is a pointer, it might still be null */ |
267 | if (!ps_buffer) |
268 | return; |
269 | #endif |
270 | |
271 | /* |
272 | * Overwrite argv[] to point at appropriate space, if needed |
273 | */ |
274 | |
275 | #ifdef PS_USE_CHANGE_ARGV |
276 | save_argv[0] = ps_buffer; |
277 | save_argv[1] = NULL; |
278 | #endif /* PS_USE_CHANGE_ARGV */ |
279 | |
280 | #ifdef PS_USE_CLOBBER_ARGV |
281 | { |
282 | int i; |
283 | |
284 | /* make extra argv slots point at end_of_area (a NUL) */ |
285 | for (i = 1; i < save_argc; i++) |
286 | save_argv[i] = ps_buffer + ps_buffer_size; |
287 | } |
288 | #endif /* PS_USE_CLOBBER_ARGV */ |
289 | |
290 | /* |
291 | * Make fixed prefix of ps display. |
292 | */ |
293 | |
294 | #if defined(PS_USE_SETPROCTITLE) || defined(PS_USE_SETPROCTITLE_FAST) |
295 | |
296 | /* |
297 | * apparently setproctitle() already adds a `progname:' prefix to the ps |
298 | * line |
299 | */ |
300 | #define PROGRAM_NAME_PREFIX "" |
301 | #else |
302 | #define PROGRAM_NAME_PREFIX "postgres: " |
303 | #endif |
304 | |
305 | if (*cluster_name == '\0') |
306 | { |
307 | snprintf(ps_buffer, ps_buffer_size, |
308 | PROGRAM_NAME_PREFIX "%s %s %s " , |
309 | username, dbname, host_info); |
310 | } |
311 | else |
312 | { |
313 | snprintf(ps_buffer, ps_buffer_size, |
314 | PROGRAM_NAME_PREFIX "%s: %s %s %s " , |
315 | cluster_name, username, dbname, host_info); |
316 | } |
317 | |
318 | ps_buffer_cur_len = ps_buffer_fixed_size = strlen(ps_buffer); |
319 | |
320 | set_ps_display(initial_str, true); |
321 | #endif /* not PS_USE_NONE */ |
322 | } |
323 | |
324 | |
325 | |
326 | /* |
327 | * Call this to update the ps status display to a fixed prefix plus an |
328 | * indication of what you're currently doing passed in the argument. |
329 | */ |
330 | void |
331 | set_ps_display(const char *activity, bool force) |
332 | { |
333 | #ifndef PS_USE_NONE |
334 | /* update_process_title=off disables updates, unless force = true */ |
335 | if (!force && !update_process_title) |
336 | return; |
337 | |
338 | /* no ps display for stand-alone backend */ |
339 | if (!IsUnderPostmaster) |
340 | return; |
341 | |
342 | #ifdef PS_USE_CLOBBER_ARGV |
343 | /* If ps_buffer is a pointer, it might still be null */ |
344 | if (!ps_buffer) |
345 | return; |
346 | #endif |
347 | |
348 | /* Update ps_buffer to contain both fixed part and activity */ |
349 | strlcpy(ps_buffer + ps_buffer_fixed_size, activity, |
350 | ps_buffer_size - ps_buffer_fixed_size); |
351 | ps_buffer_cur_len = strlen(ps_buffer); |
352 | |
353 | /* Transmit new setting to kernel, if necessary */ |
354 | |
355 | #ifdef PS_USE_SETPROCTITLE |
356 | setproctitle("%s" , ps_buffer); |
357 | #elif defined(PS_USE_SETPROCTITLE_FAST) |
358 | setproctitle_fast("%s" , ps_buffer); |
359 | #endif |
360 | |
361 | #ifdef PS_USE_PSTAT |
362 | { |
363 | union pstun pst; |
364 | |
365 | pst.pst_command = ps_buffer; |
366 | pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0); |
367 | } |
368 | #endif /* PS_USE_PSTAT */ |
369 | |
370 | #ifdef PS_USE_PS_STRINGS |
371 | PS_STRINGS->ps_nargvstr = 1; |
372 | PS_STRINGS->ps_argvstr = ps_buffer; |
373 | #endif /* PS_USE_PS_STRINGS */ |
374 | |
375 | #ifdef PS_USE_CLOBBER_ARGV |
376 | /* pad unused memory; need only clobber remainder of old status string */ |
377 | if (last_status_len > ps_buffer_cur_len) |
378 | MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING, |
379 | last_status_len - ps_buffer_cur_len); |
380 | last_status_len = ps_buffer_cur_len; |
381 | #endif /* PS_USE_CLOBBER_ARGV */ |
382 | |
383 | #ifdef PS_USE_WIN32 |
384 | { |
385 | /* |
386 | * Win32 does not support showing any changed arguments. To make it at |
387 | * all possible to track which backend is doing what, we create a |
388 | * named object that can be viewed with for example Process Explorer. |
389 | */ |
390 | static HANDLE ident_handle = INVALID_HANDLE_VALUE; |
391 | char name[PS_BUFFER_SIZE + 32]; |
392 | |
393 | if (ident_handle != INVALID_HANDLE_VALUE) |
394 | CloseHandle(ident_handle); |
395 | |
396 | sprintf(name, "pgident(%d): %s" , MyProcPid, ps_buffer); |
397 | |
398 | ident_handle = CreateEvent(NULL, TRUE, FALSE, name); |
399 | } |
400 | #endif /* PS_USE_WIN32 */ |
401 | #endif /* not PS_USE_NONE */ |
402 | } |
403 | |
404 | |
405 | /* |
406 | * Returns what's currently in the ps display, in case someone needs |
407 | * it. Note that only the activity part is returned. On some platforms |
408 | * the string will not be null-terminated, so return the effective |
409 | * length into *displen. |
410 | */ |
411 | const char * |
412 | get_ps_display(int *displen) |
413 | { |
414 | #ifdef PS_USE_CLOBBER_ARGV |
415 | /* If ps_buffer is a pointer, it might still be null */ |
416 | if (!ps_buffer) |
417 | { |
418 | *displen = 0; |
419 | return "" ; |
420 | } |
421 | #endif |
422 | |
423 | *displen = (int) (ps_buffer_cur_len - ps_buffer_fixed_size); |
424 | |
425 | return ps_buffer + ps_buffer_fixed_size; |
426 | } |
427 | |