IBM MQ MQCONN from host to Docker container - c

Trying to connect from a C application running as an IBM MQ client on a host to an MQ server running as a docker container.
The client code taken from an IBM example is provided below.
The questions is which protocol is used when MQCONN is called as no IP/port is provided? I can only guess it is some kind of an IPC.
For this reason I'm running the docker container with --ipc="host" option, but it still fails with CompCode=2, Reason=2058
#include <cmqc.h>
⋮
static char Parm1[MQ_Q_MGR_NAME_LENGTH] ;
⋮
int main(int argc, char *argv[] )
{
/* */
/* Variables for MQ calls */
/* */
MQHCONN Hconn; /* Connection handle */
MQLONG CompCode; /* Completion code */
MQLONG Reason; /* Qualifying reason */
⋮
/* Copy the queue manager name, passed in the */
/* parm field, to Parm1 */
strncpy(Parm1,argv[1],MQ_Q_MGR_NAME_LENGTH);
⋮
/* */
/* Connect to the specified queue manager. */
/* Test the output of the connect call. If the */
/* call fails, print an error message showing the */
/* completion code and reason code, then leave the */
/* program. */
/* */
MQCONN(Parm1,
&Hconn,
&CompCode,
&Reason);
if ((CompCode != MQCC_OK) | (Reason != MQRC_NONE))
{
sprintf(pBuff, MESSAGE_4_E,
ERROR_IN_MQCONN, CompCode, Reason);
PrintLine(pBuff);
RetCode = CSQ4_ERROR;
goto AbnormalExit2;
}
⋮
}

If your program will be running on the same server as the queue manager then it is best to link it with 'mqm.lib' (bindings mode) rather than 'mqic.lib' (client mode).
In the MQ KnowLedge Center, there are examples on how to compile and link your C program. The examples are listed as: 'C client application' (client mode) and 'C server application' (bindings mode).
If in the future, your C program needs to connect to a remote queue manager then you need to link it for 'client mode'.
To configure your C program to handle proper MQ security, change the MQCONN API call to MQCONNX API call.
i.e.
MQCNO cno = {MQCNO_DEFAULT};
MQCD cd = {MQCD_CLIENT_CONN_DEFAULT};
MQCSP csp = {MQCSP_DEFAULT};
strncpy(cd.ConnectionName, hostname, MQ_CONN_NAME_LENGTH);
strncpy(cd.ChannelName, channelName, MQ_CHANNEL_NAME_LENGTH);
csp.AuthenticationType = MQCSP_AUTH_USER_ID_AND_PWD;
csp.CSPUserIdPtr = &myUserId;
csp.CSPUserIdOffset = 0;
csp.CSPUserIdLength = strlen(myUserId);
csp.CSPPasswordPtr = &myPassword;
csp.CSPPasswordOffset = 0;
csp.CSPPasswordLength = strlen(myPassword);
cno.cdPtr = &cd;
cno.Version = MQCNO_CURRENT_VERSION;
cno.SecurityParmsPtr = &csp;
cno.SecurityParmsOffset = 0;
MQCONNX(QMgrName, &cno, &Hcon, &CompCode, &Reason);
A few years ago, I wrote a blog posting called: MQ API Verbs that IBM Forgot!!. I created wraps for MQCONN and MQCONNX that will allow the program to pass UserId & Password for MQCONN and MQCONNX API calls. You may find it easier to simply use the wrappers.

Related

gstreamer: multiple RTSP clients connecting at the same time makes the video stream crash

