/*
* Copyright (C) 2021 Mikhail Burakov. This file is part of MQhTTp.
*
* MQhTTp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MQhTTp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MQhTTp. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "logging.h"
#include "server.h"
static volatile sig_atomic_t g_shutdown;
static void OnSignal(int num) {
(void)num;
g_shutdown = 1;
}
static bool HandleRequest(void* user, int fd, const char* method,
const char* target, const void* body,
size_t body_size) {
const char* reply = NULL;
if (strcmp(method, "POST")) {
reply =
"HTTP/1.1 405 Method Not Allowed\r\n"
"Content-Length: 0\r\n"
"\r\n";
} else if (!body_size) {
reply =
"HTTP/1.1 400 Bad Request\r\n"
"Content-Length: 0\r\n"
"\r\n";
} else {
reply =
"HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"\r\n";
}
// TODO(mburakov): Change to iterative writing.
size_t length = strlen(reply);
ssize_t result = write(fd, reply, length);
if (result != (ssize_t)length) {
Log("Failed to write complete reply (%s)", strerror(errno));
return 0;
}
return 1;
}
int main(int argc, char* argv[]) {
if (argc < 3) Terminate("Usage: %s ", argv[0]);
int port = atoi(argv[2]);
if (0 >= port || port >= 65536)
Terminate("Invalid mosquitto port \"%s\"", argv[2]);
int epfd = epoll_create(1);
if (epfd == -1) Terminate("Failed to create epoll (%s)", strerror(errno));
int result = EXIT_FAILURE;
struct Server* server = ServerCreate(epfd, HandleRequest, NULL);
if (!server) {
Log("Failed to create server");
goto rollback_epoll_create;
}
int mosq_errno = mosquitto_lib_init();
if (mosq_errno != MOSQ_ERR_SUCCESS) {
Log("Failed to initialize mosquitto lib (%s)",
mosquitto_strerror(mosq_errno));
goto rollback_server_create;
}
struct mosquitto* mosq = mosquitto_new(NULL, true, NULL);
if (!mosq) {
Log("Failed to create mosquitto (%s)", strerror(errno));
goto rollback_mosquitto_lib_init;
}
mosq_errno = mosquitto_connect(mosq, argv[1], port, 60);
if (mosq_errno != MOSQ_ERR_SUCCESS) {
Log("Failed to connect mosquitto (%s)", mosquitto_strerror(mosq_errno));
goto rollback_mosquitto_new;
}
int mosq_sock = mosquitto_socket(mosq);
if (mosq_sock == -1) {
Log("Failed to get mosquitto socket");
goto rollback_mosquitto_connect;
}
struct epoll_event ev = {.events = EPOLLIN, .data.fd = mosq_sock};
if (epoll_ctl(epfd, EPOLL_CTL_ADD, mosq_sock, &ev)) {
Log("Failed to add mosquitto to epoll (%s)", strerror(errno));
goto rollback_mosquitto_connect;
}
static const struct sigaction sa = {.sa_handler = OnSignal};
if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) {
Log("Failed to set up signal handlers (%s)", strerror(errno));
goto rollback_mosquitto_connect;
}
while (!g_shutdown) {
switch (epoll_wait(epfd, &ev, 1, 1000)) {
case -1:
Log("Failed to wait epoll (%s)", strerror(errno));
if (errno != EINTR) goto rollback_mosquitto_connect;
continue;
case 0:
mosq_errno = mosquitto_loop_misc(mosq);
if (mosq_errno != MOSQ_ERR_SUCCESS) {
Log("Failed to loop mosquitto (%s)", mosquitto_strerror(mosq_errno));
goto rollback_mosquitto_connect;
}
continue;
default:
break;
}
if (ev.data.fd == mosq_sock) {
mosq_errno = mosquitto_loop_read(mosq, 1);
if (mosq_errno != MOSQ_ERR_SUCCESS) {
Log("Failed to read mosquitto (%s)", mosquitto_strerror(mosq_errno));
goto rollback_mosquitto_connect;
}
continue;
}
if (!ServerMaybeHandle(server, ev.data.fd))
Terminate("Stray socket in epoll");
}
result = EXIT_SUCCESS;
rollback_mosquitto_connect:
mosquitto_disconnect(mosq);
rollback_mosquitto_new:
mosquitto_destroy(mosq);
rollback_mosquitto_lib_init:
mosquitto_lib_cleanup();
rollback_server_create:
ServerDestroy(server);
rollback_epoll_create:
close(epfd);
return result;
}