void _mosquitto_destroy(struct mosquitto *mosq) { struct _mosquitto_packet *packet; if(!mosq) return; #ifdef WITH_THREADING if(!pthread_equal(mosq->thread_id, pthread_self())){ pthread_cancel(mosq->thread_id); pthread_join(mosq->thread_id, NULL); } if(mosq->id){ /* If mosq->id is not NULL then the client has already been initialised * and so the mutexes need destroying. If mosq->id is NULL, the mutexes * haven't been initialised. */ pthread_mutex_destroy(&mosq->callback_mutex); pthread_mutex_destroy(&mosq->log_callback_mutex); pthread_mutex_destroy(&mosq->state_mutex); pthread_mutex_destroy(&mosq->out_packet_mutex); pthread_mutex_destroy(&mosq->current_out_packet_mutex); pthread_mutex_destroy(&mosq->msgtime_mutex); } #endif if(mosq->sock != INVALID_SOCKET){ _mosquitto_socket_close(mosq); } _mosquitto_message_cleanup_all(mosq); _mosquitto_will_clear(mosq); #ifdef WITH_TLS if(mosq->ssl){ SSL_free(mosq->ssl); } if(mosq->ssl_ctx){ SSL_CTX_free(mosq->ssl_ctx); } if(mosq->tls_cafile) _mosquitto_free(mosq->tls_cafile); if(mosq->tls_capath) _mosquitto_free(mosq->tls_capath); if(mosq->tls_certfile) _mosquitto_free(mosq->tls_certfile); if(mosq->tls_keyfile) _mosquitto_free(mosq->tls_keyfile); if(mosq->tls_pw_callback) mosq->tls_pw_callback = NULL; if(mosq->tls_version) _mosquitto_free(mosq->tls_version); if(mosq->tls_ciphers) _mosquitto_free(mosq->tls_ciphers); if(mosq->tls_psk) _mosquitto_free(mosq->tls_psk); if(mosq->tls_psk_identity) _mosquitto_free(mosq->tls_psk_identity); #endif if(mosq->address) _mosquitto_free(mosq->address); if(mosq->id) _mosquitto_free(mosq->id); if(mosq->username) _mosquitto_free(mosq->username); if(mosq->password) _mosquitto_free(mosq->password); if(mosq->host) _mosquitto_free(mosq->host); /* Out packet cleanup */ if(mosq->out_packet && !mosq->current_out_packet){ mosq->current_out_packet = mosq->out_packet; mosq->out_packet = mosq->out_packet->next; } while(mosq->current_out_packet){ packet = mosq->current_out_packet; /* Free data and reset values */ mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; } _mosquitto_packet_cleanup(packet); _mosquitto_free(packet); } _mosquitto_packet_cleanup(&mosq->in_packet); }
/* * This will result in any outgoing packets going unsent. If we're disconnected * forcefully then it is usually an error condition and shouldn't be a problem, * but it will mean that CONNACK messages will never get sent for bad protocol * versions for example. */ void mqtt3_context_cleanup(struct mosquitto_db *db, struct mosquitto *context, bool do_free) { struct _mosquitto_packet *packet; struct mosquitto_client_msg *msg, *next; int i; if(!context) return; if(context->username){ _mosquitto_free(context->username); context->username = NULL; } if(context->password){ _mosquitto_free(context->password); context->password = NULL; } #ifdef WITH_BRIDGE if(context->bridge){ for(i=0; i<db->bridge_count; i++){ if(db->bridges[i] == context){ db->bridges[i] = NULL; } } if(context->bridge->local_clientid){ _mosquitto_free(context->bridge->local_clientid); context->bridge->local_clientid = NULL; } if(context->bridge->remote_username){ context->bridge->remote_username = NULL; } if(context->bridge->remote_password){ context->bridge->remote_password = NULL; } if(context->bridge->local_username){ context->bridge->local_username = NULL; } if(context->bridge->local_password){ context->bridge->local_password = NULL; } if(context->bridge->local_clientid){ context->bridge->local_clientid = NULL; } } #endif _mosquitto_socket_close(db, context); if((do_free || context->clean_session) && db){ mqtt3_subs_clean_session(db, context); mqtt3_db_messages_delete(db, context); } if(context->address){ _mosquitto_free(context->address); context->address = NULL; } if(context->id){ assert(db); /* db can only be NULL here if the client hasn't sent a CONNECT and hence wouldn't have an id. */ HASH_DELETE(hh_id, db->contexts_by_id, context); _mosquitto_free(context->id); context->id = NULL; } _mosquitto_packet_cleanup(&(context->in_packet)); if(context->current_out_packet){ _mosquitto_packet_cleanup(context->current_out_packet); _mosquitto_free(context->current_out_packet); context->current_out_packet = NULL; } while(context->out_packet){ _mosquitto_packet_cleanup(context->out_packet); packet = context->out_packet; context->out_packet = context->out_packet->next; _mosquitto_free(packet); } if(context->will){ if(context->will->topic) _mosquitto_free(context->will->topic); if(context->will->payload) _mosquitto_free(context->will->payload); _mosquitto_free(context->will); context->will = NULL; } if(do_free || context->clean_session){ msg = context->msgs; while(msg){ next = msg->next; mosquitto__db_msg_store_deref(db, &msg->store); _mosquitto_free(msg); msg = next; } context->msgs = NULL; context->last_msg = NULL; } if(do_free){ _mosquitto_free(context); } }
void _mosquitto_check_keepalive(struct mosquitto *mosq) { time_t last_msg_out; time_t last_msg_in; time_t now = mosquitto_time(); #ifndef WITH_BROKER int rc; #endif assert(mosq); #if defined(WITH_BROKER) && defined(WITH_BRIDGE) /* Check if a lazy bridge should be timed out due to idle. */ if(mosq->bridge && mosq->bridge->start_type == bst_lazy && mosq->sock != INVALID_SOCKET && now - mosq->last_msg_out >= mosq->bridge->idle_timeout){ _mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id); _mosquitto_socket_close(mosq); return; } #endif pthread_mutex_lock(&mosq->msgtime_mutex); last_msg_out = mosq->last_msg_out; last_msg_in = mosq->last_msg_in; pthread_mutex_unlock(&mosq->msgtime_mutex); if(mosq->keepalive && mosq->sock != INVALID_SOCKET && (now - last_msg_out >= mosq->keepalive || now - last_msg_in >= mosq->keepalive)){ if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){ _mosquitto_send_pingreq(mosq); /* Reset last msg times to give the server time to send a pingresp */ pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = now; mosq->last_msg_out = now; pthread_mutex_unlock(&mosq->msgtime_mutex); }else{ #ifdef WITH_BROKER if(mosq->listener){ mosq->listener->client_count--; assert(mosq->listener->client_count >= 0); } mosq->listener = NULL; #endif _mosquitto_socket_close(mosq); #ifndef WITH_BROKER pthread_mutex_lock(&mosq->state_mutex); if(mosq->state == mosq_cs_disconnecting){ rc = MOSQ_ERR_SUCCESS; }else{ rc = 1; } pthread_mutex_unlock(&mosq->state_mutex); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); #endif } } }
int _mosquitto_packet_write(struct mosquitto *mosq) { ssize_t write_length; struct _mosquitto_packet *packet; if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; pthread_mutex_lock(&mosq->current_out_packet_mutex); pthread_mutex_lock(&mosq->out_packet_mutex); if(mosq->out_packet && !mosq->current_out_packet){ mosq->current_out_packet = mosq->out_packet; mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } } pthread_mutex_unlock(&mosq->out_packet_mutex); if(mosq->state == mosq_cs_connect_pending){ pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; } while(mosq->current_out_packet){ packet = mosq->current_out_packet; while(packet->to_process > 0){ write_length = _mosquitto_net_write(mosq, &(packet->payload[packet->pos]), packet->to_process); if(write_length > 0){ #if defined(WITH_BROKER) && defined(WITH_SYS_TREE) g_bytes_sent += write_length; #endif packet->to_process -= write_length; packet->pos += write_length; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; }else{ pthread_mutex_unlock(&mosq->current_out_packet_mutex); switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; default: return MOSQ_ERR_ERRNO; } } } } #ifdef WITH_BROKER # ifdef WITH_SYS_TREE g_msgs_sent++; if(((packet->command)&0xF6) == PUBLISH){ g_pub_msgs_sent++; } # endif #else if(((packet->command)&0xF6) == PUBLISH){ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_publish){ /* This is a QoS=0 message */ mosq->in_callback = true; mosq->on_publish(mosq, mosq->userdata, packet->mid); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); }else if(((packet->command)&0xF0) == DISCONNECT){ /* FIXME what cleanup needs doing here? * incoming/outgoing messages? */ _mosquitto_socket_close(mosq); /* Start of duplicate, possibly unnecessary code. * This does leave things in a consistent state at least. */ /* Free data and reset values */ pthread_mutex_lock(&mosq->out_packet_mutex); mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } } pthread_mutex_unlock(&mosq->out_packet_mutex); _mosquitto_packet_cleanup(packet); _mosquitto_free(packet); pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_out = mosquitto_time(); pthread_mutex_unlock(&mosq->msgtime_mutex); /* End of duplicate, possibly unnecessary code */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, 0); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; } #endif /* Free data and reset values */ pthread_mutex_lock(&mosq->out_packet_mutex); mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } } pthread_mutex_unlock(&mosq->out_packet_mutex); _mosquitto_packet_cleanup(packet); _mosquitto_free(packet); pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_out = mosquitto_time(); pthread_mutex_unlock(&mosq->msgtime_mutex); } pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; }
int mqtt3_bridge_connect(struct mosquitto_db *db, struct mosquitto *context) { int rc; int i; char *notification_topic; int notification_topic_len; uint8_t notification_payload; if(!context || !context->bridge) return MOSQ_ERR_INVAL; context->state = mosq_cs_new; context->sock = INVALID_SOCKET; context->last_msg_in = mosquitto_time(); context->last_msg_out = mosquitto_time(); context->keepalive = context->bridge->keepalive; context->clean_session = context->bridge->clean_session; context->in_packet.payload = NULL; context->ping_t = 0; context->bridge->lazy_reconnect = false; mqtt3_bridge_packet_cleanup(context); mqtt3_db_message_reconnect_reset(db, context); if(context->clean_session){ mqtt3_db_messages_delete(db, context); } /* Delete all local subscriptions even for clean_session==false. We don't * remove any messages and the next loop carries out the resubscription * anyway. This means any unwanted subs will be removed. */ mqtt3_subs_clean_session(db, context); for(i=0; i<context->bridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); if(mqtt3_sub_add(db, context, context->bridge->topics[i].local_topic, context->bridge->topics[i].qos, &db->subs)) return 1; } } if(context->bridge->notifications){ if(context->bridge->notification_topic){ if(!context->bridge->initial_notification_done){ notification_payload = '0'; mqtt3_db_messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1); context->bridge->initial_notification_done = true; } notification_payload = '0'; rc = _mosquitto_will_set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true); if(rc != MOSQ_ERR_SUCCESS){ return rc; } }else{ notification_topic_len = strlen(context->bridge->remote_clientid)+strlen("$SYS/broker/connection//state"); notification_topic = _mosquitto_malloc(sizeof(char)*(notification_topic_len+1)); if(!notification_topic) return MOSQ_ERR_NOMEM; snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->remote_clientid); if(!context->bridge->initial_notification_done){ notification_payload = '0'; mqtt3_db_messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1); context->bridge->initial_notification_done = true; } notification_payload = '0'; rc = _mosquitto_will_set(context, notification_topic, 1, ¬ification_payload, 1, true); _mosquitto_free(notification_topic); if(rc != MOSQ_ERR_SUCCESS){ return rc; } } } _mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "Connecting bridge %s (%s:%d)", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port); rc = _mosquitto_socket_connect(context, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port, NULL, false); if(rc > 0 ){ if(rc == MOSQ_ERR_TLS){ _mosquitto_socket_close(db, context); return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } return rc; } HASH_ADD(hh_sock, db->contexts_by_sock, sock, sizeof(context->sock), context); if(rc == MOSQ_ERR_CONN_PENDING){ context->state = mosq_cs_connect_pending; } rc = _mosquitto_send_connect(context, context->keepalive, context->clean_session); if(rc == MOSQ_ERR_SUCCESS){ return MOSQ_ERR_SUCCESS; }else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){ return MOSQ_ERR_SUCCESS; }else{ if(rc == MOSQ_ERR_TLS){ return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } _mosquitto_socket_close(db, context); return rc; } }
/* * This will result in any outgoing packets going unsent. If we're disconnected * forcefully then it is usually an error condition and shouldn't be a problem, * but it will mean that CONNACK messages will never get sent for bad protocol * versions for example. */ void mqtt3_context_cleanup(struct mosquitto_db *db, struct mosquitto *context, bool do_free) { struct _mosquitto_packet *packet; struct mosquitto_client_msg *msg, *next; struct _clientid_index_hash *find_cih; if(!context) return; if(context->username){ _mosquitto_free(context->username); context->username = NULL; } if(context->password){ _mosquitto_free(context->password); context->password = NULL; } #ifdef WITH_BRIDGE if(context->bridge){ if(context->bridge->username){ context->bridge->username = NULL; } if(context->bridge->password){ context->bridge->password = NULL; } } #endif #ifdef WITH_TLS if(context->ssl){ SSL_free(context->ssl); context->ssl = NULL; } #endif if(context->sock != -1){ if(context->listener){ context->listener->client_count--; assert(context->listener->client_count >= 0); } _mosquitto_socket_close(context); context->listener = NULL; } if(context->clean_session && db){ mqtt3_subs_clean_session(db, context, &db->subs); mqtt3_db_messages_delete(context); } if(context->address){ _mosquitto_free(context->address); context->address = NULL; } if(context->id){ assert(db); /* db can only be NULL here if the client hasn't sent a CONNECT and hence wouldn't have an id. */ // Remove the context's ID from the DB hash HASH_FIND_STR(db->clientid_index_hash, context->id, find_cih); if(find_cih){ // FIXME - internal level debug? _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Found id for client \"%s\", their index was %d.", context->id, find_cih->db_context_index); HASH_DEL(db->clientid_index_hash, find_cih); _mosquitto_free(find_cih); }else{ // FIXME - internal level debug? _mosquitto_log_printf(NULL, MOSQ_LOG_WARNING, "Unable to find id for client \"%s\".", context->id); } _mosquitto_free(context->id); context->id = NULL; } _mosquitto_packet_cleanup(&(context->in_packet)); _mosquitto_packet_cleanup(context->current_out_packet); while(context->out_packet){ _mosquitto_packet_cleanup(context->out_packet); packet = context->out_packet; context->out_packet = context->out_packet->next; _mosquitto_free(packet); } if(context->will){ if(context->will->topic) _mosquitto_free(context->will->topic); if(context->will->payload) _mosquitto_free(context->will->payload); _mosquitto_free(context->will); context->will = NULL; } if(do_free || context->clean_session){ msg = context->msgs; while(msg){ next = msg->next; msg->store->ref_count--; _mosquitto_free(msg); msg = next; } context->msgs = NULL; context->last_msg = NULL; } if(do_free){ _mosquitto_free(context); } }