Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: XSRV and dbutils #1

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions ioc/dbutils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvxs is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/

#include <stdexcept>

#include <pvxs/version.h>
#include <utilpvt.h>

#include "dbutils.h"

namespace pvxs {
namespace db {

DBError::DBError(long status, const char *pv, const char *op)
:std::runtime_error(SB()<<(pv ? pv : "(null)")
<<" "<<(op?op:"(null)")
<<status)
,status(status)
{}

DBError::~DBError() {}

#if EPICS_VERSION_INT < VERSION_INT(3,16,1,0)
pvxs::db::DBEntry::DBEntry(dbCommon *prec)
:DBEntry()
{
if(dbFindRecord(&ent, prec->name)!=0)
throw std::logic_error("Record not found");
}
#endif

bool DBEntry::firstRecord() noexcept
{
// setup at first record (return false for empty DB)
return !dbFirstRecordType(&ent) && !dbFirstRecord(&ent);
}

bool DBEntry::nextRecord() noexcept
{
// step to next record, or first record of next type
return !dbNextRecord(&ent) || (!dbNextRecordType(&ent) && !dbFirstRecord(&ent));
}

DBChan::DBChan(dbChannel *ch)
:ch(ch)
{
if(!ch)
throw std::bad_alloc();
}

DBChan::DBChan(const char *pv)
{
ch.reset(dbChannelCreate(pv));
if(!ch)
throw std::runtime_error(SB()<<"Bad PV "<<pv);
if(auto sts = dbChannelOpen(ptr())) {
throw DBError(sts, pv, "open");
}
}

void DBChan::get_unlock(short dbr, void *buf, long *opts, size_t *nReq, db_field_log *pfl) const
{
long lReq = nReq ? *nReq : 1;

if(auto sts = dbChannelGet(ptr(), dbr, buf, opts, &lReq, pfl))
throw DBError(sts, (*this)->name, __func__);

if(nReq)
*nReq = lReq;
}

void DBChan::get_lock(short dbr, void *buf, long *opts, size_t *nReq, db_field_log *pfl) const
{
long lReq = nReq ? *nReq : 1;

if(auto sts = dbChannelGetField(ptr(), dbr, buf, opts, &lReq, pfl))
throw DBError(sts, (*this)->name, __func__);

if(nReq)
*nReq = lReq;
}

void DBChan::put_unlock(short dbr, void *buf, size_t nReq) const
{
if(auto sts = dbChannelPut(ptr(), dbr, buf, nReq))
throw DBError(sts, (*this)->name, __func__);
}

void DBChan::put_lock(short dbr, void *buf, size_t nReq) const
{
if(auto sts = dbChannelPut(ptr(), dbr, buf, nReq))
throw DBError(sts, (*this)->name, __func__);
}

DBCred::DBCred(const server::ClientCredentials &cc)
{
auto n = cc.peer.find_first_of(':');
host = cc.peer.substr(0, n);
if(cc.method=="ca") {
n = cc.account.find_last_of('/');
if(n == std::string::npos) {
cred.push_back(cc.account);
} else {
cred.push_back(cc.account.substr(n+1));
}
} else {
cred.push_back(SB()<<cc.method<<'/'<<cc.account);
}

for(const auto& role : cc.roles()) {
cred.push_back(SB()<<"role/"<<role);
}
}

void ASClient::update(dbChannel *ch, DBCred &cred)
{
ASClient temp;
temp.cli.resize(cred.cred.size(), nullptr);

for(size_t i=0, N=temp.cli.size(); i<N; i++) {
/* asAddClient() fails secure to no-permission */
(void)asAddClient(&temp.cli[i],
dbChannelRecord(ch)->asp,
dbChannelFldDes(ch)->as_level,
cred.cred[i].c_str(),
cred.host.data());
}

cli.swap(temp.cli);
}

ASClient::~ASClient()
{
for(auto asc : cli) {
asRemoveClient(&asc);
}
}

}} // namespace pvxs::db
157 changes: 157 additions & 0 deletions ioc/dbutils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvxs is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/