Quick summary
video stream crashes if multiple clients connect at the same time due to the clients (all but 1) that skip the media-configure callback trying to change the bitrate by accessing a not yet configured pipeline. I'm asking how to wait with calling change_bitrate as long as the configure-media callback hasn't yet finished.
Detailed overview
I'm developing a door phone application that shows video footage of a user (that just rang the door) over the RTSP protocol on one or multiple screens (called clients from now on) in e.g. an appartment building.
When the application is running, it will not create a pipeline before the first client has connected. A new client callback is created in the following way:
/* Configure Callbacks */
/* Create new client handler (Called on new client connect) */
LOG_debug("Creating 'client-connected' signal handler");
g_signal_connect(info.server, "client-connected", G_CALLBACK(new_client_handler), &info);
Which calls this function as soon as a client has connected:
/**
* new_client_handler
* Called by rtsp server on a new client connection
*/
static void new_client_handler(GstRTSPServer *server, GstRTSPClient *client, struct stream_info *si)
{
DEBUG_ENTER;
/* Used to initiate the media-configure callback */
static gboolean first_run = TRUE;
GstRTSPConnection *connection = gst_rtsp_client_get_connection(client);
if (connection == NULL)
{
LOG_err("Could not get RTSP connection");
DEBUG_EXIT;
return;
}
GstRTSPUrl *url = gst_rtsp_connection_get_url(connection);
if (url == NULL)
{
LOG_err("Could not get RTSP connection URL");
DEBUG_EXIT;
return;
}
si->num_cli++;
gchar* uri = gst_rtsp_url_get_request_uri(url);
LOG_info("[%d]A new client %s has connected", si->num_cli, uri);
g_free(uri);
si->connected = TRUE;
/* Create media-configure handler */
/*relevant part for question*/
if (si->num_cli == 1)
{ /* Initial Setup */
/**
* Stream info is required, which is only
* available on the first connection. Stream info is created
* upon the first connection and is never destroyed after that.
*/
if (first_run == TRUE)
{
LOG_debug("Creating 'media-configure' signal handler");
g_signal_connect(si->factory, "media-configure", G_CALLBACK(media_configure_handler),
si);
}
}
else
{
change_bitrate(si); //This makes video stream crash if 'media_configure_handler' isn't yet finished
}
/* Create new client_close_handler */
LOG_debug("Creating 'closed' signal handler");
g_signal_connect(client, "closed", G_CALLBACK(client_close_handler), si);
first_run = FALSE;
DEBUG_EXIT;
}
When a client is the first one to connect, it sets up the media-configure callback to initialize the pipeline. The configuration code looks like this:
**
* media_configure_handler
* Setup pipeline when the stream is first configured
*/
static void media_configure_handler(GstRTSPMediaFactory *factory, GstRTSPMedia *media,
struct stream_info *si)
{
DEBUG_ENTER;
si->media = media;
LOG_info("[%d]Configuring pipeline...", si->num_cli);
si->pipeline = GST_BIN(gst_rtsp_media_get_element(media)); //Pipeline gets configured here
setup_elements(si);
if (si->num_cli == 1)
{
/* Create Msg Event Handler */
LOG_debug("Creating 'periodic message' handler");
g_timeout_add(si->msg_rate * 1000, (GSourceFunc) periodic_msg_handler, si);
}
DEBUG_EXIT;
}
A second (or nth) client that connects skips the media configuration step and instead goes to change_bitrate. Here the bitrate is adjusted based on the amount of connected clients.
/**
* change_bitrate
* handle changing of bitrates
*/
static void change_bitrate(struct stream_info *si)
{
DEBUG_ENTER;
int c = si->curr_bitrate;
int step = (si->max_bitrate - si->min_bitrate) / si->steps;
GstElement *elem = search_pipeline(si->pipeline, "enc"); //crashes due to an unitialized pipeline
const gchar *name = g_ascii_strdown(G_OBJECT_TYPE_NAME(elem), -1);
GstStructure *extra_controls;
...
}
This all works fine if a single client connects first. Later, the connection can handle multiple clients and adjusts the bitrate accordingly.
The problem arises if the first connection is by multiple clients:
In this case, both clients enter an instance of new_client_handler, in which the first one will set up the media_configure_handler. The second connection tries to change the bitrate, but fails because the pipeline is not yet configured by the callback.
How can i make the second (and nth) connection wait until the media configure callback has finished and thus a pipeline is available?
Solved this in the end with the following code (in function new_client_handler)
/* Create media-configure handler */
if (si->num_cli == 1)
{ /* Initial Setup */
/**
* Stream info is required, which is only
* available on the first connection. Stream info is created
* upon the first connection and is never destroyed after that.
*/
if (first_run == TRUE)
{
LOG_debug("Creating 'media-constructor' signal handler");
g_signal_connect(si->factory, "media-constructed", G_CALLBACK(media_configure_handler),
si);
}
}
else if(si->pipeline != 0)
{
change_bitrate(si);
}
else
{
g_signal_connect(si->factory, "media-configure", G_CALLBACK(media_constructed_handler),
si);
}
Pipeline object's construction is now hooked to the media-constructed event, which runs before the media-configure event.
A second client will only change bitrate if pipeline is initialized. If not, the client hooks in the media-configure callback and changes bitrate there. This callback is guaranteed to run after the media-constructed callback.

