Here is an extensive example. At first, it look a little intrusive, but don't dispair.
It is a command line application that is called with various pdcom commands like list, find, query, subscribe, etc.
At first a couple of helper functions are defined. You can skip these initially.
Thereafter comes a Process implementation. This is also an extensive class definition because it contains SASL login details and network connection details which you can skip initially. Pay attention to read(), write(), flush() and connected().
Then have a look at the command implementations.
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <sstream>
#include <algorithm>
#include <stdexcept>
#include <iostream>
#include <iterator>
#include <cstring>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include <netdb.h>
#include <errno.h>
#include <map>
#include <ostream>
#ifdef SASL
# include <sasl/sasl.h>
# include <sasl/saslutil.h>
int sasl_init = sasl_client_init(0);
#endif
template <class T>
void print(std::ostream& os, T const* p)
{
os << *p;
}
template <>
void print(std::ostream& os, uint8_t const* p)
{
os << unsigned(*p);
}
template <>
void print(std::ostream& os, int8_t const* p)
{
os << int(*p);
}
template <class T>
struct Printer
{
Printer(const T* begin, const T* end):
begin(begin), end(end) {}
friend std::ostream&
operator<<(std::ostream& os,
const Printer& obj) {
char delim = 0;
for (const T* p = obj.begin; p != obj.end; ++p) {
if (delim)
os << delim;
delim = ',';
print(os, p);
}
return os;
}
const T* begin;
const T* const end;
};
template <class T>
bool read(const char* input, T* p, size_t nelem)
{
std::istringstream is(input);
do {
is >> *p++;
if (!--nelem)
return is.fail();
is.ignore(1);
} while (true);
}
template <>
bool read(const char* input, uint8_t* p, size_t nelem)
{
std::istringstream is(input);
unsigned val;
do {
is >> val;
*p++ = val;
if (!--nelem)
return is.fail();
is.ignore(1);
} while (true);
}
template <>
bool read(const char* input, int8_t* p, size_t nelem)
{
std::istringstream is(input);
int val;
do {
is >> val;
*p++ = val;
if (!--nelem)
return is.fail();
is.ignore(1);
} while (true);
}
int usage(const char* progname)
{
std::cout << "Usage: " << progname
<< " addr"
<< " <command> <command arguments>"
<< std::endl
<< "Arguments:" << std::endl
<< " addr: server<:port> (default port = 2345)" << std::endl
<< " command: (query, login, set, list, find, stream)" << std::endl
<< std::endl
<< " query <path>" << std::endl
<< " Query a process variable" << std::endl
<< std::endl
<< " login <user>" << std::endl
<< " Login to the server" << std::endl
<< std::endl
<< " set <path> <value>" << std::endl
<< " Set a process parameter value" << std::endl
<< std::endl
<< " find <path>" << std::endl
<< " Find a single variable. " << std::endl
<< std::endl
<< " list <path>" << std::endl
<< " List process variables in path. " << std::endl
<< " empty|'': List all process variables" << std::endl
<< " '/' : List root directory" << std::endl
<< " '/dir' : List directory /dir" << std::endl
<< std::endl
<< " stream <count> <decimation> <path>+" << std::endl
<< " Subscribe to a variable/s with decimation" << std::endl
<< " Exit after <count> data has arrived" << std::endl
<< std::endl;
return 0;
}
{
int sfd;
FILE *stream;
bool init;
bool busy;
std::string localIP;
std::string remoteIP;
std::string serverFQDN;
bool loginFinished;
bool loginSuccess;
const char* sasl_data;
{
init = false;
loginFinished = false;
foundVariable = 0;
}
int read(
char *buf,
size_t n)
{
return ::read(sfd, buf, n);
}
void write(
const char *buf,
size_t n)
{
::fwrite(buf, 1, n, stream);
}
{
::fflush(stream);
}
{
init = true;
}
#ifdef SASL
std::map<std::string, std::string> sasl_option;
static int cb_getopt(void *context, const char *plugin_name,
const char *option, const char **result, unsigned *len)
{
Process* process = reinterpret_cast<Process*>(context);
printf("%s(context=%p, plugin_name=%s, option=%s, result=%p len=%p)\n",
__func__, context, plugin_name, option, result, len);
std::string& opt = process->sasl_option[option];
std::getline(std::cin, opt);
if (!opt.empty()) {
*result = opt.c_str();
if (len)
*len = opt.size();
return SASL_OK;
}
return SASL_BADPARAM;
}
static int cb_simple(void *context, int id,
const char **result, unsigned *len)
{
Process* process = reinterpret_cast<Process*>(context);
printf("%s, context=%p, id=%X, result=%p, len=%p\n",
__func__, context, id, result, len);
const char* option;
switch (id) {
case SASL_CB_USER:
option = "userid";
break;
case SASL_CB_AUTHNAME:
option = "userid";
break;
case SASL_CB_LANGUAGE:
option = "language";
*result = 0;
break;
default:
return SASL_BADPARAM;
}
std::cout << option << ": " << std::flush;
std::string& opt = process->sasl_option[option];
std::getline(std::cin, opt);
if (opt.size()) {
*result = opt.c_str();
if (len)
*len = opt.size();
}
return SASL_OK;
}
char pass_buffer[100];
static int cb_getsecret(sasl_conn_t* , void *context, int id,
sasl_secret_t **psecret)
{
Process* process = reinterpret_cast<Process*>(context);
printf("%s, context=%p, id=%i, psecret=%p\n",
__func__, context, id, psecret);
sasl_secret_t *secret =
reinterpret_cast<sasl_secret_t*>(process->pass_buffer);
std::cout << "secret: " << std::flush;
std::string str;
std::getline(std::cin, str);
std::copy(str.begin(), str.end(), (char*)secret->data);
secret->len = str.size();
*psecret = secret;
return SASL_OK;
}
static int cb_chalprompt(void *context, int id,
const char *challenge,
const char *prompt, const char *defresult,
const char **result, unsigned *len)
{
Process* process = reinterpret_cast<Process*>(context);
printf("%s\n", __func__);
*result = 0;
std::cout << challenge << std::endl;
std::cout << prompt << " [" << defresult << "]: " << std::flush;
const char* option;
switch (id) {
case SASL_CB_ECHOPROMPT:
option = "chalprompt";
break;
case SASL_CB_NOECHOPROMPT:
default:
option = "chalnoprompt";
break;
};
std::string& opt = process->sasl_option[option];
std::getline(std::cin, opt);
if (opt.size()) {
*result = opt.c_str();
if (len)
*len = opt.size();
}
return SASL_OK;
}
static int cb_getrealm(void* , int ,
const char** , const char** result)
{
printf("%s\n", __func__);
*result = 0;
return SASL_OK;
}
#endif //SASL
{
#ifdef SASL
typedef int (*cb_t)(void);
sasl_callback_t cb[] = {
{SASL_CB_USER, 0, this},
{SASL_CB_AUTHNAME, 0, this},
{SASL_CB_LANGUAGE, 0, this},
{SASL_CB_PASS, 0, this},
{SASL_CB_ECHOPROMPT, 0, this},
{SASL_CB_NOECHOPROMPT, 0, this},
{SASL_CB_GETREALM, 0, this},
{SASL_CB_LIST_END, 0, 0},
{SASL_CB_GETOPT, cb_t(cb_getopt), this},
{SASL_CB_USER, cb_t(cb_simple), this},
{SASL_CB_AUTHNAME, cb_t(cb_simple), this},
{SASL_CB_LANGUAGE, cb_t(cb_simple), this},
{SASL_CB_PASS, cb_t(cb_getsecret), this},
{SASL_CB_ECHOPROMPT, cb_t(cb_chalprompt), this},
{SASL_CB_NOECHOPROMPT, cb_t(cb_chalprompt), this},
{SASL_CB_GETREALM, cb_t(cb_getrealm), this},
{SASL_CB_LIST_END, 0, 0},
};
if (sasl_init != SASL_OK)
return;
std::cout
<< "Creating new client instance: sasl_client_new()"
<< std::endl;
sasl_conn_t *conn = NULL;
int result = sasl_client_new("pdserv",
serverFQDN.c_str(),
localIP.c_str(),
remoteIP.c_str(),
cb,
SASL_SUCCESS_DATA,
&conn);
if (result != SASL_OK)
return;
std::cout
<< "Waiting for mechanism list from server..."
<< std::flush;
sasl_data = 0;
while (!sasl_data)
asyncData();
std::cout << sasl_data << std::endl;
std::cout
<< "Starting sasl client: sasl_client_start()"
<< std::endl;
sasl_interact_t *prompt = 0;
const char* clientout = 0, *mech;
unsigned int clientoutlen;
do {
result = sasl_client_start(conn, sasl_data,
&prompt, &clientout, &clientoutlen, &mech);
std::cout << "sasl_client_start() result(" << __LINE__ << ")="
<< sasl_errstring(result,0,0) << std::endl;
if (result == SASL_INTERACT) {
std::cout << "sasl_client_start() needs data:" << std::endl
<< prompt->challenge
<< ' ' << prompt->prompt
<< " [" << prompt->defresult << "] :" << std::flush;
std::string& opt =
sasl_option[std::string(prompt->challenge) + prompt->prompt];
std::getline(std::cin, opt);
prompt->result = opt.c_str();
prompt->len = opt.size();
}
} while (result == SASL_INTERACT);
if (mech)
std::cout << "Using mechanism: " << mech << std::endl;
while (mech or clientout) {
unsigned int len = (clientoutlen+2)/3*4+1;
char data[len];
result = sasl_encode64(clientout, clientoutlen, data, len, &len);
std::cout << "sasl_encode64() result(" << __LINE__ << ")="
<< sasl_errstring(result,0,0) << std::endl;
if (clientout and clientoutlen) {
std::cout << "Data to server:"
<< std::string(clientout, clientoutlen)
<< " (" << data << ')' << std::endl;
}
mech = 0;
sasl_data = 0;
while (!(sasl_data or loginFinished))
asyncData();
if (!sasl_data)
break;
len = strlen(sasl_data);
unsigned int serveroutlen = (len+3)/4*3+1;
char serverout[serveroutlen];
result = sasl_decode64(sasl_data, len,
serverout, serveroutlen, &serveroutlen);
std::cout << "sasl_decode64() result(" << __LINE__ << ")="
<< sasl_errstring(result,0,0) << std::endl;
prompt = 0;
do {
result = sasl_client_step(conn,
serverout, serveroutlen,
&prompt, &clientout, &clientoutlen);
std::cout << "sasl_client_step() result(" << __LINE__ << ")="
<< sasl_errstring(result,0,0) << std::endl;
if (result == SASL_INTERACT) {
for (sasl_interact_t *p = prompt; p->id != SASL_CB_LIST_END; p++) {
std::cout << "sasl_client_start() needs data:" << std::endl
<< p->challenge;
if (p->prompt)
std::cout << ' ' << p->prompt;
if (p->defresult)
std::cout << " [" << p->defresult << "] :";
std::cout << std::flush;
std::string key;
key += p->challenge;
key += p->prompt;
std::string& opt = sasl_option[key];
std::getline(std::cin, opt);
p->result = opt.c_str();
p->len = opt.size();
}
}
} while (result == SASL_INTERACT);
}
if (!loginFinished)
std::cout << "Hmm, problem here!!!!!!!!!!!!!!" << std::endl;
if (result != SASL_OK)
std::cout << "Result is not SASL_OK!!!!!!" << std::endl;
if (loginSuccess) {
std::cout << "YAY!!!!!!!!! Logged in" << std::endl;
}
else
std::cout << "NAH, Login unsuccessful" << std::endl;
#endif
}
void loginReply(
const char* serverData,
bool finished,
bool success)
{
#ifdef SASL
sasl_data = serverData;
loginFinished = finished;
loginSuccess = success;
std::cout << __func__ << __LINE__;
if (serverData)
std::cout << " serverdata=" << serverData;
std::cout
<< " finished=" << finished
<< " success=" << success << std::endl;
#endif
}
{
busy = false;
foundVariable = var;
if (var) {
std::cout << "Found the following variables: " << std::endl;
std::cout << var->
path() << std::endl;
}
else
std::cout << "Could not find variable" << std::endl;
}
std::list<const PdCom::Variable*>& variableList,
std::list<std::string>& directoryList)
{
busy = false;
std::cout << "Found the following variables: " << std::endl;
while (variableList.size()) {
variableList.pop_front();
std::cout << v->
path() << std::endl;
}
std::cout << "Found the following directories: " << std::endl;
std::copy(directoryList.begin(), directoryList.end(),
std::ostream_iterator<std::string>(std::cout, "/\n"));
}
int connect(const std::string& addr)
{
std::istringstream is(addr);
std::string host, port;
struct addrinfo hints;
struct addrinfo *result, *rp;
int s;
int rv;
if (is.peek() == '[') {
is.get();
std::getline(is, host, ']');
if (is.peek() == ':')
is.get();
}
else {
std::getline(is, host, ':');
}
if (is.fail())
return -EINVAL;
if (is.eof())
port = "2345";
else if (std::getline(is, port).fail())
return -EINVAL;
::memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0;
s = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", ::gai_strerror(s));
return -EINVAL;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
if (::connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) {
char host[NI_MAXHOST], service[NI_MAXSERV];
int rv = getnameinfo(rp->ai_addr, rp->ai_addrlen,
host, NI_MAXHOST,
service, NI_MAXSERV,
NI_NUMERICSERV);
if (rv) {
std::cerr << gai_strerror(rv) << std::endl;
}
else {
serverFQDN = host;
}
char ipaddr[INET6_ADDRSTRLEN];
const void *field = 0;
switch (rp->ai_family) {
case AF_INET:
field = &((struct sockaddr_in*)rp->ai_addr)->sin_addr;
break;
case AF_INET6:
field = &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr;
break;
}
if (field and inet_ntop(rp->ai_family, field,
ipaddr, INET6_ADDRSTRLEN)) {
remoteIP = ipaddr;
remoteIP.append(1,';');
remoteIP.append(service);
}
struct sockaddr_storage localaddr;
socklen_t len = sizeof(localaddr);
uint16_t port = 0;
field = 0;
if (!getsockname(sfd, (struct sockaddr*)&localaddr, &len)) {
switch (rp->ai_family) {
case AF_INET:
{
struct sockaddr_in* in =
(struct sockaddr_in*)&localaddr;
port = htons(in->sin_port);
field = &in->sin_addr;
}
break;
case AF_INET6:
{
struct sockaddr_in6* in6 =
(struct sockaddr_in6*)&localaddr;
port = htons(in6->sin6_port);
field = &in6->sin6_addr;
}
break;
}
}
if (field and inet_ntop(rp->ai_family, field,
ipaddr, INET6_ADDRSTRLEN)) {
std::ostringstream os;
os << ipaddr << ';' << port;
localIP = os.str();
}
std::cout << serverFQDN << ' '
<< remoteIP << ' ' << localIP << std::endl;
break;
}
::close(sfd);
}
freeaddrinfo(result);
if (rp == NULL) {
fprintf(stderr, "Could not connect\n");
return -ENODEV;
}
if (!(stream = ::fdopen(sfd, "r+"))) {
rv = errno;
::perror("fdopen()");
return rv;
}
while (!init)
return rv;
return 0;
}
};
int list(Process& process, int argc, const char* const* argv)
{
process.list(argc ? *argv : std::string());
process.busy = true;
while (process.busy)
process.asyncData();
return argc > 0;
}
int find(Process& process, int argc, const char* const* argv)
{
if (argc and *argv) {
if (!process.find(*argv)) {
process.busy = true;
while (process.busy)
process.asyncData();
}
}
return argc > 0;
}
int login(Process& process, int , const char* const* )
{
process.login();
return 0;
}
int query(Process& process, int argc, const char* const* argv)
{
Subscriber() {
notvalid = false;
subscription = 0;
time_ns = 0;
}
printf("1 new values %zu %p\n", time_ns, s);
};
this->time_ns = time_ns;
printf("2 new values %zu %p\n", time_ns, subscription);
};
void active(
const std::string& ,
subscription = s;
}
void invalid(
const std::string& path,
int id) {
std::cout << "Could not find " << path
<< " with request id " << id << std::endl;
notvalid = true;
}
bool notvalid;
uint64_t time_ns;
};
if (argc < 1)
return 0;
Subscriber subscriber;
process.subscribe(&subscriber, *argv, -1, 25);
while (!subscriber.notvalid and !subscriber.subscription
and !subscriber.time_ns and process.asyncData() > 0);
if (subscriber.subscription) {
std::cout
<<
"name: " << var->
name() << std::endl
<<
"path: " << var->
path() << std::endl
<<
"sample time: " << var->
sampleTime << std::endl;
if (!subscriber.time_ns and subscriber.subscription->poll())
std::cout << "Cannot poll "
<< subscriber.subscription->variable->path() << std::endl;
while (!subscriber.time_ns and process.asyncData() > 0);
std::cout << "getString value = "
<< subscriber.subscription->getStringValue(',') << std::endl;
std::cout << " ostream value = "
<< *subscriber.subscription << std::endl;
{
subscriber.subscription->getValue(val, 0, var->
nelem);
std::cout << " boolean value = "
<< Printer<bool>(val, val + var->
nelem) << std::endl;
}
{
uint16_t val[var->
nelem];
subscriber.subscription->getValue(val, 0, var->
nelem);
std::cout << " uint16_t value = "
<< Printer<uint16_t>(val, val + var->
nelem) << std::endl;
}
{
subscriber.subscription->getValue(val, 0, var->
nelem);
std::cout << " float value = "
<< Printer<float>(val, val + var->
nelem) << std::endl;
}
}
process.unsubscribe(&subscriber);
return 1;
}
int set(Process& process, int argc, const char* const* argv)
{
if (argc < 2)
return argc;
process.foundVariable = 0;
if (!process.find(*argv)) {
process.busy = true;
while (process.busy)
process.asyncData();
}
if (v) {
{
if (read(argv[1], data, v->
nelem)
std::cout << "Error: could not parse " << argv[1]
<<
" as a double list with " << v->
nelem << " elements" << std::endl;
}
std::cout << "Error: could not parse " << argv[1]
<<
" as a " << v->
ctype <<
" list with " << v->
nelem << " elements" << std::endl;
}
else
std::cout << *argv << ": not found" << std::endl;
return 2;
}
int stream(Process& p, const int argc, const char* const* argv)
{
{
Subscriber(): count(0)
{
}
{
std::cout << **subscription->
time_ns <<
": ";
count++;
}
{
std::cout << time_ns << ": ";
for (SubscriptionList::iterator it = list.begin();
it != list.end(); ++it) {
SubscriptionList::value_type& subscription = *it;
<< " = " << subscription->getStringValue() << std::endl;
}
count++;
}
void active(
const std::string& path,
{
std::cout << "Subscription " << path
<< " is active" << std::endl;
list.push_back(s);
}
void invalid(
const std::string& path,
int id)
{
std::cout << "Request path " << path << " with id " << id
<< " does not exist" << std::endl;
}
int count;
typedef std::list<const PdCom::Variable::Subscription*>
SubscriptionList;
SubscriptionList list;
};
if (argc < 3)
return argc;
int n = ::atoi(argv[0]);
double interval = ::atof(argv[1]);
{
Subscriber subscriber;
for (int i = 2; i < argc; ++i)
p.subscribe(&subscriber, argv[i], interval, i);
while (subscriber.count < n)
p.asyncData();
p.unsubscribe(&subscriber);
}
return argc;
}
int main(int argc, char **argv)
{
if (argc < 2)
return usage(argv[0]);
Process p;
int rv;
if ((rv = p.connect(argv[1])))
return rv;
typedef int (*command_T)(Process& p, int argc, const char* const* argv);
command_T command = 0;
int it = 2;
while (it != argc) {
if (!strcmp(argv[it], "query"))
command = query;
else if (!strcmp(argv[it], "login"))
command = login;
else if (!strcmp(argv[it], "set"))
command = set;
else if (!strcmp(argv[it], "list"))
command = list;
else if (!strcmp(argv[it], "find"))
command = find;
else if (!strcmp(argv[it], "stream"))
command = stream;
else {
std::cerr << "Unknown command " << argv[it] << std::endl;
usage(argv[0]);
return 0;
}
++it;
if (command)
it += command(p, argc - it, argv + it);
}
return 0;
}