/* * This file is distributed as part of the SkySQL Gateway. It 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, * version 2. * * This program 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 * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Copyright SkySQL Ab 2013 */ /** * @file server.c - A representation of a backend server within the gateway. * * @verbatim * Revision History * * Date Who Description * 18/06/13 Mark Riddoch Initial implementation * 17/05/14 Mark Riddoch Addition of unique_name * 20/05/14 Massimiliano Pinto Addition of server_string * 21/05/14 Massimiliano Pinto Addition of node_id * 28/05/14 Massimiliano Pinto Addition of rlagd and node_ts fields * * @endverbatim */ #include #include #include #include #include #include #include #include #include extern int lm_enabled_logfiles_bitmask; static SPINLOCK server_spin = SPINLOCK_INIT; static SERVER *allServers = NULL; /** * Allocate a new server withn the gateway * * * @param servname The server name * @param protocol The protocol to use to connect to the server * @param port The port to connect to * * @return The newly created server or NULL if an error occured */ SERVER * server_alloc(char *servname, char *protocol, unsigned short port) { SERVER *server; if ((server = (SERVER *)malloc(sizeof(SERVER))) == NULL) return NULL; server->name = strdup(servname); server->protocol = strdup(protocol); server->port = port; memset(&server->stats, 0, sizeof(SERVER_STATS)); server->status = SERVER_RUNNING; server->nextdb = NULL; server->monuser = NULL; server->monpw = NULL; server->unique_name = NULL; server->server_string = NULL; server->node_id = -1; server->rlag = -1; server->node_ts = 0; spinlock_acquire(&server_spin); server->next = allServers; allServers = server; spinlock_release(&server_spin); return server; } /** * Deallocate the specified server * * @param server The service to deallocate * @return Returns true if the server was freed */ int server_free(SERVER *server) { SERVER *ptr; /* First of all remove from the linked list */ spinlock_acquire(&server_spin); if (allServers == server) { allServers = server->next; } else { ptr = allServers; while (ptr && ptr->next != server) { ptr = ptr->next; } if (ptr) ptr->next = server->next; } spinlock_release(&server_spin); /* Clean up session and free the memory */ free(server->name); free(server->protocol); if (server->unique_name) free(server->unique_name); if (server->server_string) free(server->server_string); free(server); return 1; } /** * Set a unique name for the server * * @param server The server to ste the name on * @param name The unique name for the server */ void server_set_unique_name(SERVER *server, char *name) { server->unique_name = strdup(name); } /** * Find an existing server using the unique section name in * configuration file * * @param servname The Server name or address * @param port The server port * @return The server or NULL if not found */ SERVER * server_find_by_unique_name(char *name) { SERVER *server; spinlock_acquire(&server_spin); server = allServers; while (server) { if (strcmp(server->unique_name, name) == 0) break; server = server->next; } spinlock_release(&server_spin); return server; } /** * Find an existing server * * @param servname The Server name or address * @param port The server port * @return The server or NULL if not found */ SERVER * server_find(char *servname, unsigned short port) { SERVER *server; spinlock_acquire(&server_spin); server = allServers; while (server) { if (strcmp(server->name, servname) == 0 && server->port == port) break; server = server->next; } spinlock_release(&server_spin); return server; } /** * Print details of an individual server * * @param server Server to print */ void printServer(SERVER *server) { printf("Server %p\n", server); printf("\tServer: %s\n", server->name); printf("\tProtocol: %s\n", server->protocol); printf("\tPort: %d\n", server->port); printf("\tTotal connections: %d\n", server->stats.n_connections); printf("\tCurrent connections: %d\n", server->stats.n_current); } /** * Print all servers * * Designed to be called within a debugger session in order * to display all active servers within the gateway */ void printAllServers() { SERVER *ptr; spinlock_acquire(&server_spin); ptr = allServers; while (ptr) { printServer(ptr); ptr = ptr->next; } spinlock_release(&server_spin); } /** * Print all servers to a DCB * * Designed to be called within a debugger session in order * to display all active servers within the gateway */ void dprintAllServers(DCB *dcb) { SERVER *ptr; char *stat; spinlock_acquire(&server_spin); ptr = allServers; while (ptr) { dcb_printf(dcb, "Server %p (%s)\n", ptr, ptr->unique_name); dcb_printf(dcb, "\tServer: %s\n", ptr->name); stat = server_status(ptr); dcb_printf(dcb, "\tStatus: %s\n", stat); free(stat); dcb_printf(dcb, "\tProtocol: %s\n", ptr->protocol); dcb_printf(dcb, "\tPort: %d\n", ptr->port); if (ptr->server_string) dcb_printf(dcb, "\tServer Version:\t\t%s\n", ptr->server_string); dcb_printf(dcb, "\tNode Id: %d\n", ptr->node_id); if (SERVER_IS_SLAVE(ptr)) { if (ptr->rlag >= 0) { dcb_printf(dcb, "\tSlave delay:\t\t%d\n", ptr->rlag); } } if (ptr->node_ts > 0) { dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", ptr->node_ts); } dcb_printf(dcb, "\tNumber of connections: %d\n", ptr->stats.n_connections); dcb_printf(dcb, "\tCurrent no. of conns: %d\n", ptr->stats.n_current); ptr = ptr->next; } spinlock_release(&server_spin); } /** * Print server details to a DCB * * Designed to be called within a debugger session in order * to display all active servers within the gateway */ void dprintServer(DCB *dcb, SERVER *server) { char *stat; dcb_printf(dcb, "Server %p (%s)\n", server, server->unique_name); dcb_printf(dcb, "\tServer: %s\n", server->name); stat = server_status(server); dcb_printf(dcb, "\tStatus: %s\n", stat); free(stat); dcb_printf(dcb, "\tProtocol: %s\n", server->protocol); dcb_printf(dcb, "\tPort: %d\n", server->port); if (server->server_string) dcb_printf(dcb, "\tServer Version:\t\t%s\n", server->server_string); dcb_printf(dcb, "\tNode Id: %d\n", server->node_id); if (SERVER_IS_SLAVE(server)) { if (server->rlag >= 0) { dcb_printf(dcb, "\tSlave delay:\t\t%d\n", server->rlag); } } if (server->node_ts > 0) { dcb_printf(dcb, "\tLast Repl Heartbeat:\t%lu\n", server->node_ts); } dcb_printf(dcb, "\tNumber of connections: %d\n", server->stats.n_connections); dcb_printf(dcb, "\tCurrent no. of conns: %d\n", server->stats.n_current); } /** * List all servers in a tabular form to a DCB * */ void dListServers(DCB *dcb) { SERVER *ptr; char *stat; spinlock_acquire(&server_spin); ptr = allServers; if (ptr) { dcb_printf(dcb, "%-18s | %-15s | Port | %-18s | Connections\n", "Server", "Address", "Status"); dcb_printf(dcb, "-------------------------------------------------------------------------------\n"); } while (ptr) { stat = server_status(ptr); dcb_printf(dcb, "%-18s | %-15s | %5d | %-18s | %4d\n", ptr->unique_name, ptr->name, ptr->port, stat, ptr->stats.n_current); free(stat); ptr = ptr->next; } spinlock_release(&server_spin); } /** * Convert a set of server status flags to a string, the returned * string has been malloc'd and must be free'd by the caller * * @param server The server to return the status of * @return A string representation of the status flags */ char * server_status(SERVER *server) { char *status = NULL; if ((status = (char *)malloc(200)) == NULL) return NULL; status[0] = 0; if (server->status & SERVER_MAINT) strcat(status, "Maintenance, "); if (server->status & SERVER_MASTER) strcat(status, "Master, "); if (server->status & SERVER_SLAVE) strcat(status, "Slave, "); if (server->status & SERVER_JOINED) strcat(status, "Synced, "); if (server->status & SERVER_RUNNING) strcat(status, "Running"); else strcat(status, "Down"); return status; } /** * Set a status bit in the server * * @param server The server to update * @param bit The bit to set for the server */ void server_set_status(SERVER *server, int bit) { server->status |= bit; } /** * Clear a status bit in the server * * @param server The server to update * @param bit The bit to clear for the server */ void server_clear_status(SERVER *server, int bit) { server->status &= ~bit; } /** * Add a user name and password to use for monitoring the * state of the server. * * @param server The server to update * @param user The user name to use * @param passwd The password of the user */ void serverAddMonUser(SERVER *server, char *user, char *passwd) { server->monuser = strdup(user); server->monpw = strdup(passwd); } /** * Check and update a server definition following a configuration * update. Changes will not affect any current connections to this * server, however all new connections will use the new settings. * * If the new settings are different from those already applied to the * server then a message will be written to the log. * * @param server The server to update * @param protocol The new protocol for the server * @param user The monitor user for the server * @param passwd The password to use for the monitor user */ void server_update(SERVER *server, char *protocol, char *user, char *passwd) { if (!strcmp(server->protocol, protocol)) { LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Update server protocol for server %s to protocol %s.", server->name, protocol))); free(server->protocol); server->protocol = strdup(protocol); } if (user != NULL && passwd != NULL) { if (strcmp(server->monuser, user) == 0 || strcmp(server->monpw, passwd) == 0) { LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, "Update server monitor credentials for server %s", server->name))); free(server->monuser); free(server->monpw); serverAddMonUser(server, user, passwd); } } }