Commit | Line | Data |
---|---|---|
50d16976 JA |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
142575e6 | 3 | #include <stdarg.h> |
50d16976 JA |
4 | #include <unistd.h> |
5 | #include <limits.h> | |
50d16976 JA |
6 | #include <errno.h> |
7 | #include <fcntl.h> | |
8 | #include <sys/poll.h> | |
50d16976 JA |
9 | #include <sys/types.h> |
10 | #include <sys/wait.h> | |
d05c4a03 | 11 | #include <sys/socket.h> |
87aa8f19 JA |
12 | #include <sys/stat.h> |
13 | #include <sys/un.h> | |
50d16976 JA |
14 | #include <netinet/in.h> |
15 | #include <arpa/inet.h> | |
16 | #include <netdb.h> | |
e46d8091 | 17 | #include <syslog.h> |
9e22ecb0 | 18 | #include <signal.h> |
50d16976 JA |
19 | |
20 | #include "fio.h" | |
132159a5 | 21 | #include "server.h" |
fcee5ff6 | 22 | #include "crc/crc16.h" |
802ad4a8 | 23 | #include "ieee754.h" |
89cf1480 JA |
24 | |
25 | #include "fio_version.h" | |
50d16976 | 26 | |
132159a5 | 27 | int fio_net_port = 8765; |
50d16976 | 28 | |
009b1be4 JA |
29 | int exit_backend = 0; |
30 | ||
46c48f1f | 31 | static int server_fd = -1; |
87aa8f19 JA |
32 | static char *fio_server_arg; |
33 | static char *bind_sock; | |
34 | static struct sockaddr_in saddr_in; | |
37db14fe | 35 | |
132159a5 JA |
36 | int fio_send_data(int sk, const void *p, unsigned int len) |
37 | { | |
794d69ca JA |
38 | assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_PDU); |
39 | ||
132159a5 JA |
40 | do { |
41 | int ret = send(sk, p, len, 0); | |
42 | ||
43 | if (ret > 0) { | |
44 | len -= ret; | |
45 | if (!len) | |
46 | break; | |
47 | p += ret; | |
48 | continue; | |
49 | } else if (!ret) | |
50 | break; | |
51 | else if (errno == EAGAIN || errno == EINTR) | |
52 | continue; | |
53 | } while (!exit_backend); | |
54 | ||
55 | if (!len) | |
56 | return 0; | |
57 | ||
58 | return 1; | |
59 | } | |
60 | ||
61 | int fio_recv_data(int sk, void *p, unsigned int len) | |
62 | { | |
63 | do { | |
64 | int ret = recv(sk, p, len, MSG_WAITALL); | |
65 | ||
66 | if (ret > 0) { | |
67 | len -= ret; | |
68 | if (!len) | |
69 | break; | |
70 | p += ret; | |
71 | continue; | |
72 | } else if (!ret) | |
73 | break; | |
74 | else if (errno == EAGAIN || errno == EINTR) | |
75 | continue; | |
76 | } while (!exit_backend); | |
77 | ||
78 | if (!len) | |
79 | return 0; | |
80 | ||
81 | return -1; | |
82 | } | |
83 | ||
84 | static int verify_convert_cmd(struct fio_net_cmd *cmd) | |
85 | { | |
fcee5ff6 | 86 | uint16_t crc; |
132159a5 | 87 | |
fcee5ff6 JA |
88 | cmd->cmd_crc16 = le16_to_cpu(cmd->cmd_crc16); |
89 | cmd->pdu_crc16 = le16_to_cpu(cmd->pdu_crc16); | |
132159a5 | 90 | |
fcee5ff6 JA |
91 | crc = crc16(cmd, FIO_NET_CMD_CRC_SZ); |
92 | if (crc != cmd->cmd_crc16) { | |
132159a5 | 93 | log_err("fio: server bad crc on command (got %x, wanted %x)\n", |
fcee5ff6 | 94 | cmd->cmd_crc16, crc); |
132159a5 JA |
95 | return 1; |
96 | } | |
97 | ||
98 | cmd->version = le16_to_cpu(cmd->version); | |
99 | cmd->opcode = le16_to_cpu(cmd->opcode); | |
100 | cmd->flags = le32_to_cpu(cmd->flags); | |
af9c9fb3 | 101 | cmd->tag = le64_to_cpu(cmd->tag); |
132159a5 JA |
102 | cmd->pdu_len = le32_to_cpu(cmd->pdu_len); |
103 | ||
104 | switch (cmd->version) { | |
fa2ea806 | 105 | case FIO_SERVER_VER: |
132159a5 JA |
106 | break; |
107 | default: | |
108 | log_err("fio: bad server cmd version %d\n", cmd->version); | |
109 | return 1; | |
110 | } | |
111 | ||
112 | if (cmd->pdu_len > FIO_SERVER_MAX_PDU) { | |
113 | log_err("fio: command payload too large: %u\n", cmd->pdu_len); | |
114 | return 1; | |
115 | } | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
a64e88da JA |
120 | /* |
121 | * Read (and defragment, if necessary) incoming commands | |
122 | */ | |
e951bdc4 | 123 | struct fio_net_cmd *fio_net_recv_cmd(int sk) |
132159a5 | 124 | { |
a64e88da JA |
125 | struct fio_net_cmd cmd, *cmdret = NULL; |
126 | size_t cmd_size = 0, pdu_offset = 0; | |
fcee5ff6 | 127 | uint16_t crc; |
a64e88da JA |
128 | int ret, first = 1; |
129 | void *pdu = NULL; | |
132159a5 | 130 | |
a64e88da JA |
131 | do { |
132 | ret = fio_recv_data(sk, &cmd, sizeof(cmd)); | |
133 | if (ret) | |
134 | break; | |
132159a5 | 135 | |
a64e88da JA |
136 | /* We have a command, verify it and swap if need be */ |
137 | ret = verify_convert_cmd(&cmd); | |
138 | if (ret) | |
139 | break; | |
132159a5 | 140 | |
0b8f30a5 JA |
141 | if (first) { |
142 | /* if this is text, add room for \0 at the end */ | |
143 | cmd_size = sizeof(cmd) + cmd.pdu_len + 1; | |
144 | assert(!cmdret); | |
145 | } else | |
a64e88da | 146 | cmd_size += cmd.pdu_len; |
132159a5 | 147 | |
a64e88da | 148 | cmdret = realloc(cmdret, cmd_size); |
132159a5 | 149 | |
a64e88da JA |
150 | if (first) |
151 | memcpy(cmdret, &cmd, sizeof(cmd)); | |
152 | else | |
153 | assert(cmdret->opcode == cmd.opcode); | |
154 | ||
155 | if (!cmd.pdu_len) | |
156 | break; | |
157 | ||
158 | /* There's payload, get it */ | |
159 | pdu = (void *) cmdret->payload + pdu_offset; | |
160 | ret = fio_recv_data(sk, pdu, cmd.pdu_len); | |
161 | if (ret) | |
162 | break; | |
163 | ||
164 | /* Verify payload crc */ | |
165 | crc = crc16(pdu, cmd.pdu_len); | |
166 | if (crc != cmd.pdu_crc16) { | |
167 | log_err("fio: server bad crc on payload "); | |
168 | log_err("(got %x, wanted %x)\n", cmd.pdu_crc16, crc); | |
169 | ret = 1; | |
170 | break; | |
171 | } | |
172 | ||
173 | pdu_offset += cmd.pdu_len; | |
817f06bb JA |
174 | if (!first) |
175 | cmdret->pdu_len += cmd.pdu_len; | |
a64e88da JA |
176 | first = 0; |
177 | } while (cmd.flags & FIO_NET_CMD_F_MORE); | |
132159a5 | 178 | |
a64e88da JA |
179 | if (ret) { |
180 | free(cmdret); | |
181 | cmdret = NULL; | |
0b8f30a5 JA |
182 | } else if (cmdret) { |
183 | /* zero-terminate text input */ | |
184 | if (cmdret->pdu_len && (cmdret->opcode == FIO_NET_CMD_TEXT || | |
185 | cmdret->opcode == FIO_NET_CMD_JOB)) { | |
186 | char *buf = (char *) cmdret->payload; | |
187 | ||
188 | buf[cmdret->pdu_len ] = '\0'; | |
189 | } | |
190 | /* frag flag is internal */ | |
a64e88da | 191 | cmdret->flags &= ~FIO_NET_CMD_F_MORE; |
0b8f30a5 | 192 | } |
a64e88da JA |
193 | |
194 | return cmdret; | |
132159a5 JA |
195 | } |
196 | ||
197 | void fio_net_cmd_crc(struct fio_net_cmd *cmd) | |
198 | { | |
199 | uint32_t pdu_len; | |
200 | ||
ddcc0b69 | 201 | cmd->cmd_crc16 = __cpu_to_le16(crc16(cmd, FIO_NET_CMD_CRC_SZ)); |
132159a5 JA |
202 | |
203 | pdu_len = le32_to_cpu(cmd->pdu_len); | |
204 | if (pdu_len) | |
ddcc0b69 | 205 | cmd->pdu_crc16 = __cpu_to_le16(crc16(cmd->payload, pdu_len)); |
132159a5 JA |
206 | } |
207 | ||
af9c9fb3 JA |
208 | int fio_net_send_cmd(int fd, uint16_t opcode, const void *buf, off_t size, |
209 | uint64_t tag) | |
794d69ca | 210 | { |
7f868316 JA |
211 | struct fio_net_cmd *cmd = NULL; |
212 | size_t this_len, cur_len = 0; | |
794d69ca JA |
213 | int ret; |
214 | ||
215 | do { | |
216 | this_len = size; | |
217 | if (this_len > FIO_SERVER_MAX_PDU) | |
218 | this_len = FIO_SERVER_MAX_PDU; | |
219 | ||
7f868316 JA |
220 | if (!cmd || cur_len < sizeof(*cmd) + this_len) { |
221 | if (cmd) | |
222 | free(cmd); | |
223 | ||
224 | cur_len = sizeof(*cmd) + this_len; | |
225 | cmd = malloc(cur_len); | |
226 | } | |
794d69ca | 227 | |
af9c9fb3 | 228 | fio_init_net_cmd(cmd, opcode, buf, this_len, tag); |
794d69ca JA |
229 | |
230 | if (this_len < size) | |
ddcc0b69 | 231 | cmd->flags = __cpu_to_le32(FIO_NET_CMD_F_MORE); |
794d69ca JA |
232 | |
233 | fio_net_cmd_crc(cmd); | |
234 | ||
235 | ret = fio_send_data(fd, cmd, sizeof(*cmd) + this_len); | |
794d69ca JA |
236 | size -= this_len; |
237 | buf += this_len; | |
238 | } while (!ret && size); | |
239 | ||
7f868316 JA |
240 | if (cmd) |
241 | free(cmd); | |
242 | ||
794d69ca JA |
243 | return ret; |
244 | } | |
245 | ||
af9c9fb3 | 246 | int fio_net_send_simple_cmd(int sk, uint16_t opcode, uint64_t tag) |
132159a5 | 247 | { |
178cde9f | 248 | struct fio_net_cmd cmd; |
132159a5 | 249 | |
af9c9fb3 | 250 | fio_init_net_cmd(&cmd, opcode, NULL, 0, tag); |
132159a5 JA |
251 | fio_net_cmd_crc(&cmd); |
252 | ||
253 | return fio_send_data(sk, &cmd, sizeof(cmd)); | |
254 | } | |
255 | ||
9abea48b | 256 | static int fio_server_send_quit_cmd(void) |
437377e1 | 257 | { |
46c48f1f | 258 | dprint(FD_NET, "server: sending quit\n"); |
cc0df00a | 259 | return fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_QUIT, 0); |
437377e1 JA |
260 | } |
261 | ||
0b8f30a5 | 262 | static int handle_job_cmd(struct fio_net_cmd *cmd) |
132159a5 | 263 | { |
0b8f30a5 | 264 | char *buf = (char *) cmd->payload; |
a64e88da | 265 | int ret; |
132159a5 | 266 | |
e6d1c668 JA |
267 | if (parse_jobs_ini(buf, 1, 0)) { |
268 | fio_server_send_quit_cmd(); | |
81179eec | 269 | return -1; |
e6d1c668 | 270 | } |
81179eec JA |
271 | |
272 | fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0); | |
273 | ||
274 | ret = exec_run(); | |
9abea48b | 275 | fio_server_send_quit_cmd(); |
81179eec JA |
276 | reset_fio_state(); |
277 | return ret; | |
278 | } | |
279 | ||
280 | static int handle_jobline_cmd(struct fio_net_cmd *cmd) | |
281 | { | |
fa2ea806 JA |
282 | void *pdu = cmd->payload; |
283 | struct cmd_single_line_pdu *cslp; | |
284 | struct cmd_line_pdu *clp; | |
285 | unsigned long offset; | |
286 | char **argv; | |
81179eec JA |
287 | int ret, i; |
288 | ||
fa2ea806 JA |
289 | clp = pdu; |
290 | clp->lines = le16_to_cpu(clp->lines); | |
291 | argv = malloc(clp->lines * sizeof(char *)); | |
292 | offset = sizeof(*clp); | |
81179eec | 293 | |
fa2ea806 | 294 | dprint(FD_NET, "server: %d command line args\n", clp->lines); |
39e8e016 | 295 | |
fa2ea806 JA |
296 | for (i = 0; i < clp->lines; i++) { |
297 | cslp = pdu + offset; | |
298 | argv[i] = (char *) cslp->text; | |
299 | ||
300 | offset += sizeof(*cslp) + le16_to_cpu(cslp->len); | |
39e8e016 JA |
301 | dprint(FD_NET, "server: %d: %s\n", i, argv[i]); |
302 | } | |
81179eec | 303 | |
fa2ea806 | 304 | if (parse_cmd_line(clp->lines, argv)) { |
e6d1c668 | 305 | fio_server_send_quit_cmd(); |
fa2ea806 | 306 | free(argv); |
81179eec | 307 | return -1; |
e6d1c668 | 308 | } |
81179eec | 309 | |
fa2ea806 JA |
310 | free(argv); |
311 | ||
81179eec JA |
312 | fio_net_send_simple_cmd(server_fd, FIO_NET_CMD_START, 0); |
313 | ||
794d69ca | 314 | ret = exec_run(); |
9abea48b | 315 | fio_server_send_quit_cmd(); |
794d69ca | 316 | reset_fio_state(); |
132159a5 JA |
317 | return ret; |
318 | } | |
319 | ||
c28e8e8c JA |
320 | static int handle_probe_cmd(struct fio_net_cmd *cmd) |
321 | { | |
322 | struct cmd_probe_pdu probe; | |
323 | ||
324 | memset(&probe, 0, sizeof(probe)); | |
325 | gethostname((char *) probe.hostname, sizeof(probe.hostname)); | |
6eb24791 JA |
326 | #ifdef FIO_BIG_ENDIAN |
327 | probe.bigendian = 1; | |
328 | #endif | |
81179eec JA |
329 | probe.fio_major = FIO_MAJOR; |
330 | probe.fio_minor = FIO_MINOR; | |
331 | probe.fio_patch = FIO_PATCH; | |
c28e8e8c | 332 | |
cca84643 JA |
333 | probe.os = FIO_OS; |
334 | probe.arch = FIO_ARCH; | |
335 | ||
af9c9fb3 JA |
336 | return fio_net_send_cmd(server_fd, FIO_NET_CMD_PROBE, &probe, sizeof(probe), 0); |
337 | } | |
338 | ||
339 | static int handle_send_eta_cmd(struct fio_net_cmd *cmd) | |
340 | { | |
341 | struct jobs_eta *je; | |
342 | size_t size; | |
af9c9fb3 JA |
343 | int i; |
344 | ||
345 | size = sizeof(*je) + thread_number * sizeof(char); | |
7f868316 JA |
346 | je = malloc(size); |
347 | memset(je, 0, size); | |
af9c9fb3 JA |
348 | |
349 | if (!calc_thread_status(je, 1)) { | |
350 | free(je); | |
351 | return 0; | |
352 | } | |
353 | ||
354 | dprint(FD_NET, "server sending status\n"); | |
355 | ||
356 | je->nr_running = cpu_to_le32(je->nr_running); | |
357 | je->nr_ramp = cpu_to_le32(je->nr_ramp); | |
358 | je->nr_pending = cpu_to_le32(je->nr_pending); | |
359 | je->files_open = cpu_to_le32(je->files_open); | |
360 | je->m_rate = cpu_to_le32(je->m_rate); | |
361 | je->t_rate = cpu_to_le32(je->t_rate); | |
362 | je->m_iops = cpu_to_le32(je->m_iops); | |
363 | je->t_iops = cpu_to_le32(je->t_iops); | |
364 | ||
365 | for (i = 0; i < 2; i++) { | |
366 | je->rate[i] = cpu_to_le32(je->rate[i]); | |
367 | je->iops[i] = cpu_to_le32(je->iops[i]); | |
368 | } | |
369 | ||
370 | je->elapsed_sec = cpu_to_le32(je->nr_running); | |
371 | je->eta_sec = cpu_to_le64(je->eta_sec); | |
372 | ||
7f868316 | 373 | fio_net_send_cmd(server_fd, FIO_NET_CMD_ETA, je, size, cmd->tag); |
af9c9fb3 JA |
374 | free(je); |
375 | return 0; | |
c28e8e8c JA |
376 | } |
377 | ||
132159a5 JA |
378 | static int handle_command(struct fio_net_cmd *cmd) |
379 | { | |
380 | int ret; | |
381 | ||
7f868316 | 382 | dprint(FD_NET, "server: got opcode %d, pdu=%u\n", cmd->opcode, cmd->pdu_len); |
46c48f1f | 383 | |
132159a5 JA |
384 | switch (cmd->opcode) { |
385 | case FIO_NET_CMD_QUIT: | |
cc0df00a | 386 | fio_terminate_threads(TERMINATE_ALL); |
c28e8e8c | 387 | return -1; |
d7959186 | 388 | case FIO_NET_CMD_EXIT: |
132159a5 | 389 | exit_backend = 1; |
c28e8e8c | 390 | return -1; |
132159a5 | 391 | case FIO_NET_CMD_JOB: |
0b8f30a5 | 392 | ret = handle_job_cmd(cmd); |
132159a5 | 393 | break; |
81179eec JA |
394 | case FIO_NET_CMD_JOBLINE: |
395 | ret = handle_jobline_cmd(cmd); | |
396 | break; | |
c28e8e8c JA |
397 | case FIO_NET_CMD_PROBE: |
398 | ret = handle_probe_cmd(cmd); | |
399 | break; | |
af9c9fb3 JA |
400 | case FIO_NET_CMD_SEND_ETA: |
401 | ret = handle_send_eta_cmd(cmd); | |
402 | break; | |
132159a5 JA |
403 | default: |
404 | log_err("fio: unknown opcode: %d\n", cmd->opcode); | |
405 | ret = 1; | |
406 | } | |
407 | ||
408 | return ret; | |
409 | } | |
410 | ||
70e0c316 | 411 | static int handle_connection(int sk, int block) |
132159a5 JA |
412 | { |
413 | struct fio_net_cmd *cmd = NULL; | |
414 | int ret = 0; | |
415 | ||
416 | /* read forever */ | |
417 | while (!exit_backend) { | |
e951bdc4 JA |
418 | struct pollfd pfd = { |
419 | .fd = sk, | |
420 | .events = POLLIN, | |
421 | }; | |
422 | ||
423 | ret = 0; | |
424 | do { | |
425 | ret = poll(&pfd, 1, 100); | |
426 | if (ret < 0) { | |
427 | if (errno == EINTR) | |
428 | break; | |
429 | log_err("fio: poll: %s\n", strerror(errno)); | |
430 | break; | |
19c65179 JA |
431 | } else if (!ret) { |
432 | if (!block) | |
433 | return 0; | |
e951bdc4 | 434 | continue; |
19c65179 | 435 | } |
e951bdc4 JA |
436 | |
437 | if (pfd.revents & POLLIN) | |
438 | break; | |
439 | if (pfd.revents & (POLLERR|POLLHUP)) { | |
440 | ret = 1; | |
441 | break; | |
442 | } | |
19c65179 | 443 | } while (!exit_backend); |
e951bdc4 JA |
444 | |
445 | if (ret < 0) | |
446 | break; | |
447 | ||
448 | cmd = fio_net_recv_cmd(sk); | |
132159a5 | 449 | if (!cmd) { |
c28e8e8c | 450 | ret = -1; |
132159a5 JA |
451 | break; |
452 | } | |
453 | ||
132159a5 JA |
454 | ret = handle_command(cmd); |
455 | if (ret) | |
456 | break; | |
457 | ||
458 | free(cmd); | |
c77a99e7 | 459 | cmd = NULL; |
132159a5 JA |
460 | } |
461 | ||
462 | if (cmd) | |
463 | free(cmd); | |
464 | ||
465 | return ret; | |
466 | } | |
467 | ||
cc0df00a JA |
468 | void fio_server_idle_loop(void) |
469 | { | |
470 | if (server_fd != -1) | |
70e0c316 | 471 | handle_connection(server_fd, 0); |
cc0df00a JA |
472 | } |
473 | ||
50d16976 JA |
474 | static int accept_loop(int listen_sk) |
475 | { | |
bb447a27 | 476 | struct sockaddr_in addr; |
5ba13ea6 | 477 | fio_socklen_t len = sizeof(addr); |
009b1be4 | 478 | struct pollfd pfd; |
132159a5 | 479 | int ret, sk, flags, exitval = 0; |
50d16976 | 480 | |
60efd14e JA |
481 | dprint(FD_NET, "server enter accept loop\n"); |
482 | ||
009b1be4 JA |
483 | flags = fcntl(listen_sk, F_GETFL); |
484 | flags |= O_NONBLOCK; | |
485 | fcntl(listen_sk, F_SETFL, flags); | |
50d16976 | 486 | again: |
009b1be4 JA |
487 | pfd.fd = listen_sk; |
488 | pfd.events = POLLIN; | |
489 | do { | |
490 | ret = poll(&pfd, 1, 100); | |
491 | if (ret < 0) { | |
492 | if (errno == EINTR) | |
493 | break; | |
fcee5ff6 | 494 | log_err("fio: poll: %s\n", strerror(errno)); |
009b1be4 JA |
495 | goto out; |
496 | } else if (!ret) | |
497 | continue; | |
498 | ||
499 | if (pfd.revents & POLLIN) | |
500 | break; | |
501 | } while (!exit_backend); | |
502 | ||
503 | if (exit_backend) | |
504 | goto out; | |
505 | ||
6b976bfc | 506 | sk = accept(listen_sk, (struct sockaddr *) &addr, &len); |
50d16976 | 507 | if (sk < 0) { |
690e09ae | 508 | log_err("fio: accept: %s\n", strerror(errno)); |
50d16976 JA |
509 | return -1; |
510 | } | |
511 | ||
bb447a27 | 512 | dprint(FD_NET, "server: connect from %s\n", inet_ntoa(addr.sin_addr)); |
46c48f1f | 513 | |
37db14fe JA |
514 | server_fd = sk; |
515 | ||
70e0c316 | 516 | exitval = handle_connection(sk, 1); |
50d16976 | 517 | |
37db14fe | 518 | server_fd = -1; |
50d16976 | 519 | close(sk); |
5c341e9a | 520 | |
009b1be4 | 521 | if (!exit_backend) |
5c341e9a JA |
522 | goto again; |
523 | ||
009b1be4 | 524 | out: |
132159a5 | 525 | return exitval; |
50d16976 JA |
526 | } |
527 | ||
142575e6 | 528 | int fio_server_text_output(const char *buf, unsigned int len) |
37db14fe | 529 | { |
337d75a8 | 530 | if (server_fd != -1) |
af9c9fb3 | 531 | return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len, 0); |
337d75a8 | 532 | |
5037b845 | 533 | return fwrite(buf, len, 1, f_err); |
142575e6 JA |
534 | } |
535 | ||
a64e88da JA |
536 | static void convert_io_stat(struct io_stat *dst, struct io_stat *src) |
537 | { | |
538 | dst->max_val = cpu_to_le64(src->max_val); | |
539 | dst->min_val = cpu_to_le64(src->min_val); | |
540 | dst->samples = cpu_to_le64(src->samples); | |
802ad4a8 JA |
541 | |
542 | /* | |
543 | * Encode to IEEE 754 for network transfer | |
544 | */ | |
545 | dst->mean.u.i = __cpu_to_le64(fio_double_to_uint64(src->mean.u.f)); | |
546 | dst->S.u.i = __cpu_to_le64(fio_double_to_uint64(src->S.u.f)); | |
a64e88da JA |
547 | } |
548 | ||
549 | static void convert_gs(struct group_run_stats *dst, struct group_run_stats *src) | |
550 | { | |
551 | int i; | |
552 | ||
553 | for (i = 0; i < 2; i++) { | |
554 | dst->max_run[i] = cpu_to_le64(src->max_run[i]); | |
555 | dst->min_run[i] = cpu_to_le64(src->min_run[i]); | |
556 | dst->max_bw[i] = cpu_to_le64(src->max_bw[i]); | |
557 | dst->min_bw[i] = cpu_to_le64(src->min_bw[i]); | |
558 | dst->io_kb[i] = cpu_to_le64(src->io_kb[i]); | |
559 | dst->agg[i] = cpu_to_le64(src->agg[i]); | |
560 | } | |
561 | ||
562 | dst->kb_base = cpu_to_le32(src->kb_base); | |
563 | dst->groupid = cpu_to_le32(src->groupid); | |
564 | } | |
565 | ||
566 | /* | |
567 | * Send a CMD_TS, which packs struct thread_stat and group_run_stats | |
568 | * into a single payload. | |
569 | */ | |
570 | void fio_server_send_ts(struct thread_stat *ts, struct group_run_stats *rs) | |
571 | { | |
572 | struct cmd_ts_pdu p; | |
573 | int i, j; | |
574 | ||
60efd14e JA |
575 | dprint(FD_NET, "server sending end stats\n"); |
576 | ||
317b3c8b JA |
577 | memset(&p, 0, sizeof(p)); |
578 | ||
a64e88da JA |
579 | strcpy(p.ts.name, ts->name); |
580 | strcpy(p.ts.verror, ts->verror); | |
581 | strcpy(p.ts.description, ts->description); | |
582 | ||
ddcc0b69 | 583 | p.ts.error = cpu_to_le32(ts->error); |
a64e88da | 584 | p.ts.groupid = cpu_to_le32(ts->groupid); |
ddcc0b69 | 585 | p.ts.pid = cpu_to_le32(ts->pid); |
a64e88da JA |
586 | p.ts.members = cpu_to_le32(ts->members); |
587 | ||
588 | for (i = 0; i < 2; i++) { | |
589 | convert_io_stat(&p.ts.clat_stat[i], &ts->clat_stat[i]); | |
590 | convert_io_stat(&p.ts.slat_stat[i], &ts->slat_stat[i]); | |
591 | convert_io_stat(&p.ts.lat_stat[i], &ts->lat_stat[i]); | |
592 | convert_io_stat(&p.ts.bw_stat[i], &ts->bw_stat[i]); | |
593 | } | |
594 | ||
595 | p.ts.usr_time = cpu_to_le64(ts->usr_time); | |
596 | p.ts.sys_time = cpu_to_le64(ts->sys_time); | |
597 | p.ts.ctx = cpu_to_le64(ts->ctx); | |
598 | p.ts.minf = cpu_to_le64(ts->minf); | |
599 | p.ts.majf = cpu_to_le64(ts->majf); | |
600 | p.ts.clat_percentiles = cpu_to_le64(ts->clat_percentiles); | |
802ad4a8 JA |
601 | |
602 | for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) { | |
603 | fio_fp64_t *fp = &p.ts.percentile_list[i]; | |
604 | ||
605 | fp->u.i = __cpu_to_le64(fio_double_to_uint64(fp->u.f)); | |
606 | } | |
a64e88da JA |
607 | |
608 | for (i = 0; i < FIO_IO_U_MAP_NR; i++) { | |
609 | p.ts.io_u_map[i] = cpu_to_le32(ts->io_u_map[i]); | |
610 | p.ts.io_u_submit[i] = cpu_to_le32(ts->io_u_submit[i]); | |
611 | p.ts.io_u_complete[i] = cpu_to_le32(ts->io_u_complete[i]); | |
612 | } | |
613 | ||
614 | for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { | |
615 | p.ts.io_u_lat_u[i] = cpu_to_le32(ts->io_u_lat_u[i]); | |
616 | p.ts.io_u_lat_m[i] = cpu_to_le32(ts->io_u_lat_m[i]); | |
617 | } | |
618 | ||
619 | for (i = 0; i < 2; i++) | |
620 | for (j = 0; j < FIO_IO_U_PLAT_NR; j++) | |
621 | p.ts.io_u_plat[i][j] = cpu_to_le32(ts->io_u_plat[i][j]); | |
622 | ||
623 | for (i = 0; i < 3; i++) { | |
624 | p.ts.total_io_u[i] = cpu_to_le64(ts->total_io_u[i]); | |
93eee04a | 625 | p.ts.short_io_u[i] = cpu_to_le64(ts->short_io_u[i]); |
a64e88da JA |
626 | } |
627 | ||
93eee04a | 628 | p.ts.total_submit = cpu_to_le64(ts->total_submit); |
a64e88da JA |
629 | p.ts.total_complete = cpu_to_le64(ts->total_complete); |
630 | ||
631 | for (i = 0; i < 2; i++) { | |
632 | p.ts.io_bytes[i] = cpu_to_le64(ts->io_bytes[i]); | |
633 | p.ts.runtime[i] = cpu_to_le64(ts->runtime[i]); | |
634 | } | |
635 | ||
636 | p.ts.total_run_time = cpu_to_le64(ts->total_run_time); | |
637 | p.ts.continue_on_error = cpu_to_le16(ts->continue_on_error); | |
638 | p.ts.total_err_count = cpu_to_le64(ts->total_err_count); | |
ddcc0b69 JA |
639 | p.ts.first_error = cpu_to_le32(ts->first_error); |
640 | p.ts.kb_base = cpu_to_le32(ts->kb_base); | |
a64e88da JA |
641 | |
642 | convert_gs(&p.rs, rs); | |
643 | ||
af9c9fb3 | 644 | fio_net_send_cmd(server_fd, FIO_NET_CMD_TS, &p, sizeof(p), 0); |
a64e88da JA |
645 | } |
646 | ||
647 | void fio_server_send_gs(struct group_run_stats *rs) | |
648 | { | |
649 | struct group_run_stats gs; | |
650 | ||
60efd14e JA |
651 | dprint(FD_NET, "server sending group run stats\n"); |
652 | ||
a64e88da | 653 | convert_gs(&gs, rs); |
af9c9fb3 | 654 | fio_net_send_cmd(server_fd, FIO_NET_CMD_GS, &gs, sizeof(gs), 0); |
cf451d1e JA |
655 | } |
656 | ||
142575e6 JA |
657 | int fio_server_log(const char *format, ...) |
658 | { | |
659 | char buffer[1024]; | |
660 | va_list args; | |
82fa6b21 | 661 | size_t len; |
142575e6 | 662 | |
60efd14e JA |
663 | dprint(FD_NET, "server log\n"); |
664 | ||
142575e6 | 665 | va_start(args, format); |
82fa6b21 | 666 | len = vsnprintf(buffer, sizeof(buffer), format, args); |
142575e6 JA |
667 | va_end(args); |
668 | ||
82fa6b21 | 669 | return fio_server_text_output(buffer, len); |
37db14fe | 670 | } |
e46d8091 | 671 | |
87aa8f19 | 672 | static int fio_init_server_ip(void) |
81179eec | 673 | { |
87aa8f19 | 674 | int sk, opt; |
81179eec JA |
675 | |
676 | sk = socket(AF_INET, SOCK_STREAM, 0); | |
677 | if (sk < 0) { | |
678 | log_err("fio: socket: %s\n", strerror(errno)); | |
679 | return -1; | |
680 | } | |
681 | ||
682 | opt = 1; | |
683 | if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { | |
684 | log_err("fio: setsockopt: %s\n", strerror(errno)); | |
b94cba47 | 685 | close(sk); |
81179eec JA |
686 | return -1; |
687 | } | |
688 | #ifdef SO_REUSEPORT | |
6eb24791 | 689 | if (setsockopt(sk, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) { |
81179eec | 690 | log_err("fio: setsockopt: %s\n", strerror(errno)); |
b94cba47 | 691 | close(sk); |
81179eec JA |
692 | return -1; |
693 | } | |
694 | #endif | |
695 | ||
696 | saddr_in.sin_family = AF_INET; | |
81179eec JA |
697 | |
698 | if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) { | |
699 | log_err("fio: bind: %s\n", strerror(errno)); | |
b94cba47 | 700 | close(sk); |
81179eec JA |
701 | return -1; |
702 | } | |
703 | ||
87aa8f19 JA |
704 | return sk; |
705 | } | |
706 | ||
707 | static int fio_init_server_sock(void) | |
708 | { | |
709 | struct sockaddr_un addr; | |
710 | fio_socklen_t len; | |
711 | mode_t mode; | |
712 | int sk; | |
713 | ||
714 | sk = socket(AF_UNIX, SOCK_STREAM, 0); | |
715 | if (sk < 0) { | |
716 | log_err("fio: socket: %s\n", strerror(errno)); | |
717 | return -1; | |
718 | } | |
719 | ||
720 | mode = umask(000); | |
721 | ||
722 | memset(&addr, 0, sizeof(addr)); | |
723 | addr.sun_family = AF_UNIX; | |
724 | strcpy(addr.sun_path, bind_sock); | |
725 | unlink(bind_sock); | |
726 | ||
727 | len = sizeof(addr.sun_family) + strlen(bind_sock) + 1; | |
728 | ||
729 | if (bind(sk, (struct sockaddr *) &addr, len) < 0) { | |
730 | log_err("fio: bind: %s\n", strerror(errno)); | |
b94cba47 | 731 | close(sk); |
87aa8f19 JA |
732 | return -1; |
733 | } | |
734 | ||
735 | umask(mode); | |
736 | return sk; | |
737 | } | |
738 | ||
739 | static int fio_init_server_connection(void) | |
740 | { | |
bebe6398 | 741 | char bind_str[128]; |
87aa8f19 JA |
742 | int sk; |
743 | ||
744 | dprint(FD_NET, "starting server\n"); | |
745 | ||
746 | if (!bind_sock) | |
747 | sk = fio_init_server_ip(); | |
748 | else | |
749 | sk = fio_init_server_sock(); | |
750 | ||
751 | if (sk < 0) | |
752 | return sk; | |
753 | ||
bebe6398 JA |
754 | if (!bind_sock) |
755 | sprintf(bind_str, "%s:%u", inet_ntoa(saddr_in.sin_addr), fio_net_port); | |
756 | else | |
757 | strcpy(bind_str, bind_sock); | |
758 | ||
759 | log_info("fio: server listening on %s\n", bind_str); | |
760 | ||
81179eec JA |
761 | if (listen(sk, 1) < 0) { |
762 | log_err("fio: listen: %s\n", strerror(errno)); | |
763 | return -1; | |
764 | } | |
765 | ||
87aa8f19 JA |
766 | return sk; |
767 | } | |
768 | ||
bebe6398 JA |
769 | int fio_server_parse_string(const char *str, char **ptr, int *is_sock, |
770 | int *port, struct in_addr *inp) | |
771 | { | |
772 | *ptr = NULL; | |
773 | *is_sock = 0; | |
6d2cf394 | 774 | *port = fio_net_port; |
bebe6398 JA |
775 | |
776 | if (!strncmp(str, "sock:", 5)) { | |
777 | *ptr = strdup(str + 5); | |
778 | *is_sock = 1; | |
779 | } else { | |
780 | const char *host = str; | |
781 | char *portp; | |
782 | int lport = 0; | |
783 | ||
784 | /* | |
785 | * Is it ip:<ip or host>:port | |
786 | */ | |
787 | if (!strncmp(host, "ip:", 3)) | |
788 | host += 3; | |
789 | else if (host[0] == ':') { | |
790 | /* String is :port */ | |
791 | host++; | |
792 | lport = atoi(host); | |
793 | if (!lport || lport > 65535) { | |
794 | log_err("fio: bad server port %u\n", port); | |
795 | return 1; | |
796 | } | |
797 | /* no hostname given, we are done */ | |
798 | *port = lport; | |
799 | return 0; | |
800 | } | |
801 | ||
802 | /* | |
803 | * If no port seen yet, check if there's a last ':' at the end | |
804 | */ | |
805 | if (!lport) { | |
806 | portp = strchr(host, ':'); | |
807 | if (portp) { | |
808 | *portp = '\0'; | |
809 | portp++; | |
810 | lport = atoi(portp); | |
811 | if (!lport || lport > 65535) { | |
812 | log_err("fio: bad server port %u\n", port); | |
813 | return 1; | |
814 | } | |
815 | } | |
816 | } | |
817 | ||
818 | if (lport) | |
819 | *port = lport; | |
820 | ||
821 | *ptr = strdup(host); | |
822 | ||
823 | if (inet_aton(host, inp) != 1) { | |
824 | struct hostent *hent; | |
825 | ||
826 | hent = gethostbyname(host); | |
827 | if (!hent) { | |
bebe6398 JA |
828 | free(*ptr); |
829 | *ptr = NULL; | |
830 | return 1; | |
831 | } | |
832 | ||
833 | memcpy(inp, hent->h_addr, 4); | |
834 | } | |
835 | } | |
836 | ||
837 | if (*port == 0) | |
838 | *port = fio_net_port; | |
839 | ||
840 | return 0; | |
841 | } | |
842 | ||
87aa8f19 JA |
843 | /* |
844 | * Server arg should be one of: | |
845 | * | |
846 | * sock:/path/to/socket | |
847 | * ip:1.2.3.4 | |
848 | * 1.2.3.4 | |
849 | * | |
850 | * Where sock uses unix domain sockets, and ip binds the server to | |
851 | * a specific interface. If no arguments are given to the server, it | |
852 | * uses IP and binds to 0.0.0.0. | |
853 | * | |
854 | */ | |
855 | static int fio_handle_server_arg(void) | |
856 | { | |
6d2cf394 | 857 | int port = fio_net_port; |
a7de0a11 | 858 | int is_sock, ret = 0; |
bebe6398 | 859 | |
87aa8f19 JA |
860 | saddr_in.sin_addr.s_addr = htonl(INADDR_ANY); |
861 | ||
862 | if (!fio_server_arg) | |
a7de0a11 | 863 | goto out; |
87aa8f19 | 864 | |
4e5b8fb8 | 865 | ret = fio_server_parse_string(fio_server_arg, &bind_sock, &is_sock, |
6d2cf394 | 866 | &port, &saddr_in.sin_addr); |
4e5b8fb8 JA |
867 | |
868 | if (!is_sock && bind_sock) { | |
869 | free(bind_sock); | |
870 | bind_sock = NULL; | |
871 | } | |
872 | ||
a7de0a11 | 873 | out: |
6d2cf394 JA |
874 | fio_net_port = port; |
875 | saddr_in.sin_port = htons(port); | |
4e5b8fb8 | 876 | return ret; |
87aa8f19 JA |
877 | } |
878 | ||
879 | static int fio_server(void) | |
880 | { | |
881 | int sk, ret; | |
882 | ||
883 | dprint(FD_NET, "starting server\n"); | |
884 | ||
885 | if (fio_handle_server_arg()) | |
886 | return -1; | |
887 | ||
888 | sk = fio_init_server_connection(); | |
889 | if (sk < 0) | |
890 | return -1; | |
81179eec JA |
891 | |
892 | ret = accept_loop(sk); | |
87aa8f19 | 893 | |
81179eec | 894 | close(sk); |
87aa8f19 JA |
895 | |
896 | if (fio_server_arg) { | |
897 | free(fio_server_arg); | |
898 | fio_server_arg = NULL; | |
899 | } | |
bebe6398 JA |
900 | if (bind_sock) |
901 | free(bind_sock); | |
87aa8f19 | 902 | |
81179eec JA |
903 | return ret; |
904 | } | |
905 | ||
9abea48b JA |
906 | static void sig_int(int sig) |
907 | { | |
908 | fio_terminate_threads(TERMINATE_ALL); | |
909 | exit_backend = 1; | |
910 | } | |
911 | ||
912 | static void server_signal_handler(void) | |
913 | { | |
914 | struct sigaction act; | |
915 | ||
916 | memset(&act, 0, sizeof(act)); | |
917 | act.sa_handler = sig_int; | |
918 | act.sa_flags = SA_RESTART; | |
919 | sigaction(SIGINT, &act, NULL); | |
920 | ||
921 | memset(&act, 0, sizeof(act)); | |
922 | act.sa_handler = sig_int; | |
923 | act.sa_flags = SA_RESTART; | |
924 | sigaction(SIGTERM, &act, NULL); | |
925 | } | |
926 | ||
402668f3 JA |
927 | static void write_pid(pid_t pid, const char *pidfile) |
928 | { | |
929 | FILE *fpid; | |
930 | ||
931 | fpid = fopen(pidfile, "w"); | |
932 | if (!fpid) { | |
933 | log_err("fio: failed opening pid file %s\n", pidfile); | |
934 | return; | |
935 | } | |
936 | ||
937 | fprintf(fpid, "%u\n", (unsigned int) pid); | |
938 | } | |
939 | ||
940 | /* | |
941 | * If pidfile is specified, background us. | |
942 | */ | |
943 | int fio_start_server(char *pidfile) | |
e46d8091 JA |
944 | { |
945 | pid_t pid; | |
402668f3 | 946 | int ret; |
e46d8091 | 947 | |
9abea48b JA |
948 | server_signal_handler(); |
949 | ||
402668f3 | 950 | if (!pidfile) |
e46d8091 JA |
951 | return fio_server(); |
952 | ||
953 | openlog("fio", LOG_NDELAY|LOG_NOWAIT|LOG_PID, LOG_USER); | |
954 | pid = fork(); | |
955 | if (pid < 0) { | |
956 | syslog(LOG_ERR, "failed server fork"); | |
402668f3 | 957 | free(pidfile); |
c28e8e8c | 958 | return -1; |
402668f3 JA |
959 | } else if (pid) { |
960 | write_pid(pid, pidfile); | |
e46d8091 | 961 | exit(0); |
402668f3 | 962 | } |
e46d8091 JA |
963 | |
964 | setsid(); | |
965 | close(STDIN_FILENO); | |
966 | close(STDOUT_FILENO); | |
967 | close(STDERR_FILENO); | |
968 | f_out = NULL; | |
969 | f_err = NULL; | |
970 | log_syslog = 1; | |
402668f3 JA |
971 | |
972 | ret = fio_server(); | |
973 | ||
974 | closelog(); | |
975 | unlink(pidfile); | |
976 | free(pidfile); | |
977 | return ret; | |
e46d8091 | 978 | } |
87aa8f19 | 979 | |
bebe6398 | 980 | void fio_server_set_arg(const char *arg) |
87aa8f19 JA |
981 | { |
982 | fio_server_arg = strdup(arg); | |
983 | } |