blob: 6388e3d834d89ba63a71c10a08b4b9a1ac24e650 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*--------------------------------------------------------------------
* ps_status.c
*
* Routines to support changing the ps display of PostgreSQL backends
* to contain some useful information. Mechanism differs wildly across
* platforms.
*
* $PostgreSQL: pgsql/src/backend/utils/misc/ps_status.c,v 1.33 2006/10/04 00:30:04 momjian Exp $
*
* Portions Copyright (c) 2005-2009, Greenplum inc
* Copyright (c) 2000-2009, PostgreSQL Global Development Group
* various details abducted from various places
*--------------------------------------------------------------------
*/
#include "postgres.h"
#include <unistd.h>
#ifdef HAVE_SYS_PSTAT_H
#include <sys/pstat.h> /* for HP-UX */
#endif
#ifdef HAVE_PS_STRINGS
#include <machine/vmparam.h> /* for old BSD */
#include <sys/exec.h>
#endif
#if defined(__darwin__)
#include <crt_externs.h>
#endif
#include "libpq/libpq.h"
#include "miscadmin.h"
#include "utils/ps_status.h"
#include "cdb/cdbvars.h" /* Gp_role, Gp_segment, currentSliceId */
extern char **environ;
extern int PostPortNumber;
bool update_process_title = true;
/*
* Alternative ways of updating ps display:
*
* PS_USE_SETPROCTITLE
* use the function setproctitle(const char *, ...)
* (newer BSD systems)
* PS_USE_PSTAT
* use the pstat(PSTAT_SETCMD, )
* (HPUX)
* PS_USE_PS_STRINGS
* assign PS_STRINGS->ps_argvstr = "string"
* (some BSD systems)
* PS_USE_CHANGE_ARGV
* assign argv[0] = "string"
* (some other BSD systems)
* PS_USE_CLOBBER_ARGV
* write over the argv and environment area
* (most SysV-like systems)
* PS_USE_WIN32
* push the string out as the name of a Windows event
* PS_USE_NONE
* don't update ps display
* (This is the default, as it is safest.)
*/
#if defined(HAVE_SETPROCTITLE)
#define PS_USE_SETPROCTITLE
#elif defined(HAVE_PSTAT) && defined(PSTAT_SETCMD)
#define PS_USE_PSTAT
#elif defined(HAVE_PS_STRINGS)
#define PS_USE_PS_STRINGS
#elif (defined(BSD) || defined(__bsdi__) || defined(__hurd__)) && !defined(__darwin__)
#define PS_USE_CHANGE_ARGV
#elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__ksr__) || defined(__osf__) || defined(__svr4__) || defined(__svr5__) || defined(__darwin__)
#define PS_USE_CLOBBER_ARGV
#elif defined(WIN32)
#define PS_USE_WIN32
#else
#define PS_USE_NONE
#endif
/* Different systems want the buffer padded differently */
#if defined(_AIX) || defined(__linux__) || defined(__svr4__)
#define PS_PADDING '\0'
#else
#define PS_PADDING ' '
#endif
#ifndef PS_USE_CLOBBER_ARGV
/* all but one options need a buffer to write their ps line in */
#define PS_BUFFER_SIZE 256
static char ps_buffer[PS_BUFFER_SIZE];
static const size_t ps_buffer_size = PS_BUFFER_SIZE;
#else /* PS_USE_CLOBBER_ARGV */
static char *ps_buffer; /* will point to argv area */
static size_t ps_buffer_size; /* space determined at run time */
static size_t last_status_len; /* use to minimize length of clobber */
#endif /* PS_USE_CLOBBER_ARGV */
static size_t ps_buffer_fixed_size; /* size of the constant prefix */
#if (defined(sun) && !defined(BSD))
static size_t ps_argument_size; /* size for the first argument */
#endif
static size_t ps_host_info_size; /*CDB*/
static char ps_host_info[64]; /*CDB*/
static char ps_username[64]; /*CDB*/
/* save the original argv[] location here */
static int save_argc;
static char **save_argv;
/* maximum database name string length */
#define DBNAME_MAX 10
/*
* GPDB: the real activity location, the location right after
* those "con1 seg1 cmd1" strings. Sometimes we need to modify
* the original activity string in the ps display output. Use
* this pointer to get the real original activity string instead
* of get_ps_display(). See MPP-2329.
*/
static size_t real_act_prefix_size;
/*
* Call this early in startup to save the original argc/argv values.
* If needed, we make a copy of the original argv[] array to preserve it
* from being clobbered by subsequent ps_display actions.
*
* (The original argv[] will not be overwritten by this routine, but may be
* overwritten during init_ps_display. Also, the physical location of the
* environment strings may be moved, so this should be called before any code
* that might try to hang onto a getenv() result.)
*/
char **
save_ps_display_args(int argc, char **argv)
{
save_argc = argc;
save_argv = argv;
#if defined(PS_USE_CLOBBER_ARGV)
/*
* If we're going to overwrite the argv area, count the available space.
* Also move the environment to make additional room.
*/
{
char *end_of_area = NULL;
int i;
#if (defined(sun) && !defined(BSD))
ps_argument_size = 0;
#endif
/*
* check for contiguous argv strings
*/
for (i = 0; i < argc; i++)
{
if (i == 0 || end_of_area + 1 == argv[i])
end_of_area = argv[i] + strlen(argv[i]);
}
if (end_of_area == NULL) /* probably can't happen? */
{
ps_buffer = NULL;
ps_buffer_size = 0;
return argv;
}
#if (defined(sun) && !defined(BSD))
ps_argument_size = strlen(argv[0]);
#endif
/*
* MPP-14501: only use argv area for status to preserve environment
*
* Prior to this change, the code here would use all of the memory with
* the initial contents of argv and environ as a buffer for the process
* status line. However that has the side effect of preventing other
* processes from examining the initial argv/environ of this process.
* Although we aren't concerned about argv, as of MPP-14501 we want
* other processes to be able to read this process's environment to
* detect the presence of the GPKILL environment variable. So instead
* of using all of the argv+environ area, we instead just use the argv
* area. We should not have to worry about the argv area on it's own
* being too small to display the status information because it's
* fairly large with all the arguments we pass and if that ever does
* become an issue we can always just have gpstart add more arguments.
*/
ps_buffer = argv[0];
last_status_len = ps_buffer_size = (end_of_area - ps_buffer);
}
#endif /* PS_USE_CLOBBER_ARGV */
#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV)
/*
* If we're going to change the original argv[] then make a copy for
* argument parsing purposes.
*
* (NB: do NOT think to remove the copying of argv[], even though
* postmaster.c finishes looking at argv[] long before we ever consider
* changing the ps display. On some platforms, getopt() keeps pointers
* into the argv array, and will get horribly confused when it is
* re-called to analyze a subprocess' argument string if the argv storage
* has been clobbered meanwhile. Other platforms have other dependencies
* on argv[].
*/
{
char **new_argv;
int i;
new_argv = (char **) malloc((argc + 1) * sizeof(char *));
if(!new_argv)
{
write_stderr("Failed to change/set argv: Out of Memory");
exit(1);
}
for (i = 0; i < argc; i++)
{
new_argv[i] = strdup(argv[i]);
if(!new_argv[i])
{
write_stderr("Failed to change/set argv: Out of Memory");
exit(1);
}
}
new_argv[argc] = NULL;
#if defined(__darwin__)
/*
* Darwin (and perhaps other NeXT-derived platforms?) has a static
* copy of the argv pointer, which we may fix like so:
*/
*_NSGetArgv() = new_argv;
#endif
argv = new_argv;
}
#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */
return argv;
}
/*
* Call this once during subprocess startup to set the identification
* values. At this point, the original argv[] array may be overwritten.
*/
void
init_ps_display(const char *username, const char *dbname,
const char *host_info, const char *initial_str)
{
Assert(username);
Assert(dbname);
Assert(host_info);
StrNCpy(ps_username, username, sizeof(ps_username)); /*CDB*/
#ifndef PS_USE_NONE
/* no ps display for stand-alone backend */
if (!IsUnderPostmaster)
return;
/* no ps display if you didn't call save_ps_display_args() */
if (!save_argv)
return;
#ifdef PS_USE_CLOBBER_ARGV
/* If ps_buffer is a pointer, it might still be null */
if (!ps_buffer)
return;
#endif
/*
* Overwrite argv[] to point at appropriate space, if needed
*/
#ifdef PS_USE_CHANGE_ARGV
save_argv[0] = ps_buffer;
save_argv[1] = NULL;
#endif /* PS_USE_CHANGE_ARGV */
#ifdef PS_USE_CLOBBER_ARGV
#if defined(__darwin__)
/*
* MPP-14501, keep heuristic ps and other tools appear to use working
*/
if (ps_buffer_size > save_argc)
{
last_status_len -= save_argc;
ps_buffer_size -= save_argc;
memset(ps_buffer+ps_buffer_size, 0, save_argc);
ps_buffer[ps_buffer_size] = PS_PADDING;
}
#endif /* darwin */
{
int i;
/* make extra argv slots point at end_of_area (a NUL) */
for (i = 1; i < save_argc; i++)
save_argv[i] = (ps_buffer + ps_buffer_size);
}
#endif /* PS_USE_CLOBBER_ARGV */
/*
* Make fixed prefix of ps display.
*/
/* Cut displayed database name to a short string if necessary. */
char dbname2[DBNAME_MAX+4];
int dbnamelen = strlen(dbname);
if ( dbnamelen <= DBNAME_MAX )
{
strcpy(dbname2, dbname);
}
else
{
strncpy(dbname2, dbname, DBNAME_MAX);
dbname2[DBNAME_MAX ] = '.';
dbname2[DBNAME_MAX+1] = '.';
dbname2[DBNAME_MAX+2] = '.';
dbname2[DBNAME_MAX+3] = '\0';
}
#ifdef PS_USE_SETPROCTITLE
/*
* apparently setproctitle() already adds a `progname:' prefix to the ps
* line
*/
snprintf(ps_buffer, ps_buffer_size,
"port %5d, %s %s %s ",
PostPortNumber, username, dbname2, host_info);
#else
snprintf(ps_buffer, ps_buffer_size,
"postgres: port %5d, %s %s %s ",
PostPortNumber, username, dbname2, host_info);
#endif /* not PS_USE_SETPROCTITLE */
ps_buffer_fixed_size = strlen(ps_buffer);
real_act_prefix_size = ps_buffer_fixed_size;
set_ps_display(initial_str, true);
/* CDB */
StrNCpy(ps_host_info, host_info, sizeof(ps_host_info));
ps_host_info_size = snprintf(ps_buffer + ps_buffer_fixed_size,
ps_buffer_size - ps_buffer_fixed_size,
"%s ", host_info);
/*
* MPP-14501, don't let a NUL from snprintf mess up our padding
*/
#if defined(__darwin__)
ps_buffer[ps_buffer_fixed_size + ps_host_info_size] = PS_PADDING;
#endif
ps_buffer_fixed_size += ps_host_info_size;
real_act_prefix_size = ps_buffer_fixed_size;
#endif /* not PS_USE_NONE */
}
/*
* Call this to update the ps status display to a fixed prefix plus an
* indication of what you're currently doing passed in the argument.
*/
void
set_ps_display(const char *activity, bool force)
{
char *cp = ps_buffer + ps_buffer_fixed_size;
char *ep = ps_buffer + ps_buffer_size;
if (!force && !update_process_title)
return;
#ifndef PS_USE_NONE
/* no ps display for stand-alone backend */
if (!IsUnderPostmaster)
return;
#ifdef PS_USE_CLOBBER_ARGV
/* If ps_buffer is a pointer, it might still be null */
if (!ps_buffer)
return;
#endif
/* Drop the remote host and port from the fixed prefix. */
if (ps_host_info_size > 0)
{
ps_buffer_fixed_size -= ps_host_info_size;
cp -= ps_host_info_size;
ps_host_info_size = 0;
}
/* Add client session's global id. */
if (ep > cp && gp_session_id > 0)
cp += snprintf(cp, ep-cp, "con%d ", gp_session_id);
/* Which segment is accessed by this qExec? */
if (ep > cp && Gp_role == GP_ROLE_EXECUTE && GetQEIndex() >= MASTER_CONTENT_ID)
cp += snprintf(cp, ep-cp, "seg%d ", GetQEIndex());
/* Add count of commands received from client session. */
if (ep > cp && gp_command_count > 0)
cp += snprintf(cp, ep-cp, "cmd%d ", gp_command_count);
/* Add slice number information */
if (ep > cp && currentSliceId > 0)
cp += snprintf(cp, ep-cp,"slice%d ", currentSliceId);
real_act_prefix_size = cp - ps_buffer;
/* Append caller's activity string. */
if ( ep > cp )
cp += snprintf(cp, ep-cp, "%s ", activity);
/* Add host and port. (Not very useful for qExecs, so skip.) */
if (ep > cp && Gp_role != GP_ROLE_EXECUTE && ps_host_info[0] != '\0')
cp += snprintf(cp, ep-cp, "%s ", ps_host_info);
/* Transmit new setting to kernel, if necessary */
#ifdef PS_USE_SETPROCTITLE
setproctitle("%s", ps_buffer);
#endif
#ifdef PS_USE_PSTAT
{
union pstun pst;
pst.pst_command = ps_buffer;
pstat(PSTAT_SETCMD, pst, strlen(ps_buffer), 0, 0);
}
#endif /* PS_USE_PSTAT */
#ifdef PS_USE_PS_STRINGS
PS_STRINGS->ps_nargvstr = 1;
PS_STRINGS->ps_argvstr = ps_buffer;
#endif /* PS_USE_PS_STRINGS */
#ifdef PS_USE_CLOBBER_ARGV
{
int buflen;
#if (defined(sun) && !defined(BSD))
int request_buflen = 2 * ps_argument_size + 1;
#endif
/* pad unused memory */
buflen = strlen(ps_buffer);
#if (defined(sun) && !defined(BSD))
/*
* In Solaris, to properly show the ps status display, it requires the length of
* the status at least twice of the original arguments. We pad the '.'s
* if buflen is not big enough. Note that using spaces to pad does not work,
* and I don't know why.
*/
if (buflen < request_buflen)
{
int pad_len = request_buflen - buflen;
if (ps_buffer_size < request_buflen)
pad_len = ps_buffer_size - buflen;
MemSet(ps_buffer + buflen, '.', pad_len);
buflen += pad_len;
}
#endif
if (last_status_len > buflen)
MemSet(ps_buffer + buflen, PS_PADDING, last_status_len - buflen);
last_status_len = buflen;
}
#endif /* PS_USE_CLOBBER_ARGV */
#ifdef PS_USE_WIN32
{
/*
* Win32 does not support showing any changed arguments. To make it at
* all possible to track which backend is doing what, we create a
* named object that can be viewed with for example Process Explorer.
*/
static HANDLE ident_handle = INVALID_HANDLE_VALUE;
char name[PS_BUFFER_SIZE + 32];
if (ident_handle != INVALID_HANDLE_VALUE)
CloseHandle(ident_handle);
sprintf(name, "pgident(%d): %s", MyProcPid, ps_buffer);
ident_handle = CreateEvent(NULL, TRUE, FALSE, name);
}
#endif /* PS_USE_WIN32 */
#endif /* not PS_USE_NONE */
}
/*
* Returns what's currently in the ps display with a given starting
* position. On some platforms the string will not be null-terminated,
* so return the effective length into *displen.
*/
static inline const char *
get_ps_display_from_position(size_t pos, int *displen)
{
#ifdef PS_USE_CLOBBER_ARGV
size_t offset;
/* If ps_buffer is a pointer, it might still be null */
if (!ps_buffer)
{
*displen = 0;
return "";
}
/* Remove any trailing spaces to offset the effect of PS_PADDING */
offset = ps_buffer_size;
while (offset > pos && ps_buffer[offset-1] == PS_PADDING)
offset--;
*displen = offset - pos;
#else
*displen = strlen(ps_buffer + pos);
#endif
return ps_buffer + pos;
}
/*
* Returns what's currently in the ps display, in case someone needs
* it. Note that only the activity part is returned. On some platforms
* the string will not be null-terminated, so return the effective
* length into *displen.
*/
const char *
get_ps_display(int *displen)
{
return get_ps_display_from_position(ps_buffer_fixed_size, displen);
}
/* CDB: Get the "username" string saved by init_ps_display(). */
const char *
get_ps_display_username(void)
{
return ps_username;
} /* get_ps_display_username */
/*
* Returns the real activity string in the ps display without prefix
* strings like "con1 seg1 cmd1" for GPDB. On some platforms the
* string will not be null-terminated, so return the effective length
* into *displen.
*/
const char *
get_real_act_ps_display(int *displen)
{
return get_ps_display_from_position(real_act_prefix_size, displen);
}