#include <sys/termios.h>
#include <mosquitto.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "signal.h"
#include "rc.h"
#include "mqtt.h"
static struct list_head mqtt_upflow;
static pthread_mutex_t upflow_mutex;
//extern struct list_head mqtt_downflow;
static struct mosquitto *mosq = NULL;
static int publish_finished = 1;
static int already_connected = 0;
static int need_restart = 0;
int g_mqtt_sigid[150];
int g_mqtt_data = 0;
int mqtt_public_message_enqueue(char *data, char *topic, int qos)
{
struct pub_msg *msg;
msg = (struct pub_msg *)malloc(sizeof(struct pub_msg));
if(msg == NULL)
{
return 0;
}
memset(msg, 0, sizeof(struct pub_msg));
snprintf(msg->data, sizeof(msg->data), "%s", data);
snprintf(msg->topic, sizeof(msg->topic), "%s", topic);
msg->qos = qos;
msg->mid = -1;
pthread_mutex_lock(&upflow_mutex);
list_add_tail(&msg->list, &mqtt_upflow);
pthread_mutex_unlock(&upflow_mutex);
return 1;
}
/*
*why need this function?
*when mqtt process exit, always has one mqtt thread not exit , so case mqtt process can not restart
*add by jerry, 22-06-21
*
* */
static void mqtt_exit(void)
{
int ret;
/*
*ret != -1
*WIFEXITED(ret) is true
*0 == WEXITSTATUS(ret)
*all above success, system function is success, other is failed
* */
do
{
ret = system("killall -9 mqtt");
}while(!((ret != -1) && (WIFEXITED(ret)) && (0 == WEXITSTATUS(ret))));
}
static void mqtt_sig_handler(int sig)
{
switch (sig)
{
case SIGTERM:
case SIGKILL:
case SIGINT:
syslog(LOG_NOTICE, "Got a signal! exit!!");
mqtt_exit();
break;
case SIGHUP:
syslog(LOG_NOTICE, "Got a signal! exit!!");
mqtt_exit();
break;
case SIGUSR1:
break;
case SIGUSR2:
break;
}
}
static void mqtt_deamon()
{
struct sigaction sa;
FILE *fp;
if ( fork() !=0 )
exit(0);
openlog("mqtt", LOG_PID, LOG_USER);
sa.sa_handler = mqtt_sig_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGKILL, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
signal(SIGCHLD, chld_reap);
if ( setsid() < 0 )
exit(1);
if ( chdir("/") == -1 )
exit(1);
kill_pidfile_tk(MQTT_PID_FILE);
if ((fp = fopen(MQTT_PID_FILE, "w")) != NULL)
{
fprintf(fp, "%d", getpid());
fclose(fp);
}
}
/* Callback called when the client receives a CONNACK message from the broker. */
static void on_connect(struct mosquitto *mosq, void *obj, int reason_code)
{
// int rc;
// char sub_topic[256] = {0};
/* Print out the connection result. mosquitto_connack_string() produces an
* appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0
* clients is mosquitto_reason_string().
*/
syslog(LOG_ERR, "on_connect: %s", mosquitto_connack_string(reason_code));
if(reason_code != 0){
/* If the connection fails for any reason, we don't want to keep on
* retrying in this example, so disconnect. Without this, the client
* will attempt to reconnect. */
mosquitto_disconnect(mosq);
}
else
{
already_connected = 1;
//subscribe when mqtt connect success
#if 0
/* Making subscriptions in the on_connect() callback means that if the
* connection drops and is automatically resumed by the client, then the
* subscriptions will be recreated when the client reconnects. */
snprintf(sub_topic, sizeof(sub_topic), "VirtualTopic/mqtt/command/%s", nvram_safe_get("iot_sendorid"));
syslog(LOG_INFO, "sub topic: %s", sub_topic);
rc = mosquitto_subscribe(mosq, NULL, sub_topic, 0);
if(rc != MOSQ_ERR_SUCCESS){
syslog(LOG_ERR, "Error subscribing: %s\n", mosquitto_strerror(rc));
/* We might as well disconnect if we were unable to subscribe */
mosquitto_disconnect(mosq);
}
#endif
}
}
/* Callback called when the broker sends a SUBACK in response to a SUBSCRIBE. */
static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
int i;
bool have_subscription = false;
/* In this example we only subscribe to a single topic at once, but a
* SUBSCRIBE can contain many topics at once, so this is one way to check
* them all. */
for(i=0; i<qos_count; i++){
syslog(LOG_ERR, "on_subscribe: %d:granted qos = %d", i, granted_qos[i]);
if(granted_qos[i] <= 2){
have_subscription = true;
}
}
if(have_subscription == false){
/* The broker rejected all of our subscriptions, we know we only sent
* the one SUBSCRIBE, so there is no point remaining connected. */
syslog(LOG_ERR, "Error: All subscriptions rejected.");
mosquitto_disconnect(mosq);
}
}
/* Callback called when the client knows to the best of its abilities that a
* PUBLISH has been successfully sent. For QoS 0 this means the message has
* been completely written to the operating system. For QoS 1 this means we
* have received a PUBACK from the broker. For QoS 2 this means we have
* received a PUBCOMP from the broker. */
static void on_publish(struct mosquitto *mosq, void *obj, int mid)
{
struct list_head *pos = NULL;
struct pub_msg *msg = NULL;
syslog(LOG_ERR, "Message with mid %d has been published.", mid);
//when qos is 1 or 2, message publish success, delete message from queue here
pthread_mutex_lock(&upflow_mutex);
list_for_next_each(pos, &mqtt_upflow)
{
msg = container_of(pos,struct pub_msg,list);
if(msg->mid == mid)
{
//syslog(LOG_INFO,"2content:%s, topic:%s, qos:%d, mid:%d", msg->data, msg->topic, msg->qos, msg->mid);
list_del(&msg->list);
free(msg);
publish_finished = 1;
break;
}
}
pthread_mutex_unlock(&upflow_mutex);
}
/* Callback called when the client receives a message. */
static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg)
{
syslog(LOG_INFO, "-----on message");
mqtt_public_message_enqueue("hello", "mqtt1", 0);
}
char *get_time_str(int type, char *buf, int blen)
{
struct tm *info;
struct timeval tv;
struct timezone tz;
if(buf == NULL)
{
return NULL;
}
gettimeofday(&tv, &tz);
info = localtime(&tv.tv_sec);
if(type == 0)
{
snprintf(buf, blen, "%d-%.2d-%.2d %.2d:%.2d:%.2d.%ld", 1900 + info->tm_year, info->tm_mon + 1, info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec, tv.tv_usec/1000);
}
else if(type = 1)
{
snprintf(buf, blen, "%d%.2d%.2d%.2d%.2d%.2d", 1900 + info->tm_year, info->tm_mon + 1, info->tm_mday, info->tm_hour, info->tm_min, info->tm_sec);
}
return buf;
}
char *printf_time_sub(char sub, uint8_t count, char *out)
{
time_t current_time;
struct tm * timeinfo;
time(¤t_time);
timeinfo = localtime(¤t_time);
switch (sub)
{
case 'Y':
if (count == 4)
sprintf(out, "%d", timeinfo->tm_year + 1900);
else
sprintf(out, "%d", timeinfo->tm_year + 1900 - 2000);
break;
case 'M':
if (count == 2)
sprintf(out, "%02d", timeinfo->tm_mon + 1);
else
sprintf(out, "%d", timeinfo->tm_mon + 1);
break;
case 'D':
if (count == 2)
sprintf(out, "%02d", timeinfo->tm_mday);
else
sprintf(out, "%d", timeinfo->tm_mday);
break;
case 'h':
if (count == 2)
sprintf(out, "%02d", timeinfo->tm_hour);
else
sprintf(out, "%d", timeinfo->tm_hour);
break;
case 'm':
if (count == 2)
sprintf(out, "%02d", timeinfo->tm_min);
else
sprintf(out, "%d", timeinfo->tm_min);
break;
case 's':
if (count == 2)
sprintf(out, "%02d", timeinfo->tm_sec);
else
sprintf(out, "%d", timeinfo->tm_sec);
break;
default:
break;
}
return (out + strlen(out));
}
uint16_t get_length(char *line)
{
uint16_t len = 0;
while ( *line)
{
if (*line == '\n')
{
if (len) len++;
break;
}
else
{
len++;
line++;
}
}
return len;
}
char *printf_intf_data(char *line)
{
uint16_t line_len = get_length(line);
uint16_t pos = 0;
char *result = strstr(line, "<<");
if(NULL == result)
{
return;
}
pos = result - line;
line += pos;
line_len -= pos;
result = strstr(line, "INTF");
if(NULL == result)
{
return;
}
pos = result - line;
char temp_pos[6] = {0};
snprintf(temp_pos, sizeof(temp_pos), "%d", pos);
nvram_set("intf_pos", temp_pos);
line += (pos + 4);
line_len -= (pos + 4);
char intf_data[6] = {0};
int i = 0;
char temp[32] = {0};
char temp_val[32] = {0};
char mqtt_data[32] = {0};
char buf[6] = {0};
while(*line != ')')
{
if(*line == '(')
{
line ++;
continue;
}
intf_data[i] = *line;
i ++;
line ++;
}
int j;
for(j = 0; j <= nvram_get_int("g_reg_num"); j++)
{
memset(temp, 0, sizeof(temp));
memset(temp_val, 0, sizeof(temp_val));
memset(mqtt_data, 0, sizeof(mqtt_data));
snprintf(temp, sizeof(temp), "regAddr_%d", j+1);
if(!strlen(nvram_safe_get(temp)))
{
sleep(1);
}
if(nvram_get_int(temp) == atoi(intf_data))
{
snprintf(temp_val, sizeof(temp_val), "signalval_%d", j+1);
snprintf(mqtt_data, sizeof(mqtt_data), "mqtt_report_data%d", g_mqtt_data);
nvram_set(mqtt_data, nvram_safe_get(temp_val));
g_mqtt_data ++;
snprintf(buf, sizeof(buf), "%d", g_mqtt_data);
nvram_set("g_mqtt_data", buf);
}
}
return;
}
char *printf_time(char *line)
{
/*
"time":"%s"<<TIME(YYYY-MM-DD hh:mm:ss.SSSzz)
YYYY年
MM月
DD日
hh时
mm分
ss秒
SSS毫秒
zz +-08
zzzz +-08:00
*/
uint16_t line_len = get_length(line);
uint16_t pos = 0;
char *result = strstr(line, "<<");
pos = result - line;
line += pos;
line_len -= pos;
//定位TIME
result = strstr(line, "TIME");
pos = result - line;
char temp_pos[6] = {0};
snprintf(temp_pos, sizeof(temp_pos), "%d", pos);
nvram_set("time_pos", temp_pos);
line += (pos + 4);
line_len -= (pos + 4);
char field[5];
uint8_t start = 0;
char *timestr = (char *)malloc(50);
//memset(timestr, ' ', 50); // 初始化内存空间
char *temp = timestr;
do
{
switch (*line)
{
case 'Y':
case 'M':
case 'D':
case 'h':
case 'm':
case 's':
case 'S':
case 'z':
switch (start)
{
case 0:
field[start++] = *line;
break;
case 4:
start = 0;
continue;
default:
if (*line == field[start - 1])
field[start++] = *line;
else
{
timestr = printf_time_sub(field[0], start, timestr);
start = 0;
continue;
}
}
break;
case '(': break;
case ')':
if (start)
{
timestr = printf_time_sub(field[0], start, timestr);
start = 0;
}
else
{
*timestr = 0;
}
nvram_set("mqtt_time", temp);
return temp;
default:
if (start)
{
timestr = printf_time_sub(field[0], start, timestr);
start = 0;
*timestr++ = *line;
}
else
*timestr++ = *line;
break;
}
line++;
}
while (*line);
if (temp == timestr)
{
free(timestr);
return NULL;
}
else
return temp;
}
char *encode_mqtt_pack()
{
char *line = NULL;
char line_temp[2000] = {0};
line = nvram_safe_get("mqtt_template");
//line_temp = line;
char report_packet_string[1000] = {0};
syslog(LOG_INFO, "========================%s", line);
snprintf(line_temp, sizeof(line_temp),"%s", line);
char *result = strstr(line_temp, "\":");
while(nvram_get_int("g_modbus_start") == 1)
{
sleep(1);
syslog(LOG_INFO, "{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{wait for modbus}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}");
continue;
}
// g_mqtt_start = 1;
nvram_set("g_mqtt_start", "1");
sleep(1);
while(result != NULL)
{
if(nvram_get_int("g_reg_num") != nvram_get_int("g_mqtt_flag"))
{
sleep(1);
}
printf_intf_data(result);
result = strstr(result + strlen("\":"), "\":");
}
int i = 0;
char mqtt_data[32] = {0};
printf_time(line);
float num[50] = {0};
for(i = 0; i < nvram_get_int("g_mqtt_data"); i ++)
{
memset(mqtt_data, 0, sizeof(mqtt_data));
snprintf(mqtt_data, sizeof(mqtt_data), "mqtt_report_data%d", i);
sscanf(nvram_safe_get(mqtt_data), "%f", &num[i]);
//syslog(LOG_INFO, "======================num%d :%f", i, num[i]);
}
if(num[nvram_get_int("g_reg_num")] == 0)
{
syslog(LOG_INFO, "num[%d] = 0", nvram_get_int("g_reg_num"));
return NULL;
}
// char tmp_buf[10] = {0};
// snprintf(tmp_buf, sizeof(tmp_buf), )
// if(nvram_get_int("intf_pos") < nvram_get_int("time_pos"))
//将intf和time值赋值给模板
char *tmp_line = line;
char tmp_string[70] = {0};
char packet_string[80] = {0};
char add_n_string[90] = {0};
i = 0;
int m = 0;
int data_num = 0;
while(tmp_line[m])
{
if(tmp_line[m] == '\n')
{
if(NULL != strstr(tmp_string, "INTF"))
{
sprintf(packet_string, tmp_string, num[data_num]);
data_num ++;
sprintf(add_n_string, "%s\n", packet_string);
memset(tmp_string, 0, sizeof(tmp_string));
}
else if(NULL != strstr(tmp_string, "TIME"))
{
sprintf(packet_string, tmp_string, nvram_safe_get("mqtt_time"));
sprintf(add_n_string, "%s\n", packet_string);
memset(tmp_string, 0, sizeof(tmp_string));
}
else
{
sprintf(packet_string, "%s", tmp_string);
sprintf(add_n_string, "%s\n", packet_string);
memset(tmp_string, 0, sizeof(tmp_string));
}
strcat(report_packet_string, add_n_string);
m ++;
i = 0;
}
tmp_string[i] = tmp_line[m];
i++;
m++;
}
strcat(report_packet_string, tmp_string);
char *token = report_packet_string;
char report_mqtt_string[1000] = {0};
int k = 0;
while(*token != NULL)
{
if(*token != ',')
{
report_mqtt_string[k] = *token;
k ++;
token ++;
}
else
{
report_mqtt_string[k] = *token;
k ++;
token ++;
report_mqtt_string[k] = '\n';
k ++;
while(*token != '\n')
{
token ++;
}
token ++;
}
}
if(NULL == strstr(report_mqtt_string, "{") || NULL == strstr(report_mqtt_string, "}"))
{
return NULL;
}
return report_mqtt_string;
}
static void *formate_data_thread(void *arg)
{
char *buf = NULL;
while(1)
{
buf = encode_mqtt_pack();
syslog(LOG_INFO, "======================buf :%s", buf);
if(buf == NULL)
{
sleep(3);
continue;
}
// snprintf(buf, sizeof(buf), "hello world%d", count);
mqtt_public_message_enqueue(buf, nvram_safe_get("iot_topic"), 0);
sleep(nvram_get_int("iot_mqtt_interval"));
}
}
static void *publish_thread(void *arg)
{
int rc;
struct list_head *pos;
struct pub_msg *msg = NULL;
while(1)
{
if((publish_finished) == 0 || (already_connected == 0))
{
//syslog(LOG_ERR, "Mqtt not connected or message is null");
sleep(1);
if(need_restart == 1)
{
mosquitto_disconnect(mosq);
}
continue;
}
pthread_mutex_lock(&upflow_mutex);
list_for_next_each(pos, &mqtt_upflow)
{
msg = container_of(pos,struct pub_msg,list);
break;
}
pthread_mutex_unlock(&upflow_mutex);
//mqtt_upflow is not null
if(pos != &mqtt_upflow)
{
publish_finished = 0;
//syslog(LOG_INFO,"1content:%s, topic:%s, qos:%d, mid:%d", msg->data, msg->topic, msg->qos, msg->mid);
rc = mosquitto_publish(mosq, &msg->mid, msg->topic, strlen(msg->data), msg->data, msg->qos, false);
g_mqtt_data = 0;
if(rc != MOSQ_ERR_SUCCESS)
{
publish_finished = 1;
syslog(LOG_ERR, "Error publishing: %s", mosquitto_strerror(rc));
sleep(1);
}
nvram_set("g_mqtt_start", "0");
}
else
{
//syslog(LOG_ERR, "current msg is NULL");
sleep(1);
if(need_restart == 1)
{
mosquitto_disconnect(mosq);
}
}
}
}
static void mqtt_tls_config(void)
{
char buf[64];
FILE *fp = NULL;
int rc = 0;
mkdir("/etc/mqtt",0755);
snprintf(buf, sizeof(buf), "/etc/mqtt/ca.crt");
fp = fopen(buf,"wb");
if(fp != NULL)
{
chmod(buf,S_IRUSR|S_IWUSR);
fprintf(fp,"%s",nvram_safe_get("iot_root_ca"));
fclose(fp);
}
if(nvram_match("iot_mutual_enable", "1"))
{
snprintf(buf, sizeof(buf), "/etc/mqtt/client.crt");
fp = fopen(buf,"wb");
if(fp != NULL)
{
chmod(buf,S_IRUSR|S_IWUSR);
fprintf(fp,"%s",nvram_safe_get("iot_client_ca"));
fclose(fp);
}
snprintf(buf, sizeof(buf), "/etc/mqtt/client.key");
fp = fopen(buf,"wb");
if(fp != NULL)
{
chmod(buf,S_IRUSR|S_IWUSR);
fprintf(fp,"%s",nvram_safe_get("iot_client_key"));
fclose(fp);
}
rc = mosquitto_tls_set(mosq, "/etc/mqtt/ca.crt", "/etc/mqtt", "/etc/mqtt/client.crt", "/etc/mqtt/client.key", NULL);
if(rc != MOSQ_ERR_SUCCESS)
{
syslog(LOG_ERR, "Error: set tls %s\n", mosquitto_strerror(rc));
}
}
else
{
rc = mosquitto_tls_set(mosq, "/etc/mqtt/ca.crt", NULL, NULL, NULL, NULL);
if(rc != MOSQ_ERR_SUCCESS)
{
syslog(LOG_ERR, "Error: set tls %s\n", mosquitto_strerror(rc));
}
}
}
int mqtt_main(int argc, char *argv[])
{
int rc;
pthread_t publish_pid, formate_pid, modbus_pid;
char *username = NULL, *passwd = NULL;
//char will_buf[1024] = {0};
nvram_set("g_mqtt_start", "0");
nvram_set("g_modbus_start", "1");
mqtt_deamon();
if (!strcmp(nvram_safe_get("mqtt_enable_rtu"), "0"))
{
syslog( LOG_NOTICE, "MQTT Disabled, Pause!!" );
while (1)
{
pause();
}
}
//check ppp online
while( !check_wanup() )
{
syslog(LOG_NOTICE, "MQTT : Cellular Offline" );
sleep( 3 );
}
//等待modbus进程读取数据
sleep(10);
/* Required before calling other mosquitto functions */
mosquitto_lib_init();
INIT_LIST_HEAD(&mqtt_upflow);
pthread_mutex_init(&upflow_mutex, NULL);
/* Create a new client instance.
* id = NULL -> ask the broker to generate a client id for us
* clean session = true -> the broker should remove old sessions when we connect
* obj = NULL -> we aren't passing any of our private data for callbacks
*/
mosq = mosquitto_new(nvram_match("iot_clientid", "") ? NULL : nvram_safe_get("iot_clientid"), true, NULL);
if(mosq == NULL)
{
syslog(LOG_ERR, "Error: Out of memory.");
return 0;
}
mqtt_tls_config();
//downflow data
//INIT_LIST_HEAD(&mqtt_downflow);
//mqtt will message set
//mosquitto_will_set(mosq, MQTT_PUBLISH_TOPIC, strlen(will_buf), will_buf, 1, false);
username = nvram_safe_get("iot_username");
passwd = nvram_safe_get("iot_passwd");
if(strlen(username) == 0)
{
username = NULL;
}
if(strlen(passwd) == 0)
{
passwd = NULL;
}
rc = mosquitto_username_pw_set(mosq, username, passwd);
if(rc != MOSQ_ERR_SUCCESS)
{
syslog(LOG_ERR, "Error1: %s", mosquitto_strerror(rc));
goto ERROR;
}
/* Configure callbacks. This should be done before connecting ideally. */
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_publish_callback_set(mosq, on_publish);
//mosquitto_subscribe_callback_set(mosq, on_subscribe);
mosquitto_message_callback_set(mosq, on_message);
/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.
* This call makes the socket connection only, it does not complete the MQTT
* CONNECT/CONNACK flow, you should use mosquitto_loop_start() or
* mosquitto_loop_forever() for processing net traffic. */
rc = mosquitto_connect(mosq, nvram_safe_get("iot_hostname"), nvram_get_int("iot_port"), 60);
if(rc != MOSQ_ERR_SUCCESS){
syslog(LOG_ERR, "Error connect: %s", mosquitto_strerror(rc));
goto ERROR;
}
if (pthread_create(&publish_pid, NULL, (void *)publish_thread, NULL) != 0)
{
syslog(LOG_NOTICE, "MQTT Thread %d ERR",(int) publish_pid);
goto ERROR;
}
else
{
pthread_detach(publish_pid);
}
#if 1
if (pthread_create(&formate_pid, NULL, (void *)formate_data_thread, NULL) != 0)
{
syslog(LOG_NOTICE, "MQTT formate Thread %d ERR",(int) formate_pid);
goto ERROR;
}
else
{
pthread_detach(formate_pid);
}
#endif
// if (pthread_create(&modbus_pid, NULL, (void *)modbus_process_thread, NULL) != 0)
// {
// syslog(LOG_NOTICE, "MQTT modbus Thread %d ERR",(int) modbus_pid);
// goto ERROR;
// }
// else
// {
// pthread_detach(modbus_pid);
// }
/* Run the network loop in a blocking call. The only thing we do in this
* example is to print incoming messages, so a blocking call here is fine.
*
* This call will continue forever, carrying automatic reconnections if
* necessary, until the user calls mosquitto_disconnect().
*/
//mosquitto_loop_forever(mosq, 60*1000, 1);//keeplive 60 second
do{
rc = mosquitto_loop(mosq, 50, 1);
}while(rc == MOSQ_ERR_SUCCESS);
syslog(LOG_ERR, "End Error: %s", mosquitto_strerror(rc));
ERROR:
pthread_mutex_destroy(&upflow_mutex);
mosquitto_loop_stop(mosq, false);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
mqtt_exit();
return 0;
}
使用上述代码有了更详细的错误日志TLS Error in mosquitto_connect: A TLS error occurred. (code: 8);TLS Error: Generic TLS connection error;Check certificate paths, permissions, and validity;Failed to connect to MQTT broker for device 3: A TLS error occurred.请根据错误日志分析原因并解决