Merge pull request #399 from kanoi/bflsc API Socket changes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
diff --git a/api.c b/api.c
index 2f169ed..96c087a 100644
--- a/api.c
+++ b/api.c
@@ -125,7 +125,6 @@ char *WSAErrorMsg(void) {
return &(WSAbuf[0]);
}
#endif
-static SOCKETTYPE sock = INVSOCK;
static const char *UNAVAILABLE = " - API will not be available";
static const char *INVAPIGROUPS = "Invalid --api-groups parameter";
@@ -3617,8 +3616,7 @@ static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, c
static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson)
{
char buf[SOCKBUFSIZ + sizeof(JSON_CLOSE) + sizeof(JSON_END)];
- int len;
- int n;
+ int count, res, tosend, len, n;
strcpy(buf, io_data->ptr);
@@ -3633,28 +3631,62 @@ static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson)
}
len = strlen(buf);
+ tosend = len+1;
- applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", len+1, buf, len > 10 ? "..." : BLANK);
+ applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", tosend, buf, len > 10 ? "..." : BLANK);
- // ignore failure - it's closed immediately anyway
- n = send(c, buf, len+1, 0);
+ count = 0;
+ while (count++ < 5 && tosend > 0) {
+ // allow 50ms per attempt
+ struct timeval timeout = {0, 50000};
+ fd_set wd;
+
+ FD_ZERO(&wd);
+ FD_SET(c, &wd);
+ if ((res = select(c + 1, NULL, &wd, NULL, &timeout)) < 1) {
+ applog(LOG_WARNING, "API: send select failed (%d)", res);
+ return;
+ }
- if (SOCKETFAIL(n))
- applog(LOG_WARNING, "API: send failed: %s", SOCKERRMSG);
- else
- applog(LOG_DEBUG, "API: sent %d", n);
+ n = send(c, buf, tosend, 0);
+
+ if (SOCKETFAIL(n)) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ continue;
+
+ applog(LOG_WARNING, "API: send (%d) failed: %s", tosend, SOCKERRMSG);
+
+ return;
+ } else {
+ if (count <= 1) {
+ if (n == tosend)
+ applog(LOG_DEBUG, "API: sent all of %d first go", tosend);
+ else
+ applog(LOG_DEBUG, "API: sent %d of %d first go", n, tosend);
+ } else {
+ if (n == tosend)
+ applog(LOG_DEBUG, "API: sent all of remaining %d (count=%d)", tosend, count);
+ else
+ applog(LOG_DEBUG, "API: sent %d of remaining %d (count=%d)", n, tosend, count);
+ }
+
+ tosend -= n;
+ }
+ }
}
static void tidyup(__maybe_unused void *arg)
{
mutex_lock(&quit_restart_lock);
+ SOCKETTYPE *apisock = (SOCKETTYPE *)arg;
+
bye = true;
- if (sock != INVSOCK) {
- shutdown(sock, SHUT_RDWR);
- CLOSESOCKET(sock);
- sock = INVSOCK;
+ if (*apisock != INVSOCK) {
+ shutdown(*apisock, SHUT_RDWR);
+ CLOSESOCKET(*apisock);
+ *apisock = INVSOCK;
}
if (ipaccess != NULL) {
@@ -3971,6 +4003,11 @@ void api(int api_thr_id)
bool did;
int i;
+ SOCKETTYPE *apisock;
+
+ apisock = malloc(sizeof(*apisock));
+ *apisock = INVSOCK;
+
if (!opt_api_listen) {
applog(LOG_DEBUG, "API not running%s", UNAVAILABLE);
return;
@@ -3980,7 +4017,7 @@ void api(int api_thr_id)
mutex_init(&quit_restart_lock);
- pthread_cleanup_push(tidyup, NULL);
+ pthread_cleanup_push(tidyup, (void *)apisock);
my_thr_id = api_thr_id;
setup_groups();
@@ -3998,8 +4035,8 @@ void api(int api_thr_id)
* to ensure curl has already called WSAStartup() in windows */
nmsleep(opt_log_interval*1000);
- sock = socket(AF_INET, SOCK_STREAM, 0);
- if (sock == INVSOCK) {
+ *apisock = socket(AF_INET, SOCK_STREAM, 0);
+ if (*apisock == INVSOCK) {
applog(LOG_ERR, "API1 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
return;
}
@@ -4024,7 +4061,7 @@ void api(int api_thr_id)
// another program has it open - which is what we want
int optval = 1;
// If it doesn't work, we don't really care - just show a debug message
- if (SOCKETFAIL(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval))))
+ if (SOCKETFAIL(setsockopt(*apisock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval))))
applog(LOG_DEBUG, "API setsockopt SO_REUSEADDR failed (ignored): %s", SOCKERRMSG);
#else
// On windows a 2nd program can bind to a port>1024 already in use unless
@@ -4036,7 +4073,7 @@ void api(int api_thr_id)
bound = 0;
bindstart = time(NULL);
while (bound == 0) {
- if (SOCKETFAIL(bind(sock, (struct sockaddr *)(&serv), sizeof(serv)))) {
+ if (SOCKETFAIL(bind(*apisock, (struct sockaddr *)(&serv), sizeof(serv)))) {
binderror = SOCKERRMSG;
if ((time(NULL) - bindstart) > 61)
break;
@@ -4053,25 +4090,25 @@ void api(int api_thr_id)
return;
}
- if (SOCKETFAIL(listen(sock, QUEUE))) {
+ if (SOCKETFAIL(listen(*apisock, QUEUE))) {
applog(LOG_ERR, "API3 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
- CLOSESOCKET(sock);
+ CLOSESOCKET(*apisock);
return;
}
if (opt_api_allow)
- applog(LOG_WARNING, "API running in IP access mode on port %d", port);
+ applog(LOG_WARNING, "API running in IP access mode on port %d (%d)", port, *apisock);
else {
if (opt_api_network)
- applog(LOG_WARNING, "API running in UNRESTRICTED read access mode on port %d", port);
+ applog(LOG_WARNING, "API running in UNRESTRICTED read access mode on port %d (%d)", port, *apisock);
else
- applog(LOG_WARNING, "API running in local read access mode on port %d", port);
+ applog(LOG_WARNING, "API running in local read access mode on port %d (%d)", port, *apisock);
}
while (!bye) {
clisiz = sizeof(cli);
- if (SOCKETFAIL(c = accept(sock, (struct sockaddr *)(&cli), &clisiz))) {
- applog(LOG_ERR, "API failed (%s)%s", SOCKERRMSG, UNAVAILABLE);
+ if (SOCKETFAIL(c = accept(*apisock, (struct sockaddr *)(&cli), &clisiz))) {
+ applog(LOG_ERR, "API failed (%s)%s (%d)", SOCKERRMSG, UNAVAILABLE, *apisock);
goto die;
}