I am trying to write an AMQP 1.0 client using Qpid Proton in C. I do not want to use messenger. I want to use the proton-c engine. I am having a little trouble figuring out how to do this. My main sticking point is setting the endpoint for the connection. The only example of a C client using the proton-c engine I can find is here.
https://github.com/apache/qpid-proton/blob/master/examples/engine/c/psend.c
However, it uses structs that are not part of the Qpid Proton C API 0.12.0. Specifically, I don’t see pn_driver_t or pn_connector_t as part of the 0.12.0 API.
I am trying to follow the general workflow defined in the AMQP 1.0 spec 1) create a connection, 2) create a session, 3) create a sender link. I am not very experienced with C and this is my first time using the non-messenger part of the Qpid Proton library so forgive me if I missed something obvious. Here is my current code. I have been trying different options and searching for days.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "proton/message.h"
#include "proton/messenger.h"
#include "proton/connection.h"
#include "proton/session.h"
#include "proton/link.h"
#include "proton/delivery.h"
#include "proton/event.h"
#include "proton/engine.h"
//State integer values are defined in Connection macros
//https://qpid.apache.org/releases/qpid-proton-0.12.0/proton/c/api/group__connection.html
void print_state(char * name, pn_state_t state)
{
printf("[%s] local: %i, remote: %i\n", name, PN_LOCAL_MASK & state, PN_REMOTE_MASK & state);
}
//Reference https://github.com/apache/qpid-proton/blob/master/examples/engine/c/psend.c
void send_engine()
{
struct pn_connection_t * connection;
connection = pn_connection();
//STACKOVERFLOW - I have a feeling this is not right, but cannot find an alternative to set protocol (amqp or ws) the hostname and port. I see a way to set the hostname only
pn_connection_set_container(connection, "amqp://amqpserver:port");
print_state("Connection Initialized", pn_connection_state(connection));
pn_transport_t * transport;
transport = pn_transport();
int r = pn_transport_bind(transport, connection);
if (r != 0)
{
printf("transport bind error: %i\n", r);
}
pn_connection_open(connection);
print_state("Connection Opened", pn_connection_state(connection));
pn_session_t * sess;
sess = pn_session(connection);
print_state("Session Initialized", pn_session_state(sess));
pn_session_open(sess);
print_state("Session Opened", pn_session_state(sess));
pn_link_t * sender;
sender = pn_sender(sess, "c-client");
//the queue name in "toserver"
pn_terminus_set_address(pn_link_target(sender), "toserver");
print_state("Sender Link Initialized", pn_link_state(sender));
pn_link_open(sender);
print_state("Sender Link Opened", pn_link_state(sender));
pn_delivery_t *delivery;
char *tagID = "uid";
delivery = pn_delivery(sender, pn_dtag(tagID, strlen(tagID)));
char *msg = "abc";
printf("%zd\n", pn_link_send(sender, msg, strlen(msg)));
pn_delivery_settle(delivery);
printf("Delivery stettled %d\n", pn_delivery_settled(delivery));
print_state("Connection End", pn_connection_state(connection));
print_state("Session End", pn_session_state(sess));
print_state("Sender Link End", pn_link_state(sender));
//TODO free everything
}
int main (int argc, char *argv[])
{
send_engine();
printf("done\n");
return 0;
}
On my AMQP server I have enable frame level tracing and do not see any communication from the client. This is not a problem with the server. It works with many other clients, including a C client using the messenger API. What am I missing in this sample? Thank you!
Note: I have tried to solve this exhaustively and provided as much context as possible.
The driver API was removed in Apache Qpid Proton release 0.9 in March, 2015.
commit 4b53bfca06432e440c95d60648b5e7be54ae4296
Author: Rafael Schloming
Date: Fri Feb 13 08:27:23 2015 -0500
removed driver API and bindings
C code examples in Proton are Messenger based.
A lot of recent work has gone into a reactor-based C++ implementation that is not Messenger based. See release 0.12.1 documentation for more details and examples.
Related
I am using BLUEZ and GLIB/D-BUS to connect 2 Raspberry Pi (also a laptop and a Raspberry Pi).
So far I could make fair progress.
EDIT: on good advises from #ukBaz I am using a python client from my laptop, and my C code server on the Raspberry Pi.
On the "server", I can register the device with a custom service UUID and a Serial RFCOMM profile UUID, and wait for connection. Connecting with the python client works and I can see that there is a handler available (see after code below for debug output)
I'm using this code (within a dbus loop, code simplified for readability):
static void new_connection(GDBusMethodInvocation *inv)
{
g_log(LOG_SERVER, G_LOG_LEVEL_MESSAGE, "New connection.");
GDBusMessage *msg = g_dbus_method_invocation_get_message(inv);
// This prints the output below this code snippet
gchar *content = g_dbus_message_print(msg, 2);
g_log(LOG_SERVER, G_LOG_LEVEL_INFO, "Message is:\n%s", content);
g_free(content);
GVariant *params = g_dbus_method_invocation_get_parameters(inv);
const char *object;
GVariant *properties;
gint32 *handle;
g_variant_get(params, "(oha{sv})", &object, &handle, &properties);
// Problem here, 'handle' is NULL
g_log(LOG_SERVER, G_LOG_LEVEL_INFO, "Object is [%s]\nHandle is [%ls]", object, handle);
GVariantIter iter;
g_variant_iter_init(&iter, properties);
display_properties(&iter);
}
Here is the output:
New connection.
Message is:
Type: method-call
Flags: none
Version: 0
Serial: 32
Headers:
path -> objectpath '/org/bluez/jscturret'
interface -> 'org.bluez.Profile1'
member -> 'NewConnection'
destination -> ':1.18'
sender -> ':1.11'
signature -> signature 'oha{sv}'
num-unix-fds -> uint32 1
Body: (objectpath '/org/bluez/hci0/dev_00_AA_AA_AA_AA_AA', handle 0, #a{sv} {})
UNIX File Descriptors:
fd 7: dev=0:8,mode=0140777,ino=41101,uid=0,gid=0,rdev=0:0,size=0,atime=0,mtime=0,ctime=0
Object is [/org/bluez/hci0/dev_00_AA_AA_AA_AA_AA]
Handle is [(null)]
It shows that there is a file descriptor fd 7 but when I read the GVariant parameter I get NULL.
How can I access the file descriptor? My understanding is I need that to be able to read/write from/to the client.
I used https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/device-api.txt and https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/adapter-api.txt for reference and a few other posts here on SO.
Also got a lot of info in https://www.linumiz.com/.
Current full code is available here: btservice
Oh! I am pretty sure you are supposed to send a pointer to an integer (not a pointer to a pointer to it).
You can do
gint32 handle; // instead of gint32 *handle;
and it should work.
This API has a very poor design (relying on variadic, with format specifiers... the reason why people dislike C).
I have a C program which fills and calculates the data structures like
struct Users
{
unsigned char login[LOGIN_SIZE+1];
unsigned int hours, minut, works;
struct Range {
unsigned int hours_a, minut_a, time_a;
unsigned int hours_b, minut_b, time_b;
unsigned int dur_hours, dur_minut, dur_time;
struct WorkIDs {
unsigned int work_id;
unsigned int filled;
} work;
unsigned int filled;
} range[RANGE_SIZE];
unsigned int verbosity;
unsigned int filled;
} users[USERS_SIZE];
Now I need to export the filled data structures from C program to any of the Grafana-supported databases, i.e.: InfluxDB, MySQL or PostgreSQL (full list is available at https://grafana.com/docs/grafana/latest/features/datasources/ and may be expanded with plugins)
However, I couldn't find any good example of exporting a C filled data structure to a database, and struggle establishing a connection between these technologies. Please could you advise a right approach?
My working approach (feel free to suggest the alternatives!)
1) use a PostgreSQL : unlike MySQL which gave me a lot of various errors until I gave up, the installation of PostgreSQL was really quick and straightforward, just follow an article like https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04 to quickly get started with your first example database.
2) take a libpqxx library : they give a great C++ example at documentation http://pqxx.org/development/libpqxx/ , which could be built with a command like c++ -o post post.cxx -lpqxx -lpq. Your libpqxx library needs to be above a certain version for this example to work! On a Linux Mint 19.3, I had to download libpqxx-6.2_6.2.5-1_amd64.deb and libpqxx-dev_6.2.5-1_amd64.deb of "The Eoan Ermine" from https://launchpad.net/ubuntu/+source/libpqxx : because building a fresh libpqxx from source isn't that easy, while its' version at Linux Mint distro's official repositories is way too old and not sufficient.
Connection string at this C++ example could be replaced with something like
pqxx::connection c("dbname=anonymous host=localhost user=anonymous password=123"); and then it works. Whole modified source code, to match a tutorial from 1 - paint all the playgrounds to a color of your choice:
#include <iostream>
#include <pqxx/pqxx>
int main(int, char *argv[])
{
pqxx::connection c("dbname=anonymous host=localhost user=anonymous password=123");
pqxx::work txn(c);
int equip_id = 1;
std::cout << "Updating equip_id #" << equip_id << " and others" << std::endl;
for (equip_id = 1; equip_id < 3; equip_id++) {
txn.exec0(
"UPDATE playground "
"SET color = " + txn.quote(argv[1]) +
"WHERE equip_id = " + txn.quote(equip_id));
}
// Make our change definite.
txn.commit();
}
Then, it's just a matter of time before you could export the C structure data fields into this PostgreSQL database using lots of SQL queries in a cycle; although, you'd also need to come up with a good database structure and add it to PostgreSQL using a psql interactive command line before doing this.
3) Install Grafana and configure PostgreSQL data source - using the tutorials like
https://grafana.com/docs/grafana/latest/installation/debian/
https://grafana.com/docs/grafana/latest/getting-started/getting-started/
https://grafana.com/docs/grafana/latest/features/datasources/postgres/
I had to disable a SSL mode at Data Sources / PostgreSQL configuration to successfully connect to a database using the same username/password as your C++ program (didn't need to specify a port).
I have 15 years old C program which connects to IBM MQ. It has part of code, which is trying to synchronize something, when has several MQ, which tales from database.
However now we have only one MQ. Do I have to use this method of synchronization?
#include "dte_mq.h"
#include <string.h>
#include <stdlib.h>
typedef struct tagDTE_QUEUE_DESCRIPTOR
{
MQHOBJ handle;
int IsSyncpointControled;
} DTE_QUEUE_DESCRIPTOR, *PDTE_QUEUE_DESCRIPTOR;
static MQHCONN sHConn = 0;
static MQLONG sCompCode = MQCC_OK;
static MQLONG sReason = MQRC_NONE;
static int sNumOpenQueues = 0;
static PDTE_QUEUE_DESCRIPTOR sQueues = NULL;
#define MAX_NUM_OPEN_QUEUES 10
for(i = 0; i < MAX_NUM_OPEN_QUEUES; i++)
{
if(sQueues[i].handle == -1)
{
*qd = i;
sQueues[i].handle = hObj;
sQueues[i].IsSyncpointControled = 0;
break;
}
}
Do we need now this logic, if the value of q=-1 and in the loop it gets 0 and out of the loop?
If you need it, I can write the whole code into the question.
I have 15 years old C program which connects to IBM MQ. It has part of
code, which is trying to synchronize something, when has several MQ,
which tales from database.
Why don't you read the documentation for the program and the comments in the code to determine what it is doing and/or not doing and whether or not the code needs to be modified.
Secondly, if it has been working for 15 years then why are you messing with something you don't understand?
Third, why don't you hire a qualified MQ contractor to review the code and documentation and do whatever is required to the program? Because you have posted 7 questions on StackOverflow all related to the same program.
I would like to know how I can obtain a list of all Xorg displays on my system, along with a list of screens associated with each display. I spent some time looking through the Xlib documentation, but was not able to find a function that does what I want. Please assume that I have no other dependencies other than a POSIX-complaint OS and X (e.g., no GTK). If what I ask is not possible assuming these minimal dependencies, then a solution using other libraries is fine.
Thank you very much for your help!
The only way I know of to get a list of displays is to check the /tmp/.X11-unix directory.
Once you do that, you can use Xlib to query each display for more information.
Per example:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <X11/Xlib.h>
int main(void) {
DIR* d = opendir("/tmp/.X11-unix");
if (d != NULL) {
struct dirent *dr;
while ((dr = readdir(d)) != NULL) {
if (dr->d_name[0] != 'X')
continue;
char display_name[64] = ":";
strcat(display_name, dr->d_name + 1);
Display *disp = XOpenDisplay(display_name);
if (disp != NULL) {
int count = XScreenCount(disp);
printf("Display %s has %d screens\n",
display_name, count);
int i;
for (i=0; i<count; i++)
printf(" %d: %dx%d\n",
i, XDisplayWidth(disp, i), XDisplayHeight(disp, i));
XCloseDisplay(disp);
}
}
closedir(d);
}
return 0;
}
Running the above gives me this output with my current displays/screens:
Display :0 has 1 screens
0: 3046x1050
Display :1 has 2 screens
0: 1366x768
1: 1680x1050
Never found a better way of listing X displays other than that. I'd very much like to know if any better alternative exists.
Like netcoder wrote, the problem has two distinct parts:
Connection to the X server
The process establishes a connection to an X server using XOpenDisplay(). The connection is torn down using XCloseDisplay(). netcoders code in this thread is a good example of how to do it correctly.
As netcoder mentioned, the problem is that there is no reliable way find out which X servers a process can connect to. His code checks the typical location where the X sockets are, /tmp/.X11-unix/. That approach does not work at all if the user is remotely connected, for example via SSH (with X forwarding enabled). In that case there is really only the DISPLAY environment variable (and perhaps some trickery wrt. ~/.Xauthority files).
Unfortunately, I do not know of any better method either. I personally prefer to use a per-user configuration file -- say ~/.application/displays --, where the user can list the server names the application should try to connect in the same format as the DISPLAY environment variable, in addition to the default one. It is not automatic (netcoder's code is), but this approach suits me better.
Finding out about the screens provided by an X server
XScreenCount() will return the number of screens provided by the X server the process is currently connected to. If you only need the screen dimensions, follow netcoders example. For more detailed information, use XScreenOfDisplay(Display,index) to obtain the Screen pointers; 0 <= index < XScreenCount(Display).
In C code, the macros ScreenCount() and ScreenOfDisplay() are usually a bit more efficient than the actual function calls.
I am porting an existing windows based C++ application to 64 bit environment, and this is one of those weird errors.
In the code snippet you can that I am using openforwardonly and it used to work fine with our old setup but in the 64 bit environment it gives the problem of fetching only ONE recordset. Or it could be a problem with the MoveNext(); of ADO.
To circumvent it we started using adOpenStatic, and it worked fine for me for a while but only later realized that it has a performance hit and it is taking forever to get/iterative through values.
I request someone to try this code with both the flags and validate the behavior I am seeing.
Information about ado flags:
http://www.w3schools.com/ADO/met_rs_open.asp
Another EE topic
http://www.experts-exchange.com/Programming/Languages/Visual_Basic/VB_DB/Q_11520558.html
I remember seeing a MS support case, but I can't get to it now.
I would appreciate any help or suggestions. I know we are using old technology, but we want to move to the additional capabilities without changing code much.
// Dbtest.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <time.h>
#import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
no_namespace rename("EOF", "EndOfFile")
int main(int argc, char* argv[])
{
HRESULT hr = S_OK;
try
{
CoInitialize(NULL);
_bstr_t strCnn("Provider=OraOLEDB.Oracle;Data Source =****; User Id=****; password=***");
_ConnectionPtr m_pConn;
hr=m_pConn.CreateInstance(__uuidof(Connection));
if(FAILED(hr))
{
printf("Failed creating record set instance\n");
return 0;
}
m_pConn->Open(strCnn,"","",NULL);
//Open the Record set for getting records from Author table
_RecordsetPtr pRstDoctors = NULL;
time_t start,end1,end2;
pRstDoctors.CreateInstance(__uuidof(Recordset));
time(&start);
pRstDoctors->Open("select logid from log",strCnn, adOpenForwardOnly,
**adLockReadOnly**,adCmdText);
//Declare a variable of type _bstr_t
int valField1;
//int valField2;
pRstDoctors->MoveFirst();
//Loop through the Record set
if (!pRstDoctors->EndOfFile)
{
while(!pRstDoctors->EndOfFile)
{
valField1 = pRstDoctors->Fields->GetItem("logid")->Value.intVal;
// valField2 = pRstDoctors->Fields->GetItem("reportid")->Value.intVal;
// printf("%d - \n",valField1);
pRstDoctors->MoveNext();
}
}
time(&end1);
double dif=difftime(end1,start);
printf("time difference is %.2lf",dif);
}
catch(_com_error e)
{
printf("Error:%s\n",e);
}
CoUninitialize();
return 0;
}
Using "adOpenStatic" instead of "adOpenForwardOnly" will work
pRstDoctors->Open("select logid from log",strCnn, adOpenStatic,
**adLockReadOnly**,adCmdText);