I've written a simple function to return a list of processes running on
the server. The function uses the Linux /proc filesystem, so is not
portable. Usage is like this:
tradebot01@ [local] => select * from tb_ps() where command like
'%post%';
pid | username | command
-------+----------+------------------------------------------------
3597 | root | /usr/lib/postfix/master
23671 | postgres | /usr/bin/postmaster -i -D /var/lib/pgsql/data
23677 | postgres | postgres: stats buffer process
23678 | postgres | postgres: stats collector process
23738 | postgres | postgres: tradebot tradebot01 [local]: SELECT
(5 rows)
I used C++ and noticed that some Postgres headers contain C++ keywords.
Is there any interest among PG developers in making the C-language
interface C++ clean? Or, is there hostility to this idea?
-K
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
extern "C" {
// Postgres includes. Some includes use C++ keywords,
// so we must surround them with these defines.
//
#define typeid typeid_
#define typename typename_
#define using using_
#include "postgres.h"
#include "pgstat.h"
#include "fmgr.h"
#include "c.h"
#include "funcapi.h"
#undef using
#undef typename
#undef typeid
}
/*
* tb_ps - returns a process listing from the machine on which the backend
* is running. Each row of the listing contains a pid, username and command
*
* To install, make shared library, copy tbpg.so to `pg_config --pkglibdir`.
* su to postgres, and from psql execute:
CREATE TYPE __tb_proc_list AS ( pid integer, username text, command text );
CREATE OR REPLACE FUNCTION tb_ps() RETURNS SETOF __tb_proc_list
AS 'tbpg', 'tb_ps'
LANGUAGE C IMMUTABLE STRICT;
*/
class Proc {
public:
Proc();
~Proc();
bool getNextCmd();
char *currentPid() const;
char *currentUser() const;
char *currentCommand() const;
public:
DIR *dir_;
struct dirent *dirent_;
std::string currentPid_;
std::string currentUser_;
std::string currentCommand_;
bool isCurrentACommand() const;
void unNullify( char *buf, ssize_t bytes ) const;
};
extern "C" {
PG_FUNCTION_INFO_V1(tb_ps);
};
extern "C" Datum
tb_ps(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
TupleTableSlot *slot;
AttInMetadata *attinmeta;
Proc *proc;
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
{
MemoryContext oldcontext;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* Build a tuple description for a __tb_proc_list tuple */
TupleDesc tupdesc = RelationNameGetTupleDesc("__tb_proc_list");
/* allocate a slot for a tuple with this tupdesc */
slot = TupleDescGetSlot(tupdesc);
/* assign slot to function context */
funcctx->slot = slot;
/*
* generate attribute metadata needed later to produce tuples from raw
* C strings
*/
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
// Create state for scanning proc dir
funcctx->user_fctx = new Proc();
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
slot = funcctx->slot;
attinmeta = funcctx->attinmeta;
proc = reinterpret_cast<Proc*>(funcctx->user_fctx);
if (proc->getNextCmd()) {
char **values;
HeapTuple tuple;
Datum result;
/*
* Prepare a values array for storage in our slot.
* This should be an array of C strings which will
* be processed later by the type input functions.
*/
values = (char **) palloc(3 * sizeof(char *));
values[0] = proc->currentPid();
values[1] = proc->currentUser();
values[2] = proc->currentCommand();
/* build a tuple */
tuple = BuildTupleFromCStrings(attinmeta, values);
/* make the tuple into a datum */
result = TupleGetDatum(slot, tuple);
SRF_RETURN_NEXT(funcctx, result);
} else {
delete proc;
SRF_RETURN_DONE(funcctx);
}
}
//--------------------------------------------------------------------------------------------------
Proc::Proc()
: dir_(0),
dirent_(0)
{
dir_ = opendir( "/proc" );
}
//--------------------------------------------------------------------------------------------------
Proc::~Proc()
{
if (dir_) {
closedir(dir_);
dir_ = 0;
}
}
//--------------------------------------------------------------------------------------------------
bool
Proc::getNextCmd()
{
if (!dir_) return false;
for(;;) {
do {
if ((dirent_ = readdir(dir_)) == 0) return false;
} while(!isCurrentACommand());
std::string fname("/proc/");
fname += dirent_->d_name;
fname += "/cmdline";
int fd = open( fname.c_str(), O_RDONLY );
if (fd < 0) continue;
char buf[4096];
ssize_t bytes = read( fd, buf, sizeof(buf)-1 );
if (bytes < 0) {
close( fd );
continue;
}
buf[bytes] = 0;
unNullify( buf, bytes );
struct stat statbuf;
if (fstat( fd, &statbuf ) < 0) {
close( fd );
continue;
}
close( fd );
struct passwd *pwd = getpwuid( statbuf.st_uid );
if (!pwd) continue;
currentPid_ = dirent_->d_name;
currentUser_ = pwd->pw_name;
currentCommand_ = buf;
break;
}
return true;
}
//--------------------------------------------------------------------------------------------------
char *
Proc::currentPid() const
{
char *rval = (char *)palloc(currentPid_.size()+1 * sizeof(char));
strcpy(rval,currentPid_.c_str());
return rval;
}
//--------------------------------------------------------------------------------------------------
char *
Proc::currentUser() const
{
char *rval = (char *)palloc(currentUser_.size()+1 * sizeof(char));
strcpy(rval,currentUser_.c_str());
return rval;
}
//--------------------------------------------------------------------------------------------------
char *
Proc::currentCommand() const
{
char *rval = (char *)palloc(currentCommand_.size()+1 * sizeof(char));
strcpy(rval,currentCommand_.c_str());
return rval;
}
//--------------------------------------------------------------------------------------------------
// Current is a command if it's name is all digits
//
bool
Proc::isCurrentACommand() const
{
if (!dir_ || !dirent_) return false;
for( char *cp = dirent_->d_name; *cp; ++cp )
if (!isdigit(static_cast<unsigned char>(*cp))) return false;
return true;
}
//--------------------------------------------------------------------------------------------------
// Command line is argv with null delimiters between elements. Replace nulls with spaces.
//
void
Proc::unNullify( char *buf, ssize_t bytes ) const
{
if (!buf || bytes <= 0) return;
for( char *cp = buf; bytes; ++cp, --bytes )
if (*cp == 0) *cp = ' ';
}
---------------------------(end of broadcast)---------------------------
TIP 6: Have you searched our list archives?
http://archives.postgresql.org