#ifndef DBUTILS_H
#define DBUTILS_H

#include <string>
#include <memory>
#include <vector>
#include <stdexcept>

#include <dbAccess.h>
#include <dbStaticLib.h>
#include <asLib.h>

#include <pvxs/source.h>

// hooks for std::unique_ptr
namespace std {
template<>
struct default_delete<dbChannel> {
inline void operator()(dbChannel* ch) { if(ch) dbChannelDelete(ch); }
};
} // namespace std

namespace pvxs {namespace db {

struct DBError : public std::runtime_error {
long status;
DBError(long status, const char* pv, const char* op);
virtual ~DBError();
};

class DBEntry {
DBENTRY ent;
public:
inline
DBEntry() noexcept {
dbInitEntry(pdbbase, &ent);
}

#if EPICS_VERSION_INT >= VERSION_INT(3,16,1,0)
inline
explicit DBEntry(dbCommon *com) noexcept
{
dbInitEntryFromRecord(com, &ent);
}
#else
explicit DBEntry(dbCommon *com);
#endif

inline
explicit DBEntry(const dbChannel *chan) noexcept
:DBEntry(dbChannelRecord(chan))
{}

inline
~DBEntry() {
dbFinishEntry(&ent);
}

template<typename Rec=dbCommon>
inline
Rec* record() const noexcept {
static_assert (sizeof(Rec::dpvt)==sizeof(void*),
"Rec must be dbCommon or specific recordType");
return ent.precnode ? (Rec*)ent.precnode->precord : nullptr;
}

inline
const char* info(const char* key, const char* def=nullptr) noexcept {
if(dbFindInfo(&ent, key))
return def;
return dbGetInfoString(&ent);
}

bool firstRecord() noexcept;
bool nextRecord() noexcept;
};

struct Records {
class iterator {
dbRecordNode *node = nullptr;
public:
iterator() = default;

bool operator==(const iterator& o) const { return node == o.node; }
bool operator!=(const iterator& o) const { return node != o.node; }
dbCommon* operator*() const { return (dbCommon*)node->precord; }

iterator& operator++() noexcept;
iterator operator++(int) noexcept {
iterator ret(*this);
++(*this);
return ret;
}
};

iterator begin() const noexcept;
inline
iterator end() const { return iterator(); }
};


class DBChan {
std::unique_ptr<dbChannel> ch;
public:

DBChan() = default;
explicit DBChan(dbChannel *ch);
explicit DBChan(const char *pv);
inline
explicit DBChan(const std::string& pv) :DBChan(pv.c_str()) {}

inline
explicit operator bool() const noexcept { return ch.operator bool(); }
inline
dbChannel& operator*() const noexcept { return *ch; }
inline
dbChannel* operator->() const noexcept { return ch.get(); }
inline
dbChannel* ptr() const noexcept { return ch.get(); }

// dbGet()
void get_unlock(short dbr, void *buf, long *opts, size_t *nReq, db_field_log *pfl) const;
// dbGetField()
void get_lock(short dbr, void *buf, long *opts, size_t *nReq, db_field_log *pfl) const;
void put_unlock(short dbr, void *buf, size_t nReq) const;
void put_lock(short dbr, void *buf, size_t nReq) const;
};

struct DBCred {
// eg.
// "username" implies "ca/" prefix
// "krb/principle"
// "role/groupname"
std::vector<std::string> cred;
std::string host;
explicit DBCred(const server::ClientCredentials& cred);
DBCred(const DBCred&) = delete;
DBCred(DBCred&&) = default;
};

struct ASClient {
std::vector<ASCLIENTPVT> cli;
~ASClient();
void update(dbChannel* ch, DBCred& cred);
};

} // namespace pvxs::db

}

#endif // DBUTILS_H
Loading