GLIB D-BUS Bluetooth - How to get the file descriptor?

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).

How to correctly set the hostname for the TCP/IP adapter on the ESP32

Problem
Espressif's ESP-32 (specifically the ESP-WROOM-32 in this case) appears on a network with the default hostname "Espressif". I don't want to use this hostname, so I've opted to change it as follows:
// Initialize the TCP/IP adapter (launches handler task)
tcpip_adapter_init();
// Set the hostname for the default TCP/IP station interface
if ((err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, g_hostname))
!= ESP_OK) {
return err;
}
Of course, this isn't working. I get back the following error: ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY.
Attempted Solutions
To solve this, I look to see if the TCP/IP adapter will post some kind of event when it is finished initializing. That way I can register a handler to set the hostname. The Espressif WiFi Driver Guide here indicates a task is launched - so there is probably an event right:
"The main task calls tcpip_adapter_init() to create an LwIP core task and initialize LwIP-related work."
Well I cannot find any such events. Neither the API documentation nor the actual file itself (tcpip_adapter.h) have it. I checked the header file for events, and none seem to exist solely for the purpose of indicating that the TCP/IP adapter has finished starting:
/** IP event declarations */
typedef enum {
IP_EVENT_STA_GOT_IP, /*!< ESP32 station got IP from connected AP */
IP_EVENT_STA_LOST_IP, /*!< ESP32 station lost IP and the IP is reset to 0 */
IP_EVENT_AP_STAIPASSIGNED, /*!< ESP32 soft-AP assign an IP to a connected station */
IP_EVENT_GOT_IP6, /*!< ESP32 station or ap or ethernet interface v6IP addr is preferred */
IP_EVENT_ETH_GOT_IP, /*!< ESP32 ethernet got IP from connected AP */
} ip_event_t;
Possible Lead
I have noticed that in Espressif's WiFi guide they indicate that the event SYSTEM_EVENT_STA_START (which indicates that a station has started), will:
Upon receiving this event, the event task will initialize the LwIP network interface (netif).
If I place the call after a handler receives this event, I no longer get the error:
// After the event WIFI_EVENT_STA_START
if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) {
// Set the hostname for the default TCP/IP station interface
if ((err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, g_hostname))
!= ESP_OK) {
fprintf(stderr, "Err: %s", esp_err_to_name(err));
}
...
}
However, the hostname still hasn't changed. Therefore I find myself a bit stuck. How can I actually change the hostname? I've found little to no results from searching this problem. However, the esp32 is a popular module and I'm sure many other people will find themselves facing the same problem.
Turns out I was doing it correctly. It was my router that had failed to refresh the hostname adequately. For consistency I will restate what I did to solve this problem:
The Espressif WiFi Guide indicates that the event SYSTEM_EVENT_STA_START is generated once esp_wifi_start() returns successfully.
The generation of this event also means that the event task will initialize the LwIP network interface (netif). Since we know that the TCP/IP adapter will surely have been initialized at this point, we can invoke the hostname change function. Here is an example of a handler that does that, taken right from their example:
void wifi_event_handler (void *handler_arg, esp_event_base_t base, int32_t id,
void *event_data) {
esp_err_t err;
// If esp_wifi_start() returned ESP_OK and WiFi mode is in station mode
if (base == WIFI_EVENT && id == WIFI_EVENT_STA_START) {
const char *name;
// Set the hostname for the default TCP/IP station interface
if ((err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_STA, g_hostname))
!= ESP_OK) {
fprintf(stderr, "Err: %s", esp_err_to_name(err));
} else {
if ((err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &name)) != ESP_OK) {
fprintf(stderr, "Err Get Hostname: %s\n", esp_err_to_name(err));
} else {
printf("Hostname: %s\n", (name == NULL ? "<None>" : name));
}
}
...
}
...
}
In this example I get the hostname after setting it, and print it to stdout. You can validate it if you are running the monitor for the ESP32. The hostname set will be the one visible from the router page.

SDLNet Networking Not Working

