Plugin to handle CONNECT events

We have a requirement to track the connection status of devices connected to the Mosquitto broker. When disconnected, we can use the MOSQ_EVT_DISCONNECT callback. But there is no equivalent for CONNECT. Any suggestions?

One thought: Write a MOSQ_EVT_MESSAGE handler that takes action only on CONNACK with a successful status. Will that be robust?

But but I’m crazy about getting called for every message just to track the 1st ACK.

Thanks for any pointers.

Hi Charlie,

You’re quite right that this is an omission, and I’m pleased to say that it’s already fixed in the in-development release. That doesn’t help you right now unless you want to try out that code.

What are you currently doing for authentication?

If you’re doing your own authentication then you know whether clients have connected or not already - so I assume you aren’t doing that.

If you’re using the dynamic security plugin then you could use a combined plugin and preload library to intercept the basic authentication callback and hence be able to figure out whether the client authenticated or not. I’ve put some example code below that does this (note that it’ll stop working in 2.1, but then you can use MOSQ_EVT_CONNECT). You can replace the printf lines with whatever you need to keep track of the clients. Compile with gcc -shared -o evt_connect.so evt_connect.c -fPIC -ldl, add plugin /path/to/evt_connect.so to your config file before the dynamic security plugin, and then run with LD_PRELOAD=/path/to/evt_connect.so /usr/sbin/mosquitto -c /etc/mosquitto.conf. Your command may be different, the important point is that the environment variable LD_PRELOAD needs setting in the environment before mosquitto is run.

If you’re using another authentication method you’ll have to let me know to see if something can be done.

Regards,

Roger

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>

#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
#include "mosquitto.h"

#ifndef UNUSED
#define UNUSED(A) (void)(A)
#endif

static mosquitto_plugin_id_t *mosq_pid = NULL;
static int init_state = 0;
static int (*plugin_auth_callback)(int, void *, void *) = NULL;

static void init(void)
{
	if(init_state) return;

	init_state = 1;

	/* This loads the real symbol from the dynsec plugin */
	plugin_auth_callback = dlsym(RTLD_NEXT, "dynsec_auth__basic_auth_callback");
}

/* This function is loaded *before* the same function in the dynsec plugin, so is used in preference. */
int dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata)
{
	struct mosquitto_evt_basic_auth *ed = event_data;
	int rc;

	if(!init_state){
		init();
	}
	if(plugin_auth_callback == NULL){
		return MOSQ_ERR_UNKNOWN;
	}

	/* Run the real authentication check */
	rc = plugin_auth_callback(event, event_data, userdata);

	if(rc == MOSQ_ERR_SUCCESS){
		printf("Client %s connected.\n", mosquitto_client_id(ed->client));
	}
	return 0;
}

static int callback_disconnect(int event, void *event_data, void *userdata)
{
	struct mosquitto_evt_disconnect *ed = event_data;
	UNUSED(event);
	UNUSED(event_data);
	UNUSED(userdata);

	printf("Client %s disconnected\n", mosquitto_client_id(ed->client));
	return MOSQ_ERR_SUCCESS;
}

int mosquitto_plugin_version(int supported_version_count, const int *supported_versions)
{
	int i;

	for(i=0; i<supported_version_count; i++){
		if(supported_versions[i] == 5){
			return 5;
		}
	}
	return -1;
}

int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)
{
	UNUSED(user_data);
	UNUSED(opts);
	UNUSED(opt_count);

	mosq_pid = identifier;
	return mosquitto_callback_register(mosq_pid, MOSQ_EVT_DISCONNECT, callback_disconnect, NULL, NULL);
}

int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)
{
	UNUSED(user_data);
	UNUSED(opts);
	UNUSED(opt_count);

	return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_DISCONNECT, callback_disconnect, NULL);
}

Thanks much, Roger.

Yes, I found the code in the develop branch and merged it into our 2.x branch where it has been working well.