diff --git a/includes/protocol.h b/includes/protocol.h index a528d3bd..c1fc3f90 100644 --- a/includes/protocol.h +++ b/includes/protocol.h @@ -39,6 +39,16 @@ extern "C" { #define RENDER_PORT 7654 #define XMLCONFIG_MAX 41 +/* TCP keepalive configuration */ +struct keepalive_settings { + int enabled; + int time; + int interval; + int probes; +}; + +extern struct keepalive_settings keepalives; + enum protoCmd { cmdIgnore, cmdRender, cmdDirty, cmdDone, cmdNotDone, cmdRenderPrio, cmdRenderBulk, cmdRenderLow }; struct protocol { diff --git a/src/render_expired.c b/src/render_expired.c index a606229c..4d1326a7 100644 --- a/src/render_expired.c +++ b/src/render_expired.c @@ -61,6 +61,8 @@ int main(int argc, char **argv) } #else +struct keepalive_settings keepalives; + // tile marking arrays unsigned int **tile_requested; @@ -109,6 +111,8 @@ int main(int argc, char **argv) struct storage_backend * store; char name[PATH_MAX]; + memset(&keepalives, 0, sizeof(struct keepalive_settings)); + // excess_zoomlevels is how many zoom levels at the large end // we can ignore because their tiles will share one meta tile. // with the default METATILE==8 this is 3. diff --git a/src/render_list.c b/src/render_list.c index e87b9a4e..768aa157 100644 --- a/src/render_list.c +++ b/src/render_list.c @@ -52,6 +52,8 @@ int main(int argc, char **argv) } #else +struct keepalive_settings keepalives; + static int minZoom = 0; static int maxZoom = MAX_ZOOM; static int verbose = 0; @@ -92,6 +94,8 @@ int main(int argc, char **argv) struct storage_backend * store; struct stat_info s; + memset(&keepalives, 0, sizeof(struct keepalive_settings)); + while (1) { int option_index = 0; static struct option long_options[] = { @@ -102,6 +106,8 @@ int main(int argc, char **argv) {"min-y", required_argument, 0, 'y'}, {"max-y", required_argument, 0, 'Y'}, {"socket", required_argument, 0, 's'}, + {"keepalives", no_argument, 0, 'k'}, + {"keepalive-config", required_argument, 0, 'K'}, {"num-threads", required_argument, 0, 'n'}, {"max-load", required_argument, 0, 'l'}, {"tile-dir", required_argument, 0, 't'}, @@ -113,7 +119,7 @@ int main(int argc, char **argv) {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "hvaz:Z:x:X:y:Y:s:m:t:n:l:f", long_options, &option_index); + c = getopt_long(argc, argv, "hvaz:Z:x:X:y:Y:s:kK:m:t:n:l:f", long_options, &option_index); if (c == -1) { break; @@ -129,6 +135,54 @@ int main(int argc, char **argv) spath = strdup(optarg); break; + case 'k': /* -k, --keepalives */ + keepalives.enabled = 1; + break; + + case 'K': { /* --keepalive-config=:: */ + char * confstr = strdup(optarg); + if (!strlen(confstr)) { + fprintf(stderr, "No parameters provided for the TCP keepalive config\n"); + return 1; + } + + int val_count = 0; + const int val_count_expected = 3; + char * val[val_count_expected]; + char * p = strtok(confstr, ":"); + while (p != NULL) { + val[ val_count++ ] = p; + p = strtok(NULL, ":"); + } + + if (val_count != val_count_expected) { + fprintf(stderr, "Must provide exactly %d instead of %d arguments to --kepalive-config. See help for details.\n", val_count_expected, val_count); + return 1; + } + + keepalives.enabled = 1; + char * error_char = NULL; + keepalives.time = strtol(val[0], &error_char, 10); + if (*error_char != '\0') { + fprintf(stderr, "TCP keepalive time contains invalid character\n"); + return 1; + } + + keepalives.interval = strtol(val[1], &error_char, 10); + if (*error_char != '\0') { + fprintf(stderr, "TCP keepalive interval contains invalid character\n"); + return 1; + } + + keepalives.probes = strtol(val[2], &error_char, 10); + if (*error_char != '\0') { + fprintf(stderr, "TCP keepalive probe count contains invalid character\n"); + return 1; + } + + break; + } + case 't': /* -t, --tile-dir */ tile_dir = strdup(optarg); break; @@ -202,6 +256,8 @@ int main(int argc, char **argv) fprintf(stderr, " -m, --map=MAP render tiles in this map (defaults to '" XMLCONFIG_DEFAULT "')\n"); fprintf(stderr, " -l, --max-load=LOAD sleep if load is this high (defaults to %d)\n", MAX_LOAD_OLD); fprintf(stderr, " -s, --socket=SOCKET|HOSTNAME:PORT unix domain socket name or hostname and port for contacting renderd\n"); + fprintf(stderr, " -k, --keepalives enable TCP keepalives\n"); + fprintf(stderr, " -K, --keepalive-config=:: TCP keepalive configuration. Send keepalives after t seconds of inactivity, every intvl seconds. Consider connection broken after count probes. Implicitly enables TCP keepalives.\n"); fprintf(stderr, " -n, --num-threads=N the number of parallel request threads (default 1)\n"); fprintf(stderr, " -t, --tile-dir tile cache directory (defaults to '" HASH_PATH "')\n"); fprintf(stderr, " -z, --min-zoom=ZOOM filter input to only render tiles greater or equal to this zoom level (default is 0)\n"); diff --git a/src/render_old.c b/src/render_old.c index 900a5a39..8559a7e1 100644 --- a/src/render_old.c +++ b/src/render_old.c @@ -65,7 +65,7 @@ static struct timeval start, end; int foreground = 1; - +struct keepalive_settings keepalives; void display_rate(struct timeval start, struct timeval end, int num) { @@ -199,6 +199,8 @@ int main(int argc, char **argv) int dd, mm, yy; struct tm tm; + memset(&keepalives, 0, sizeof(struct keepalive_settings)); + while (1) { int option_index = 0; static struct option long_options[] = { diff --git a/src/render_submit_queue.c b/src/render_submit_queue.c index ff9dde55..c6c7c441 100644 --- a/src/render_submit_queue.c +++ b/src/render_submit_queue.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -306,10 +307,52 @@ int make_connection(const char *spath) continue; } + if (keepalives.enabled) { + fprintf(stderr, "Enabling TCP keepalives\n"); + if (keepalives.time > 0) { + fprintf(stderr, "TCP keepalives configuration: time=%d, interval=%d, probes=%d\n", + keepalives.time, + keepalives.interval, + keepalives.probes + ); + } + int optval = 0; + socklen_t optlen = sizeof(optval); + + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) < 0) { + perror("setsockopt()"); + close(fd); + exit(2); + } + +#ifdef LINUX + if (keepalives.time > 0 && setsockopt(fd, SOL_SOCKET, TCP_KEEPIDLE, &keepalives.time, sizeof(keepalives.time)) < 0) { + perror("setsockopt()"); + close(fd); + exit(2); + } + + if (keepalives.interval > 0 && setsockopt(fd, SOL_SOCKET, TCP_KEEPINTVL, &keepalives.interval, sizeof(keepalives.interval)) < 0) { + perror("setsockopt()"); + close(fd); + exit(2); + } + + if (keepalives.probes > 0 && setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &keepalives.probes, sizeof(keepalives.probes)) < 0) { + perror("setsockopt()"); + close(fd); + exit(2); + } +#endif + + } + char resolved_addr[NI_MAXHOST]; char resolved_port[NI_MAXSERV]; int name_info = getnameinfo(rp->ai_addr, rp->ai_addrlen, resolved_addr, sizeof(resolved_addr), resolved_port, sizeof(resolved_port), NI_NUMERICHOST | NI_NUMERICSERV); if (name_info != 0) { + close(fd); fprintf(stderr, "cannot retrieve name info: %d\n", name_info); exit(2); } diff --git a/src/speedtest.cpp b/src/speedtest.cpp index b9678a35..3a782f28 100644 --- a/src/speedtest.cpp +++ b/src/speedtest.cpp @@ -49,6 +49,8 @@ int main(int argc, char **argv) } #else +struct keepalive_settings keepalives; + static const int minZoom = 0; static const int maxZoom = 18; @@ -208,6 +210,8 @@ int main(int argc, char **argv) int verbose = 0; int numThreads = 1; + memset(&keepalives, 0, sizeof(struct keepalive_settings)); + while (1) { int option_index = 0; static struct option long_options[] = {