From 5dbddb3cc4bee63085a7f02e0d900e9ea65d2aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Jaramago=20Fern=C3=A1ndez?= Date: Sat, 9 Jan 2021 01:45:54 +0000 Subject: [PATCH] Check for specified 'restapi_port' availability before instantiating 'ProxySQL_RESTAPI_Server' --- lib/ProxySQL_Admin.cpp | 64 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/lib/ProxySQL_Admin.cpp b/lib/ProxySQL_Admin.cpp index 355f9c02e8..6f2d3fd25b 100644 --- a/lib/ProxySQL_Admin.cpp +++ b/lib/ProxySQL_Admin.cpp @@ -5708,6 +5708,51 @@ std::shared_ptr make_response( return response; } +/** + * @brief Checks if the supplied port is available. + * + * @param port_num The port number to check. + * @param free Output parameter. True if the port is free, false otherwise. + * + * @return Returns: + * - '-1' in case 'SO_REUSEADDR' fails to be set for the check. + * - '-2' in case of invalid arguments supplied. + * - '0' otherwise. + */ +int check_port_availability(int port_num, bool* port_free) { + int ecode = 0; + int sfd = 0; + int reuseaddr = 1; + struct sockaddr_in tmp_addr; + + if (port_num == 0 || port_free == nullptr) { + return -2; + } + + // set 'port_free' to false by default + *port_free = false; + + sfd = socket(AF_INET, SOCK_STREAM, 0); + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.sin_family = AF_INET; + tmp_addr.sin_port = htons(port_num); + tmp_addr.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, sizeof(reuseaddr)) == -1) { + close(sfd); + ecode = -1; + } else { + if (bind(sfd, (struct sockaddr*)&tmp_addr, sizeof(tmp_addr)) == -1) { + close(sfd); + } else { + *port_free = true; + close(sfd); + } + } + + return ecode; +} + void ProxySQL_Admin::flush_admin_variables___database_to_runtime(SQLite3DB *db, bool replace) { proxy_debug(PROXY_DEBUG_ADMIN, 4, "Flushing ADMIN variables. Replace:%d\n", replace); char *error=NULL; @@ -5788,8 +5833,23 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime(SQLite3DB *db, return http_response; } }; + + bool free_restapi_port = false; + int e_port_check = check_port_availability(variables.restapi_port, &free_restapi_port); + + if (free_restapi_port == false) { + if (e_port_check == -1) { + proxy_error("Unable to start 'ProxySQL_RestAPI_Server', failed to set 'SO_REUSEADDR' to check port availability.\n"); + } else { + proxy_error( + "Unable to start 'ProxySQL_RestAPI_Server', port '%d' already in use.\n", + variables.restapi_port + ); + } + } + if (variables.restapi_enabled != variables.restapi_enabled_old) { - if (variables.restapi_enabled) { + if (variables.restapi_enabled && free_restapi_port) { AdminRestApiServer = new ProxySQL_RESTAPI_Server( variables.restapi_port, {{"/metrics", prometheus_callback}} ); @@ -5804,7 +5864,7 @@ void ProxySQL_Admin::flush_admin_variables___database_to_runtime(SQLite3DB *db, delete AdminRestApiServer; AdminRestApiServer = NULL; } - if (variables.restapi_enabled) { + if (variables.restapi_enabled && free_restapi_port) { AdminRestApiServer = new ProxySQL_RESTAPI_Server( variables.restapi_port, {{"/metrics", prometheus_callback}} );