mosquitto/src/mosquitto.c

648 lines
16 KiB
C
Raw Normal View History

2014-05-07 22:27:00 +00:00
/*
Copyright (c) 2009-2020 Roger Light <roger@atchoo.org>
2014-05-07 22:27:00 +00:00
All rights reserved. This program and the accompanying materials
2020-11-25 17:34:21 +00:00
are made available under the terms of the Eclipse Public License 2.0
2014-05-07 22:27:00 +00:00
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
2020-11-25 17:34:21 +00:00
https://www.eclipse.org/legal/epl-2.0/
2014-05-07 22:27:00 +00:00
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
2020-12-01 18:21:59 +00:00
2014-05-07 22:27:00 +00:00
Contributors:
Roger Light - initial implementation and documentation.
*/
2015-04-29 20:37:47 +00:00
#include "config.h"
2014-05-07 22:27:00 +00:00
#ifndef WIN32
/* For initgroups() */
# include <unistd.h>
# include <grp.h>
# include <assert.h>
2014-05-07 22:27:00 +00:00
#endif
#ifndef WIN32
#include <pwd.h>
#else
#include <process.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#endif
2014-06-02 00:01:29 +00:00
#ifndef WIN32
# include <sys/time.h>
#endif
2014-05-07 22:27:00 +00:00
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_SYSTEMD
# include <systemd/sd-daemon.h>
#endif
2014-05-07 22:27:00 +00:00
#ifdef WITH_WRAP
#include <tcpd.h>
#endif
2014-06-30 22:30:43 +00:00
#ifdef WITH_WEBSOCKETS
# include <libwebsockets.h>
#endif
2014-05-07 22:27:00 +00:00
#include "mosquitto_broker_internal.h"
2015-04-29 20:37:47 +00:00
#include "memory_mosq.h"
#include "misc_mosq.h"
2014-05-07 22:27:00 +00:00
#include "util_mosq.h"
struct mosquitto_db db;
2014-05-07 22:27:00 +00:00
static struct mosquitto__listener_sock *listensock = NULL;
static int listensock_count = 0;
static int listensock_index = 0;
2014-05-07 22:27:00 +00:00
bool flag_reload = false;
#ifdef WITH_PERSISTENCE
bool flag_db_backup = false;
#endif
bool flag_tree_print = false;
int run;
#ifdef WITH_WRAP
#include <syslog.h>
int allow_severity = LOG_INFO;
int deny_severity = LOG_INFO;
#endif
/* mosquitto shouldn't run as root.
* This function will attempt to change to an unprivileged user and group if
* running as root. The user is given in config->user.
* Returns 1 on failure (unknown user, setuid/setgid failure)
* Returns 0 on success.
* Note that setting config->user to "root" does not produce an error, but it
* strongly discouraged.
*/
int drop_privileges(struct mosquitto__config *config)
2014-05-07 22:27:00 +00:00
{
#if !defined(__CYGWIN__) && !defined(WIN32)
struct passwd *pwd;
2018-11-07 17:43:21 +00:00
char *err;
int rc;
2014-05-07 22:27:00 +00:00
const char *snap = getenv("SNAP_NAME");
if(snap && !strcmp(snap, "mosquitto")){
/* Don't attempt to drop privileges if running as a snap */
return MOSQ_ERR_SUCCESS;
}
2014-05-07 22:27:00 +00:00
if(geteuid() == 0){
if(config->user && strcmp(config->user, "root")){
pwd = getpwnam(config->user);
if(!pwd){
if(strcmp(config->user, "mosquitto")){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to drop privileges to '%s' because this user does not exist.", config->user);
return 1;
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Warning: Unable to drop privileges to '%s' because this user does not exist. Trying 'nobody' instead.", config->user);
pwd = getpwnam("nobody");
if(!pwd){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to drop privileges to 'nobody'.");
return 1;
}
}
2014-05-07 22:27:00 +00:00
}
if(initgroups(config->user, pwd->pw_gid) == -1){
2018-11-07 17:43:21 +00:00
err = strerror(errno);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error setting groups whilst dropping privileges: %s.", err);
2014-05-07 22:27:00 +00:00
return 1;
}
rc = setgid(pwd->pw_gid);
if(rc == -1){
2018-11-07 17:43:21 +00:00
err = strerror(errno);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error setting gid whilst dropping privileges: %s.", err);
2014-05-07 22:27:00 +00:00
return 1;
}
rc = setuid(pwd->pw_uid);
if(rc == -1){
2018-11-07 17:43:21 +00:00
err = strerror(errno);
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error setting uid whilst dropping privileges: %s.", err);
2014-05-07 22:27:00 +00:00
return 1;
}
}
if(geteuid() == 0 || getegid() == 0){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Mosquitto should not be run as root/administrator.");
2014-05-07 22:27:00 +00:00
}
}
#else
UNUSED(config);
2014-05-07 22:27:00 +00:00
#endif
return MOSQ_ERR_SUCCESS;
}
2021-03-21 09:17:53 +00:00
static void mosquitto__daemonise(void)
{
#ifndef WIN32
2018-11-07 17:43:21 +00:00
char *err;
pid_t pid;
pid = fork();
if(pid < 0){
2018-11-07 17:43:21 +00:00
err = strerror(errno);
2016-06-21 22:33:58 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error in fork: %s", err);
exit(1);
}
if(pid > 0){
exit(0);
}
if(setsid() < 0){
2018-11-07 17:43:21 +00:00
err = strerror(errno);
2016-06-21 22:33:58 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error in setsid: %s", err);
exit(1);
}
assert(freopen("/dev/null", "r", stdin));
assert(freopen("/dev/null", "w", stdout));
assert(freopen("/dev/null", "w", stderr));
#else
2016-06-21 22:33:58 +00:00
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Can't start in daemon mode in Windows.");
#endif
}
2014-05-07 22:27:00 +00:00
void listener__set_defaults(struct mosquitto__listener *listener)
{
listener->security_options.allow_anonymous = -1;
listener->security_options.allow_zero_length_clientid = true;
listener->protocol = mp_mqtt;
listener->max_connections = -1;
listener->max_qos = 2;
listener->max_topic_alias = 10;
}
void listeners__reload_all_certificates(void)
{
#ifdef WITH_TLS
int i;
int rc;
struct mosquitto__listener *listener;
for(i=0; i<db.config->listener_count; i++){
listener = &db.config->listeners[i];
if(listener->ssl_ctx && listener->certfile && listener->keyfile){
rc = net__load_certificates(listener);
if(rc){
log__printf(NULL, MOSQ_LOG_ERR, "Error when reloading certificate '%s' or key '%s'.",
listener->certfile, listener->keyfile);
}
}
}
#endif
}
2021-03-21 09:17:53 +00:00
static int listeners__start_single_mqtt(struct mosquitto__listener *listener)
{
int i;
struct mosquitto__listener_sock *listensock_new;
if(net__socket_listen(listener)){
return 1;
}
listensock_count += listener->sock_count;
listensock_new = mosquitto__realloc(listensock, sizeof(struct mosquitto__listener_sock)*(size_t)listensock_count);
if(!listensock_new){
return 1;
}
listensock = listensock_new;
for(i=0; i<listener->sock_count; i++){
if(listener->socks[i] == INVALID_SOCKET){
return 1;
}
listensock[listensock_index].sock = listener->socks[i];
listensock[listensock_index].listener = listener;
#ifdef WITH_EPOLL
listensock[listensock_index].ident = id_listener;
#endif
listensock_index++;
}
return MOSQ_ERR_SUCCESS;
}
#ifdef WITH_WEBSOCKETS
void listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd)
{
int i;
struct mosquitto__listener *listener = NULL;
struct mosquitto__listener_sock *listensock_new;
/* Don't add more listeners after we've started the main loop */
if(run || ws_context == NULL) return;
/* Find context */
for(i=0; i<db.config->listener_count; i++){
if(db.config->listeners[i].ws_in_init){
listener = &db.config->listeners[i];
break;
}
}
if(listener == NULL){
return;
}
listensock_count++;
listensock_new = mosquitto__realloc(listensock, sizeof(struct mosquitto__listener_sock)*(size_t)listensock_count);
if(!listensock_new){
return;
}
listensock = listensock_new;
listensock[listensock_index].sock = fd;
listensock[listensock_index].listener = listener;
#ifdef WITH_EPOLL
listensock[listensock_index].ident = id_listener_ws;
#endif
listensock_index++;
}
#endif
2021-03-21 09:17:53 +00:00
static int listeners__add_local(const char *host, uint16_t port)
{
struct mosquitto__listener *listeners;
listeners = db.config->listeners;
listener__set_defaults(&listeners[db.config->listener_count]);
listeners[db.config->listener_count].security_options.allow_anonymous = true;
listeners[db.config->listener_count].port = port;
listeners[db.config->listener_count].host = mosquitto__strdup(host);
if(listeners[db.config->listener_count].host == NULL){
return MOSQ_ERR_NOMEM;
}
if(listeners__start_single_mqtt(&listeners[db.config->listener_count])){
2020-11-30 10:31:14 +00:00
mosquitto__free(listeners[db.config->listener_count].host);
listeners[db.config->listener_count].host = NULL;
return MOSQ_ERR_UNKNOWN;
}
db.config->listener_count++;
return MOSQ_ERR_SUCCESS;
}
2021-03-21 09:17:53 +00:00
static int listeners__start_local_only(void)
{
/* Attempt to open listeners bound to 127.0.0.1 and ::1 only */
int i;
int rc;
struct mosquitto__listener *listeners;
listeners = mosquitto__realloc(db.config->listeners, 2*sizeof(struct mosquitto__listener));
if(listeners == NULL){
return MOSQ_ERR_NOMEM;
}
memset(listeners, 0, 2*sizeof(struct mosquitto__listener));
db.config->listener_count = 0;
db.config->listeners = listeners;
log__printf(NULL, MOSQ_LOG_WARNING, "Starting in local only mode. Connections will only be possible from clients running on this machine.");
log__printf(NULL, MOSQ_LOG_WARNING, "Create a configuration file which defines a listener to allow remote access.");
log__printf(NULL, MOSQ_LOG_WARNING, "For more details see https://mosquitto.org/documentation/authentication-methods/");
if(db.config->cmd_port_count == 0){
rc = listeners__add_local("127.0.0.1", 1883);
if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM;
rc = listeners__add_local("::1", 1883);
if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM;
}else{
for(i=0; i<db.config->cmd_port_count; i++){
rc = listeners__add_local("127.0.0.1", db.config->cmd_port[i]);
if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM;
rc = listeners__add_local("::1", db.config->cmd_port[i]);
if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM;
}
}
if(db.config->listener_count > 0){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_UNKNOWN;
}
}
2021-03-21 09:17:53 +00:00
static int listeners__start(void)
2020-04-08 10:34:31 +00:00
{
int i;
2020-04-08 10:34:31 +00:00
listensock_count = 0;
if(db.config->listener_count == 0){
if(listeners__start_local_only()){
db__close();
if(db.config->pid_file){
(void)remove(db.config->pid_file);
}
return 1;
}
return MOSQ_ERR_SUCCESS;
}
for(i=0; i<db.config->listener_count; i++){
if(db.config->listeners[i].protocol == mp_mqtt){
if(listeners__start_single_mqtt(&db.config->listeners[i])){
db__close();
if(db.config->pid_file){
(void)remove(db.config->pid_file);
2020-04-08 10:34:31 +00:00
}
return 1;
}
}else if(db.config->listeners[i].protocol == mp_websockets){
2020-04-08 10:34:31 +00:00
#ifdef WITH_WEBSOCKETS
mosq_websockets_init(&db.config->listeners[i], db.config);
if(!db.config->listeners[i].ws_context){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to create websockets listener on port %d.", db.config->listeners[i].port);
2020-04-08 10:34:31 +00:00
return 1;
}
#endif
}
}
if(listensock == NULL){
2020-04-08 10:34:31 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to start any listening sockets, exiting.");
return 1;
}
return MOSQ_ERR_SUCCESS;
}
2021-03-21 09:17:53 +00:00
static void listeners__stop(void)
2020-04-08 10:34:31 +00:00
{
int i;
for(i=0; i<db.config->listener_count; i++){
2020-04-08 10:34:31 +00:00
#ifdef WITH_WEBSOCKETS
if(db.config->listeners[i].ws_context){
lws_context_destroy(db.config->listeners[i].ws_context);
2020-04-08 10:34:31 +00:00
}
mosquitto__free(db.config->listeners[i].ws_protocol);
2020-04-08 10:34:31 +00:00
#endif
#ifdef WITH_UNIX_SOCKETS
if(db.config->listeners[i].unix_socket_path != NULL){
unlink(db.config->listeners[i].unix_socket_path);
2020-04-08 10:34:31 +00:00
}
#endif
}
for(i=0; i<listensock_count; i++){
if(listensock[i].sock != INVALID_SOCKET){
COMPAT_CLOSE(listensock[i].sock);
2020-04-08 10:34:31 +00:00
}
}
mosquitto__free(listensock);
}
2021-03-21 09:17:53 +00:00
static void signal__setup(void)
2020-04-08 10:34:31 +00:00
{
signal(SIGINT, handle_sigint);
signal(SIGTERM, handle_sigint);
#ifdef SIGHUP
signal(SIGHUP, handle_sighup);
#endif
#ifndef WIN32
signal(SIGUSR1, handle_sigusr1);
signal(SIGUSR2, handle_sigusr2);
signal(SIGPIPE, SIG_IGN);
#endif
#ifdef WIN32
CreateThread(NULL, 0, SigThreadProc, NULL, 0, NULL);
#endif
}
2021-03-21 09:17:53 +00:00
static int pid__write(void)
2020-04-08 10:34:31 +00:00
{
FILE *pid;
if(db.config->pid_file){
pid = mosquitto__fopen(db.config->pid_file, "wt", false);
2020-04-08 10:34:31 +00:00
if(pid){
fprintf(pid, "%d", getpid());
fclose(pid);
}else{
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to write pid file.");
return 1;
}
}
return MOSQ_ERR_SUCCESS;
}
2014-05-07 22:27:00 +00:00
int main(int argc, char *argv[])
{
2015-05-16 18:03:12 +00:00
struct mosquitto__config config;
#ifdef WITH_BRIDGE
2020-04-08 10:34:31 +00:00
int i;
#endif
2014-05-07 22:27:00 +00:00
int rc;
#ifdef WIN32
SYSTEMTIME st;
#else
struct timeval tv;
#endif
struct mosquitto *ctxt, *ctxt_tmp;
2014-05-07 22:27:00 +00:00
#if defined(WIN32) || defined(__CYGWIN__)
if(argc == 2){
if(!strcmp(argv[1], "run")){
service_run();
return 0;
}else if(!strcmp(argv[1], "install")){
service_install();
return 0;
}else if(!strcmp(argv[1], "uninstall")){
service_uninstall();
return 0;
}
}
#endif
#ifdef WIN32
GetSystemTime(&st);
srand(st.wSecond + st.wMilliseconds);
#else
gettimeofday(&tv, NULL);
2020-10-17 00:23:08 +00:00
srand((unsigned int)(tv.tv_sec + tv.tv_usec));
2014-05-07 22:27:00 +00:00
#endif
#ifdef WIN32
_setmaxstdio(2048);
#endif
memset(&db, 0, sizeof(struct mosquitto_db));
db.now_s = mosquitto_time();
db.now_real_s = time(NULL);
2014-05-07 22:27:00 +00:00
2018-09-18 11:08:49 +00:00
net__broker_init();
2014-05-07 22:27:00 +00:00
config__init(&config);
rc = config__parse_args(&config, argc, argv);
2014-05-07 22:27:00 +00:00
if(rc != MOSQ_ERR_SUCCESS) return rc;
db.config = &config;
2014-05-07 22:27:00 +00:00
/* Drop privileges permanently immediately after the config is loaded.
* This requires the user to ensure that all certificates, log locations,
* etc. are accessible my the `mosquitto` or other unprivileged user.
*/
rc = drop_privileges(&config);
if(rc != MOSQ_ERR_SUCCESS) return rc;
2014-05-07 22:27:00 +00:00
if(config.daemon){
mosquitto__daemonise();
2014-05-07 22:27:00 +00:00
}
if(pid__write()) return 1;
2014-05-07 22:27:00 +00:00
rc = db__open(&config);
2014-05-07 22:27:00 +00:00
if(rc != MOSQ_ERR_SUCCESS){
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_ERR, "Error: Couldn't open database.");
2014-05-07 22:27:00 +00:00
return rc;
}
/* Initialise logging only after initialising the database in case we're
* logging to topics */
2017-03-06 21:19:53 +00:00
if(log__init(&config)){
rc = 1;
return rc;
}
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s starting", VERSION);
if(db.config_file){
log__printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", db.config_file);
2014-05-07 22:27:00 +00:00
}else{
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_INFO, "Using default config.");
2014-05-07 22:27:00 +00:00
}
rc = mosquitto_security_module_init();
2014-05-07 22:27:00 +00:00
if(rc) return rc;
rc = mosquitto_security_init(false);
2014-05-07 22:27:00 +00:00
if(rc) return rc;
/* After loading persisted clients and ACLs, try to associate them,
* so persisted subscriptions can start storing messages */
HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){
if(ctxt && !ctxt->clean_start && ctxt->username){
rc = acl__find_acls(ctxt);
if(rc){
log__printf(NULL, MOSQ_LOG_WARNING, "Failed to associate persisted user %s with ACLs, "
"likely due to changed ports while using a per_listener_settings configuration.", ctxt->username);
}
}
}
2014-05-07 22:27:00 +00:00
#ifdef WITH_SYS_TREE
sys_tree__init();
2014-05-07 22:27:00 +00:00
#endif
if(listeners__start()) return 1;
2014-05-07 22:27:00 +00:00
rc = mux__init(listensock, listensock_count);
if(rc) return rc;
2020-04-08 10:34:31 +00:00
signal__setup();
2014-05-07 22:27:00 +00:00
#ifdef WITH_BRIDGE
bridge__start_all();
2014-05-07 22:27:00 +00:00
#endif
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s running", VERSION);
#ifdef WITH_SYSTEMD
sd_notify(0, "READY=1");
#endif
2014-05-07 22:27:00 +00:00
run = 1;
rc = mosquitto_main_loop(listensock, listensock_count);
2014-05-07 22:27:00 +00:00
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s terminating", VERSION);
2014-05-07 22:27:00 +00:00
2019-02-25 22:28:51 +00:00
/* FIXME - this isn't quite right, all wills with will delay zero should be
* sent now, but those with positive will delay should be persisted and
* restored, pending the client reconnecting in time. */
HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){
context__send_will(ctxt);
}
will_delay__send_all();
#ifdef WITH_PERSISTENCE
persist__backup(true);
#endif
session_expiry__remove_all();
listeners__stop();
HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){
2014-06-30 22:30:43 +00:00
#ifdef WITH_WEBSOCKETS
if(!ctxt->wsi)
#endif
{
context__cleanup(ctxt, true);
2014-06-30 22:30:43 +00:00
}
2014-05-07 22:27:00 +00:00
}
HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){
context__cleanup(ctxt, true);
2014-06-28 00:38:58 +00:00
}
2014-06-30 22:37:03 +00:00
#ifdef WITH_BRIDGE
for(i=0; i<db.bridge_count; i++){
if(db.bridges[i]){
context__cleanup(db.bridges[i], true);
}
}
mosquitto__free(db.bridges);
2014-06-30 22:37:03 +00:00
#endif
context__free_disused();
2014-06-30 22:30:43 +00:00
db__close();
2014-05-07 22:27:00 +00:00
mosquitto_security_module_cleanup();
2014-05-07 22:27:00 +00:00
if(config.pid_file){
(void)remove(config.pid_file);
2014-05-07 22:27:00 +00:00
}
log__close(&config);
config__cleanup(db.config);
2018-09-19 12:01:13 +00:00
net__broker_cleanup();
2014-05-07 22:27:00 +00:00
return rc;
}
#ifdef WIN32
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2014-05-07 22:27:00 +00:00
{
char **argv;
int argc = 1;
char *token;
char *saveptr = NULL;
int rc;
UNUSED(hInstance);
UNUSED(hPrevInstance);
UNUSED(nCmdShow);
argv = mosquitto__malloc(sizeof(char *)*1);
2014-05-07 22:27:00 +00:00
argv[0] = "mosquitto";
token = strtok_r(lpCmdLine, " ", &saveptr);
while(token){
argc++;
argv = mosquitto__realloc(argv, sizeof(char *)*argc);
2014-05-07 22:27:00 +00:00
if(!argv){
fprintf(stderr, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
argv[argc-1] = token;
token = strtok_r(NULL, " ", &saveptr);
}
rc = main(argc, argv);
mosquitto__free(argv);
2014-05-07 22:27:00 +00:00
return rc;
}
#endif