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