Files
bell-rspro/bell-mqtt.c
2020-10-26 20:07:30 +01:00

309 lines
8.8 KiB
C

/**************************************************************/
/* Multicast listener (server) */
/* */
/* Activation using: {program name} {Multicast IP} {port} */
/* {program name} - This program name */
/* {Multicast IP} - The IP address to listen to (Class D) */
/* {port} - The port hnumber to listen on */
/* {gpio} - gpio pin number for output */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* */
/* This is free software released under the GPL license. */
/* See the GNU GPL for details. */
/* */
/* (c) Juan-Mariano de Goyeneche. 1998, 1999. */
/**************************************************************/
#include <stdio.h> /* printf(), snprintf() */
#include <stdlib.h> /* strtol(), exit() */
#include <sys/types.h>
#include <errno.h> /* perror() */
#include <unistd.h> /* fork(), sleep() */
#include <sys/utsname.h> /* uname() */
#include <string.h> /* memset() */
#include <syslog.h> /* openlog(), syslog() */
#include "gpiosysfs.h"
#include <signal.h> //signal(3)
#include <sys/stat.h> //umask(3)
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <err.h>
#include "mosquitto.h"
#define DEBUG
#define MAXLEN 1024
#ifdef DEBUG
#define LOG(...) do { printf(__VA_ARGS__); } while (0)
#else
#define LOG(...)
#endif
/* How many seconds the broker should wait between sending out
* keep-alive messages. */
#define KEEPALIVE_SECONDS 60
/* Hostname and port for the MQTT broker. */
char *mqtt_host;
int port;
char *topic;
char *pin;
int timeout;
bool debug=false;
int daemonize(char* name, char* path, char* outfile, char* errfile, char* infile , char* pidfile)
{
char filename[64];
FILE *pf;
if(!path) { path="/"; }
if(!name) { name="medaemon"; }
if(!infile) { infile="/dev/null"; }
if(!outfile) { outfile="/dev/null"; }
if(!errfile) { errfile="/dev/null"; }
if(!pidfile) {
snprintf(filename,63,"/var/run/%s.pid",name);
pidfile = filename;
}
//printf("%s %s %s %s\n",name,path,outfile,infile);
pid_t child;
//fork, detach from process group leader
if( (child=fork())<0 ) { //failed fork
fprintf(stderr,"error: failed fork\n");
exit(EXIT_FAILURE);
}
if (child>0) { //parent
exit(EXIT_SUCCESS);
}
if( setsid()<0 ) { //failed to become session leader
fprintf(stderr,"error: failed setsid\n");
exit(EXIT_FAILURE);
}
//catch/ignore signals
signal(SIGCHLD,SIG_IGN);
signal(SIGHUP,SIG_IGN);
//fork second time
if ( (child=fork())<0) { //failed fork
fprintf(stderr,"error: failed fork\n");
exit(EXIT_FAILURE);
}
if( child>0 ) { //parent
exit(EXIT_SUCCESS);
}
//new file permissions
umask(0);
//change to path directory
chdir(path);
// open pid file
pf = fopen(pidfile,"w+");
if (pf == NULL) {
fprintf(stderr,"error: failed open pid file\n");
exit(EXIT_FAILURE);
} else {
fprintf(pf,"%d",getpid());
fclose(pf);
}
//Close all open file descriptors
int fd;
for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
{
close(fd);
}
//reopen stdin, stdout, stderr
freopen(infile,"r",stdin); //fd=0
freopen(outfile,"w+",stdout); //fd=1
freopen(errfile,"w+",stderr); //fd=2
//open syslog
openlog(name,LOG_PID,LOG_DAEMON);
syslog (LOG_DAEMON, "Program started by User %d", getuid ());
return(0);
}
void beep()
{
gpioWrite(pin,"1");
msleep(timeout);
gpioWrite(pin,"0");
msleep(timeout);
}
struct client_info {
struct mosquitto *m;
pid_t pid;
uint32_t tick_ct;
};
static void die(const char *msg);
static struct mosquitto *init(struct client_info *info);
static bool set_callbacks(struct mosquitto *m);
static bool connect(struct mosquitto *m);
static int run_loop(struct client_info *info);
/* Fail with an error message. */
static void die(const char *msg) {
fprintf(stderr, "%s", msg);
exit(1);
}
/* Initialize a mosquitto client. */
static struct mosquitto *init(struct client_info *info) {
void *udata = (void *)info;
size_t buf_sz = 32;
char buf[buf_sz];
if (buf_sz < snprintf(buf, buf_sz, "bellrecv_%d", info->pid)) {
return NULL; /* snprintf buffer failure */
}
/* Create a new mosquitto client, with the name "client_#{PID}". */
LOG("Init\n");
mosquitto_lib_init();
struct mosquitto *m = mosquitto_new(buf, true, udata);
return m;
}
/* Callback for successful connection: add subscriptions. */
static void on_connect(struct mosquitto *m, void *udata, int res) {
if (res == 0) { /* success */
LOG("connecion successfull\n");
//struct client_info *info = (struct client_info *)udata;
mosquitto_subscribe(m, NULL, topic, 0);
} else {
die("connection refused\n");
}
}
/* A message was successfully published. */
static void on_publish(struct mosquitto *m, void *udata, int m_id) {
LOG("-- published successfully\n");
}
static bool match(const char *topic, const char *key) {
return 0 == strncmp(topic, key, strlen(key));
}
/* Handle a message that just arrived via one of the subscriptions. */
static void on_message(struct mosquitto *m, void *udata,
const struct mosquitto_message *msg) {
if (msg == NULL) {
LOG("-- message NULL\n");
return;
}
LOG("-- got message @ %s: (%d, QoS %d, %s) '%s'\n",
msg->topic, msg->payloadlen, msg->qos, msg->retain ? "R" : "!r",
(char *)msg->payload);
struct client_info *info = (struct client_info *)udata;
syslog(LOG_DAEMON, "Bell message recived: '%s' -> '%s'", msg->topic, (char *)msg->payload);
if (match(msg->topic, topic)) {
if (0 == strncmp(msg->payload, "BELL:", 5)) {
LOG("%s %d\n",(char *)msg->payload, info->pid);
syslog (LOG_DAEMON, "Bell ringing by message: %s",(char *)msg->payload);
if (debug == false) beep();
} else {
LOG("invalid message: '%s' excepted 'BELL:'\n",(char *)msg->payload);
}
}
}
/* Successful subscription hook. */
static void on_subscribe(struct mosquitto *m, void *udata, int mid,
int qos_count, const int *granted_qos) {
LOG("-- subscribed successfully on topic: '%s'\n",topic);
syslog (LOG_DAEMON, "Bell subscibed on topic %s" ,topic);
}
/* Register the callbacks that the mosquitto connection will use. */
static bool set_callbacks(struct mosquitto *m) {
mosquitto_connect_callback_set(m, on_connect);
mosquitto_publish_callback_set(m, on_publish);
mosquitto_subscribe_callback_set(m, on_subscribe);
mosquitto_message_callback_set(m, on_message);
return true;
}
/* Connect to the network. */
static bool connect(struct mosquitto *m) {
int res = mosquitto_connect(m, mqtt_host, port, KEEPALIVE_SECONDS);
return res == MOSQ_ERR_SUCCESS;
}
/* Loop until it is explicitly halted or the network is lost, then clean up. */
static int run_loop(struct client_info *info) {
int res = mosquitto_loop_forever(info->m, 1000, 1000 /* unused */);
mosquitto_destroy(info->m);
(void)mosquitto_lib_cleanup();
closelog();
if (res == MOSQ_ERR_SUCCESS) {
return 0;
} else {
return 1;
}
}
int main(int argc, char* argv[])
{
int res;
if (argc < 6) {
fprintf(stderr, "Usage: %s mqtt_host port topic gpio seconds [debug]\n", argv[0]);
exit(1);
}
mqtt_host = argv[1];
port = atoi(argv[2]);
topic = argv[3];
pin = argv[4];
timeout = atoi(argv[5])*1000; //To milis
if (argc == 6) {
if( (res=daemonize("bellring","/tmp",NULL,NULL,NULL,NULL)) != 0 ) {
fprintf(stderr,"error: daemonize failed\n");
exit(EXIT_FAILURE);
}
} else { debug = true ;};
if (debug == false) gpioSetup(pin);
pid_t pid = getpid();
struct client_info info;
memset(&info, 0, sizeof(info));
info.pid = pid;
struct mosquitto *m = init(&info);
if (m == NULL) { die("init() failure\n"); }
info.m = m;
if (!set_callbacks(m)) { die("set_callbacks() failure\n"); }
if (!connect(m)) { die("connect() failure\n"); }
return run_loop(&info);
}