I am working on a game written in C using SDL. Given that it already uses SDL, SDL_image, and SDL_ttf, I decided to add SDL_mixer and SDL_net to my engine. Getting SDL_mixer set up and working was very easy, but I am having a lot of trouble with SDL_net.
To test I created a very simple application with the following rules:
Run without arguments act as a TCP server on port 9999
Run with an argument try to connect to the server at the given IP address on port 9999
Here are some of the key lines of the program (I'm not going to post my whole event-driven SDL engine because its too long):
char *host = NULL;
if (argc > 1) host = argv[1];
and...
IPaddress ip;
TCPsocket server = NULL;
TCPsocket conn = NULL;
if (host) { /* client mode */
if (SDLNet_ResolveHost(&ip,host,port) < 0)
return NULL; //this is actually inside an engine method
if (!(conn = SDLNet_TCP_Open(&ip)))
return NULL;
} else { /* server mode */
if (SDLNet_ResolveHost(&ip,NULL,port) < 0)
return NULL;
if (!(server = SDLNet_TCP_Open(&ip)))
return NULL;
}
and... inside the event loop
if (server) {
if (!conn)
conn = SDLNet_TCP_Accept(server);
}
if (conn) {
void *buf = malloc(size); //server, conn, size are actually members of a weird struct
while (SDLNet_TCP_Recv(conn,buf,size))
onReceive(buf); //my engine uses a callback system to handle things
free(buf);
}
The program seems to start up just fine. However for some reason when I run it in client mode trying to connect to my home computer (which I have on a different IP) from my laptop I find that the call to SDLNet_TCP_Open blocks the program for awhile (5-10 seconds) then returns NULL. Can anybody see what I did wrong? Should I post more of the code? Let me know.

Specifying ipv6 interface using glib/gio

I'm trying to use the Gnome glib/gio C library to create a client program to connect to a server via IPv6. My server box has a link local IPv6 address:
inet6 addr: fe80::2d0:c9ff:feda:99e0/64 Scope:Link
So, I to access it, I have to tell the client software which interface to use (eth0 in this case). So the following works (using port 1500):
nc -6 fe80::2d0:c9ff:feda:99e0%eth0 1500
In glib, using the %eth0 notation violates the URI notation:
(process:31159): GLib-GIO-WARNING **: Invalid URI 'none://[fe80:0:0:0:2d0:c9ff:feda:99e0%eth0]:1500'
I've looked in the code and it clearly expects to see the percent escape notation (i.e. the characters '%25') but I can't seem to get the format correct:
** (process:5741): ERROR **: Invalid URI 'none://[fe80:0:0:0:2d0:c9ff:feda:99e0%25eth0]1500'
So, anyone know how to specify the interface?
EDIT: Here's the code
// gchar test[255] = "fe80:0:0:0:2d0:c9ff:feda:99e0%eth0";
// gchar test[255] = "fe80:0:0:0:2d0:c9ff:feda:99e0\%eth0";
// gchar test[255] = "fe80:0:0:0:2d0:c9ff:feda:99e0\x25eth0";
// gchar test[255] = "fe80:0:0:0:2d0:c9ff:feda:99e0\%%25eth0";
gchar test[255] = "[fe80:0:0:0:2d0:c9ff:feda:99e0\%%eth0]";
connection = g_socket_client_connect_to_uri (client,
test,
1500,
NULL,
&error);
EDIT 2: Complete code (with MichaelHampton's input):
#include <glib.h>
#include <gio/gio.h>
int
main (int argc, char *argv[])
{
/* initialize glib */
g_type_init ();
GError * error = NULL;
/* create a new connection */
GSocketConnection * connection = NULL;
GSocketClient * client = g_socket_client_new();
connection = g_socket_client_connect_to_host (client,
(gchar*)"fe80::5054:ff:fe1f:6b6c\%br0",
1500, /* your port goes here */
NULL,
&error);
/* don't forget to check for errors */
if (error != NULL)
{
g_error (error->message);
}
else
{
g_print ("Connection successful!\n");
}
return 0;
}
Ah, you're calling the wrong function. You should be using g_socket_client_connect_to_host to connect directly to a host and port.
Here is a working example:
connection = g_socket_client_connect_to_host (client,
(gchar*)"fe80::5054:ff:fe1f:6b6c\%br0",
1500, /* your port goes here */
NULL,
&error);
The complete example code, which it looked like you were using, was in the related question: GIO socket-server / -client example

Resources