Search Postgresql Archives

C++-Language Function/Process List

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Postgresql Jobs]     [Postgresql Admin]     [Postgresql Performance]     [Linux Clusters]     [PHP Home]     [PHP on Windows]     [Kernel Newbies]     [PHP Classes]     [PHP Books]     [PHP Databases]     [Postgresql & PHP]     [Yosemite]
  Powered by Linux