diff options
author | Ladar Levison <ladar@lavabit.com> | 2018-11-07 22:48:56 +0300 |
---|---|---|
committer | Ladar Levison <ladar@lavabit.com> | 2018-11-07 22:48:56 +0300 |
commit | 87ef4b88f3e06b44450611d9bcf1c0c0c754edd8 (patch) | |
tree | 587ce71e48f772ea524ccfe28a21defbc35c7bec | |
parent | 26d9c5dee9401bbbcf1893b5856cb5f85a3eb91c (diff) |
Cleaned up the shutdown process.
-rw-r--r-- | check/magma/magma_check.c | 3 | ||||
-rw-r--r-- | src/engine/context/signal.c | 79 | ||||
-rw-r--r-- | src/engine/controller/protocol.c | 8 | ||||
-rw-r--r-- | src/engine/controller/queue.c | 2 | ||||
-rw-r--r-- | src/engine/status/status.c | 2 | ||||
-rw-r--r-- | src/network/clients.c | 6 | ||||
-rw-r--r-- | src/network/listeners.c | 103 | ||||
-rw-r--r-- | src/network/network.h | 1 | ||||
-rw-r--r-- | src/providers/cryptography/tls.c | 2 |
9 files changed, 122 insertions, 84 deletions
diff --git a/check/magma/magma_check.c b/check/magma/magma_check.c index 6ffc14da..fabb8853 100644 --- a/check/magma/magma_check.c +++ b/check/magma/magma_check.c @@ -365,7 +365,8 @@ int main(int argc, char *argv[]) { srunner_free(sr); // Cleanup the background listening thread. - thread_cancel(*net_listen_thread); + net_trigger(false); + thread_join(*net_listen_thread); mm_free(net_listen_thread); diff --git a/src/engine/context/signal.c b/src/engine/context/signal.c index a3925120..02d55ec6 100644 --- a/src/engine/context/signal.c +++ b/src/engine/context/signal.c @@ -51,18 +51,11 @@ void signal_segfault(int signal) { */ void signal_shutdown(int signal) { - ip_t ip; - char working[64]; - struct stat64 info; - struct rlimit64 limits; pthread_t status_thread; - server_t *server = NULL; - struct sockaddr *saddr = MEMORYBUF(sizeof(struct sockaddr_in6)); - socklen_t len = sizeof(struct sockaddr_in6); const struct timespec split = { .tv_sec = 0, .tv_nsec = 100000000 }, single = { .tv_sec = 1, .tv_nsec = 0 }; // We assume the server is being shutdown for a good reason. - log_critical("Signal received. The Magma daemon is attempting a graceful exit. { signal = %s }", signal_name(signal, working, 32)); + log_critical("Signal received. The Magma daemon is attempting a graceful exit. { signal = %s }", signal_name(signal, MEMORYBUF(32), 32)); // Clear the thread structure or we'll get a segfault when we attempt the thread join operation. mm_wipe(&status_thread, sizeof(pthread_t)); @@ -70,74 +63,24 @@ void signal_shutdown(int signal) { // Set the status flag so all the worker threads exit nicely. thread_launch(&status_thread, &status_signal, NULL); - // Loop through and shutdown all of the socket descriptors used to listen for incomoing connections. - for (uint64_t i = 0; i < MAGMA_SERVER_INSTANCES; i++) { - if ((server = magma.servers[i]) && server->enabled && server->network.sockd > 0) { - shutdown(server->network.sockd, SHUT_RDWR); - } - } -// -// // We give threads 0.1 seconds to ensure the status update is queued and awaiting the lock. + // We give threads 0.1 seconds to ensure the status update is queued and awaiting the lock. nanosleep(&split, NULL); -// -// // Signals the worker threads, so they unblock. -// queue_signal(); -// -// // We give threads 0.1 seconds to let the status update. -// nanosleep(&split, NULL); -// -// // Signals the worker threads, so they unblock one more time and see the updated status, thus exiting normally. -// queue_signal(); -// -// // Then sleep for one second before forcibly shutting down the client connections. + + // Signals the worker threads, so they unblock one more time and see the updated status, thus exiting normally. + queue_signal(); + + // Then sleep for one second before forcibly shutting down the client connections. nanosleep(&single, NULL); nanosleep(&single, NULL); nanosleep(&single, NULL); - thread_join(status_thread); - - // Now go through and shutdown all client connections. - if (getrlimit64(RLIMIT_NOFILE, &limits)) { - log_critical("Unable to determine the maximum legal file descriptor."); - thread_join(status_thread); - return; - } - - // Loop through and check all of the potentially valid file descriptors. - for (int fd = 0; fd <= limits.rlim_max; fd++) { - - mm_wipe(&info, sizeof(struct stat64)); - mm_wipe(&saddr, sizeof(struct sockaddr_in6)); - - /// LOW: This only compares the port number for the sockets. We should also ensure the socket is owned by magmad, and/or - /// that the server used INADDR_ANY or IN6ADDR_ANY_INIT, otherwise the logic below will close sockets that could be owned by - /// other processes on the system that are using the same port number, while bound to a different IP interface. - // Look for socket descriptors using ports assigned to server instances and close them. - if (!fstat64(fd, &info) && S_ISSOCK(info.st_mode) && !getsockname(fd, saddr, &len)) { - - if (len == sizeof(struct sockaddr_in6) && ((struct sockaddr_in6 *)saddr)->sin6_family == AF_INET6 && - servers_get_count_using_port(ntohs(((struct sockaddr_in6 *)saddr)->sin6_port))) { - - mm_copy(&(ip.ip6), &(((struct sockaddr_in6 *)saddr)->sin6_addr), sizeof(struct in6_addr)); - ip.family = AF_INET6; - log_info("%s:%u is being shutdown.", st_char_get(ip_presentation(&ip, PLACER(working, 64))), - ntohs(((struct sockaddr_in6 *)saddr)->sin6_port)); - shutdown(fd, SHUT_RDWR); - } - else if (len == sizeof(struct sockaddr_in) && (((struct sockaddr_in *)saddr)->sin_family == AF_INET && - servers_get_count_using_port(ntohs(((struct sockaddr_in *)saddr)->sin_port)))) { - - mm_copy(&(ip.ip4), &(((struct sockaddr_in *)saddr)->sin_addr), sizeof(struct in_addr)); - ip.family = AF_INET; - log_info("%s:%u is being shutdown.", st_char_get(ip_presentation(&ip, PLACER(working, 64))), - ntohs(((struct sockaddr_in *)saddr)->sin_port)); - shutdown(fd, SHUT_RDWR); - } - } - } + // Shutdown any remaining threads. + net_trigger(true); // Signals the worker threads, so they unblock and see the underlying connection has been shutdown. queue_signal(); + + thread_join(status_thread); return; } diff --git a/src/engine/controller/protocol.c b/src/engine/controller/protocol.c index 65394820..4f517fa2 100644 --- a/src/engine/controller/protocol.c +++ b/src/engine/controller/protocol.c @@ -171,8 +171,12 @@ void protocol_secure(connection_t *con) { // Create a new TLS object. if (!(con->network.tls = tls_server_alloc(con->server, con->network.sockd, M_SSL_BIO_NOCLOSE))) { - log_pedantic("The TLS connection attempt failed. { ip = %s / port = %u / protocol = %.*s }", st_char_get(con_addr_presentation(con, MANAGEDBUF(256))), - con->server->network.port, st_length_int(protocol_type(con)), st_char_get(protocol_type(con))); +#ifdef MAGMA_PEDANTIC + if (status()) { + log_pedantic("The TLS connection attempt failed. { ip = %s / port = %u / protocol = %.*s }", st_char_get(con_addr_presentation(con, MANAGEDBUF(256))), + con->server->network.port, st_length_int(protocol_type(con)), st_char_get(protocol_type(con))); + } +#endif // We manually free the connection structure since calling con_destroy() would improperly decrement the statistical counters. if (con->network.tls) tls_free(con->network.tls); diff --git a/src/engine/controller/queue.c b/src/engine/controller/queue.c index b40155ce..a40a79ae 100644 --- a/src/engine/controller/queue.c +++ b/src/engine/controller/queue.c @@ -136,7 +136,7 @@ void queue_signal(void) { for (uint64_t i = 0; queue.workers && i < magma.system.worker_threads; i++) { - if (queue.workers + i && (ret = thread_signal(*(queue.workers + i), SIGALRM)) && status()) { + if (queue.workers + i && (ret = thread_signal(*(queue.workers + i), SIGALRM))) { log_info("Unable to signal the worker thread. {ret = %i}", ret); } diff --git a/src/engine/status/status.c b/src/engine/status/status.c index a7215812..808d1f61 100644 --- a/src/engine/status/status.c +++ b/src/engine/status/status.c @@ -35,7 +35,7 @@ bool_t status(void) { } /** - * @brief Set the status level to a specified value. + * @brief Set the status level to provided value. * @see status() * @param value the integer value of the new status level. * @return This function returns no value. diff --git a/src/network/clients.c b/src/network/clients.c index 96647907..e6fba773 100644 --- a/src/network/clients.c +++ b/src/network/clients.c @@ -84,8 +84,12 @@ client_t * client_connect(chr_t *host, uint32_t port) { // Resolve the hostname. if ((ret = getaddrinfo(host, service, &hints, &info)) || !info || info->ai_socktype != SOCK_STREAM) { - log_pedantic("Unable to resolve the host %s:%u and create a client connection. { getaddrinfo = %i / errno = %s }", host, + +#ifdef MAGMA_PEDANTIC + if (status()) log_pedantic("Unable to resolve the host %s:%u and create a client connection. { getaddrinfo = %i / errno = %s }", host, port, ret, strerror_r(errno, MEMORYBUF(256), 256)); +#endif + if (info) freeaddrinfo(info); return NULL ; } diff --git a/src/network/listeners.c b/src/network/listeners.c index b0d1c661..6a9c4b15 100644 --- a/src/network/listeners.c +++ b/src/network/listeners.c @@ -119,7 +119,101 @@ bool_t net_init(server_t *server) { return true; } +/** + * @brief Trigers a connection for each server instance, allowing the the listeners to shutdown cleanly. And then purges + * any remaining sockets. + */ +void net_trigger(bool_t verbose) { + + ip_t lip, rip; + client_t *client = NULL; + server_t *server = NULL; + bool_t connected = false; + struct rlimit64 limits; + struct stat64 info; + socklen_t slen = sizeof(struct sockaddr_in6), rlen = sizeof(struct sockaddr_in6); + struct sockaddr *saddr = MEMORYBUF(sizeof(struct sockaddr_in6)), *raddr = MEMORYBUF(sizeof(struct sockaddr_in6)); + + // Wakeup the listening threads. + for (uint64_t i = 0; i < MAGMA_SERVER_INSTANCES; i++) { + if ((server = magma.servers[i]) && server->enabled && server->network.sockd > 0) { + client_connect("localhost", server->network.port); + client_close(client); + shutdown(server->network.sockd, SHUT_RDWR); + } + } + + // Now go through and shutdown all client connections. + if (getrlimit64(RLIMIT_NOFILE, &limits)) { + log_critical("Unable to determine the maximum legal file descriptor."); + return; + } + + // Loop through and check all of the potentially valid file descriptors. + for (int fd = 0; fd <= limits.rlim_max; fd++) { + + connected = false; + mm_wipe(&lip, sizeof(ip_t)); + mm_wipe(&rip, sizeof(ip_t)); + mm_wipe(&info, sizeof(struct stat64)); + mm_wipe(saddr, sizeof(struct sockaddr_in6)); + mm_wipe(raddr, sizeof(struct sockaddr_in6)); + + /// LOW: This only compares the port number for the sockets. We should also ensure the socket is owned by magmad, and/or + /// that the server used INADDR_ANY or IN6ADDR_ANY_INIT, otherwise the logic below will close sockets that could be owned by + /// other processes on the system that are using the same port number, while bound to a different IP interface. + // Look for socket descriptors using ports assigned to server instances and close them. + if (!fstat64(fd, &info) && S_ISSOCK(info.st_mode) && !getsockname(fd, saddr, &slen)) { + + if (!getpeername(fd, raddr, &rlen)) { + if (rlen == sizeof(struct sockaddr_in6) && ((struct sockaddr_in6 *)raddr)->sin6_family == AF_INET6) { + mm_copy(&(rip.ip6), &(((struct sockaddr_in6 *)raddr)->sin6_addr), sizeof(struct in6_addr)); + rip.family = AF_INET6; + connected = true; + } + else if (rlen == sizeof(struct sockaddr_in) && ((struct sockaddr_in *)raddr)->sin_family == AF_INET) { + mm_copy(&(rip.ip4), &(((struct sockaddr_in *)raddr)->sin_addr), sizeof(struct in_addr)); + rip.family = AF_INET; + connected = true; + } + } + + if (slen == sizeof(struct sockaddr_in6) && ((struct sockaddr_in6 *)saddr)->sin6_family == AF_INET6 && + servers_get_count_using_port(ntohs(((struct sockaddr_in6 *)saddr)->sin6_port))) { + mm_copy(&(lip.ip6), &(((struct sockaddr_in6 *)saddr)->sin6_addr), sizeof(struct in6_addr)); + lip.family = AF_INET6; + shutdown(fd, SHUT_RDWR); + } + else if (slen == sizeof(struct sockaddr_in) && (((struct sockaddr_in *)saddr)->sin_family == AF_INET && + servers_get_count_using_port(ntohs(((struct sockaddr_in *)saddr)->sin_port)))) { + mm_copy(&(lip.ip4), &(((struct sockaddr_in *)saddr)->sin_addr), sizeof(struct in_addr)); + lip.family = AF_INET; + shutdown(fd, SHUT_RDWR); + } + else { + connected = false; + } + + if (connected && verbose) log_info("%s:%u <-> %s:%u is being shutdown.", st_char_get(ip_presentation(&lip, PLACER(MEMORYBUF(64), 64))), + ntohs(((struct sockaddr_in6 *)saddr)->sin6_port), st_char_get(ip_presentation(&rip, PLACER(MEMORYBUF(64), 64))), + ntohs(((struct sockaddr_in6 *)raddr)->sin6_port)); + } + } + + // Signals the worker threads, so they unblock and see the underlying connection has been shutdown. + queue_signal(); + + return; +} +/** + * @brief Close the listening socket associated with a server. + * @return This function returns no value. + */ +void net_shutdown(server_t *server) { + close(server->network.sockd); + return; +} /** * @brief The main network handler entry point; poll the listening socket of each configured protocol server, and dispatch the @@ -289,12 +383,3 @@ bool_t net_init(server_t *server) { // // return true; //} - -/** - * @brief Close the listening socket associated with a server. - * @return This function returns no value. - */ -void net_shutdown(server_t *server) { - close(server->network.sockd); - return; -} diff --git a/src/network/network.h b/src/network/network.h index 71b0948d..36c5839c 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -128,6 +128,7 @@ void con_reverse_status(connection_t *con, int_t status); bool_t net_init(server_t *server); void net_listen(void); void net_shutdown(server_t *server); +void net_trigger(bool_t verbose); /// write.c int64_t client_print(client_t *client, chr_t *format, ...); diff --git a/src/providers/cryptography/tls.c b/src/providers/cryptography/tls.c index bce6e53f..03e05c40 100644 --- a/src/providers/cryptography/tls.c +++ b/src/providers/cryptography/tls.c @@ -197,7 +197,7 @@ TLS * tls_server_alloc(void *server, int sockd, int flags) { // If the result code indicates a handshake error, but the TCP connection is still alive, we retry the handshake. do { // Attempt the server connection setup. - if ((result = SSL_accept_d(tls)) <= 0) { + if ((result = SSL_accept_d(tls)) <= 0 && status()) { switch ((error = SSL_get_error_d(tls, result))) { |