server: unify shm setup
[fio.git] / server.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <unistd.h>
5 #include <limits.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <sys/poll.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <sys/mman.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <netdb.h>
15 #include <syslog.h>
16
17 #include "fio.h"
18 #include "server.h"
19 #include "crc/crc16.h"
20
21 int fio_net_port = 8765;
22
23 int exit_backend = 0;
24
25 static char *job_buf;
26 static unsigned int job_cur_len;
27 static unsigned int job_max_len;
28
29 static int server_fd = -1;
30
31 int fio_send_data(int sk, const void *p, unsigned int len)
32 {
33         assert(len <= sizeof(struct fio_net_cmd) + FIO_SERVER_MAX_PDU);
34
35         do {
36                 int ret = send(sk, p, len, 0);
37
38                 if (ret > 0) {
39                         len -= ret;
40                         if (!len)
41                                 break;
42                         p += ret;
43                         continue;
44                 } else if (!ret)
45                         break;
46                 else if (errno == EAGAIN || errno == EINTR)
47                         continue;
48         } while (!exit_backend);
49
50         if (!len)
51                 return 0;
52
53         return 1;
54 }
55
56 int fio_recv_data(int sk, void *p, unsigned int len)
57 {
58         do {
59                 int ret = recv(sk, p, len, MSG_WAITALL);
60
61                 if (ret > 0) {
62                         len -= ret;
63                         if (!len)
64                                 break;
65                         p += ret;
66                         continue;
67                 } else if (!ret)
68                         break;
69                 else if (errno == EAGAIN || errno == EINTR)
70                         continue;
71         } while (!exit_backend);
72
73         if (!len)
74                 return 0;
75
76         return -1;
77 }
78
79 static int verify_convert_cmd(struct fio_net_cmd *cmd)
80 {
81         uint16_t crc;
82
83         cmd->cmd_crc16 = le16_to_cpu(cmd->cmd_crc16);
84         cmd->pdu_crc16 = le16_to_cpu(cmd->pdu_crc16);
85
86         crc = crc16(cmd, FIO_NET_CMD_CRC_SZ);
87         if (crc != cmd->cmd_crc16) {
88                 log_err("fio: server bad crc on command (got %x, wanted %x)\n",
89                                 cmd->cmd_crc16, crc);
90                 return 1;
91         }
92
93         cmd->version    = le16_to_cpu(cmd->version);
94         cmd->opcode     = le16_to_cpu(cmd->opcode);
95         cmd->flags      = le32_to_cpu(cmd->flags);
96         cmd->serial     = le64_to_cpu(cmd->serial);
97         cmd->pdu_len    = le32_to_cpu(cmd->pdu_len);
98
99         switch (cmd->version) {
100         case FIO_SERVER_VER1:
101                 break;
102         default:
103                 log_err("fio: bad server cmd version %d\n", cmd->version);
104                 return 1;
105         }
106
107         if (cmd->pdu_len > FIO_SERVER_MAX_PDU) {
108                 log_err("fio: command payload too large: %u\n", cmd->pdu_len);
109                 return 1;
110         }
111
112         return 0;
113 }
114
115 struct fio_net_cmd *fio_net_cmd_read(int sk)
116 {
117         struct fio_net_cmd cmd, *ret = NULL;
118         uint16_t crc;
119
120         if (fio_recv_data(sk, &cmd, sizeof(cmd)))
121                 return NULL;
122
123         /* We have a command, verify it and swap if need be */
124         if (verify_convert_cmd(&cmd))
125                 return NULL;
126
127         /* Command checks out, alloc real command and fill in */
128         ret = malloc(sizeof(cmd) + cmd.pdu_len);
129         memcpy(ret, &cmd, sizeof(cmd));
130
131         if (!ret->pdu_len)
132                 return ret;
133
134         /* There's payload, get it */
135         if (fio_recv_data(sk, (void *) ret + sizeof(*ret), ret->pdu_len)) {
136                 free(ret);
137                 return NULL;
138         }
139
140         /* Verify payload crc */
141         crc = crc16(ret->payload, ret->pdu_len);
142         if (crc != ret->pdu_crc16) {
143                 log_err("fio: server bad crc on payload (got %x, wanted %x)\n",
144                                 ret->pdu_crc16, crc);
145                 free(ret);
146                 return NULL;
147         }
148
149         return ret;
150 }
151
152 void fio_net_cmd_crc(struct fio_net_cmd *cmd)
153 {
154         uint32_t pdu_len;
155
156         cmd->cmd_crc16 = cpu_to_le16(crc16(cmd, FIO_NET_CMD_CRC_SZ));
157
158         pdu_len = le32_to_cpu(cmd->pdu_len);
159         if (pdu_len)
160                 cmd->pdu_crc16 = cpu_to_le16(crc16(cmd->payload, pdu_len));
161 }
162
163 int fio_net_send_cmd(int fd, uint16_t opcode, const char *buf, off_t size)
164 {
165         struct fio_net_cmd *cmd;
166         size_t this_len;
167         int ret;
168
169         do {
170                 this_len = size;
171                 if (this_len > FIO_SERVER_MAX_PDU)
172                         this_len = FIO_SERVER_MAX_PDU;
173
174                 cmd = malloc(sizeof(*cmd) + this_len);
175
176                 fio_init_net_cmd(cmd, opcode, buf, this_len);
177
178                 if (this_len < size)
179                         cmd->flags = cpu_to_le32(FIO_NET_CMD_F_MORE);
180
181                 fio_net_cmd_crc(cmd);
182
183                 ret = fio_send_data(fd, cmd, sizeof(*cmd) + this_len);
184                 free(cmd);
185                 size -= this_len;
186                 buf += this_len;
187         } while (!ret && size);
188
189         return ret;
190 }
191
192 static int send_simple_command(int sk, uint16_t opcode, uint64_t serial)
193 {
194         struct fio_net_cmd cmd = {
195                 .version        = cpu_to_le16(FIO_SERVER_VER1),
196                 .opcode         = cpu_to_le16(opcode),
197                 .serial         = cpu_to_le64(serial),
198         };
199
200         fio_net_cmd_crc(&cmd);
201
202         return fio_send_data(sk, &cmd, sizeof(cmd));
203 }
204
205 /*
206  * Send an ack for this command
207  */
208 static int ack_command(int sk, struct fio_net_cmd *cmd)
209 {
210 #if 0
211         return send_simple_command(sk, FIO_NET_CMD_ACK, cmd->serial);
212 #else
213         return 0;
214 #endif
215 }
216
217 #if 0
218 static int nak_command(int sk, struct fio_net_cmd *cmd)
219 {
220         return send_simple_command(sk, FIO_NET_CMD_NAK, cmd->serial);
221 }
222 #endif
223
224 static int send_quit_command(void)
225 {
226         dprint(FD_NET, "server: sending quit\n");
227         return send_simple_command(server_fd, FIO_NET_CMD_QUIT, 0);
228 }
229
230 static int handle_cur_job(struct fio_net_cmd *cmd)
231 {
232         unsigned int left = job_max_len - job_cur_len;
233         int ret = 0;
234
235         if (left < cmd->pdu_len) {
236                 job_buf = realloc(job_buf, job_max_len + 2 * cmd->pdu_len);
237                 job_max_len += 2 * cmd->pdu_len;
238         }
239
240         memcpy(job_buf + job_cur_len, cmd->payload, cmd->pdu_len);
241         job_cur_len += cmd->pdu_len;
242
243         /*
244          * More data coming for this job
245          */
246         if (cmd->flags & FIO_NET_CMD_F_MORE)
247                 return 0;
248
249         parse_jobs_ini(job_buf, 1, 0);
250         ret = exec_run();
251         send_quit_command();
252         reset_fio_state();
253         free(job_buf);
254         job_buf = NULL;
255         job_cur_len = job_max_len = 0;
256         return ret;
257 }
258
259 static int handle_command(struct fio_net_cmd *cmd)
260 {
261         int ret;
262
263         dprint(FD_NET, "server: got opcode %d\n", cmd->opcode);
264
265         switch (cmd->opcode) {
266         case FIO_NET_CMD_QUIT:
267                 return 1;
268         case FIO_NET_CMD_EXIT:
269                 exit_backend = 1;
270                 return 1;
271         case FIO_NET_CMD_ACK:
272                 return 0;
273         case FIO_NET_CMD_NAK:
274                 return 1;
275         case FIO_NET_CMD_JOB:
276                 ret = handle_cur_job(cmd);
277                 break;
278         default:
279                 log_err("fio: unknown opcode: %d\n", cmd->opcode);
280                 ret = 1;
281         }
282
283         return ret;
284 }
285
286 static int handle_connection(int sk)
287 {
288         struct fio_net_cmd *cmd = NULL;
289         int ret = 0;
290
291         /* read forever */
292         while (!exit_backend) {
293                 cmd = fio_net_cmd_read(sk);
294                 if (!cmd) {
295                         ret = 1;
296                         break;
297                 }
298
299                 ret = ack_command(sk, cmd);
300                 if (ret)
301                         break;
302
303                 ret = handle_command(cmd);
304                 if (ret)
305                         break;
306
307                 free(cmd);
308                 cmd = NULL;
309         }
310
311         if (cmd)
312                 free(cmd);
313
314         return ret;
315 }
316
317 static int accept_loop(int listen_sk)
318 {
319         struct sockaddr addr;
320         unsigned int len = sizeof(addr);
321         struct pollfd pfd;
322         int ret, sk, flags, exitval = 0;
323
324         flags = fcntl(listen_sk, F_GETFL);
325         flags |= O_NONBLOCK;
326         fcntl(listen_sk, F_SETFL, flags);
327 again:
328         pfd.fd = listen_sk;
329         pfd.events = POLLIN;
330         do {
331                 ret = poll(&pfd, 1, 100);
332                 if (ret < 0) {
333                         if (errno == EINTR)
334                                 break;
335                         log_err("fio: poll: %s\n", strerror(errno));
336                         goto out;
337                 } else if (!ret)
338                         continue;
339
340                 if (pfd.revents & POLLIN)
341                         break;
342         } while (!exit_backend);
343
344         if (exit_backend)
345                 goto out;
346
347         sk = accept(listen_sk, &addr, &len);
348         if (sk < 0) {
349                 log_err("fio: accept: %s\n", strerror(errno));
350                 return -1;
351         }
352
353         dprint(FD_NET, "server got a connection\n");
354
355         server_fd = sk;
356
357         exitval = handle_connection(sk);
358
359         server_fd = -1;
360         close(sk);
361
362         if (!exit_backend)
363                 goto again;
364
365 out:
366         return exitval;
367 }
368
369 static int fio_server(void)
370 {
371         struct sockaddr_in saddr_in;
372         struct sockaddr addr;
373         unsigned int len;
374         int sk, opt, ret;
375
376         dprint(FD_NET, "starting server\n");
377
378         sk = socket(AF_INET, SOCK_STREAM, 0);
379         if (sk < 0) {
380                 log_err("fio: socket: %s\n", strerror(errno));
381                 return -1;
382         }
383
384         opt = 1;
385         if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
386                 log_err("fio: setsockopt: %s\n", strerror(errno));
387                 return -1;
388         }
389 #ifdef SO_REUSEPORT
390         if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
391                 log_err("fio: setsockopt: %s\n", strerror(errno));
392                 return 1;
393         }
394 #endif
395
396         saddr_in.sin_family = AF_INET;
397         saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
398         saddr_in.sin_port = htons(fio_net_port);
399
400         if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) {
401                 log_err("fio: bind: %s\n", strerror(errno));
402                 return -1;
403         }
404
405         if (listen(sk, 1) < 0) {
406                 log_err("fio: listen: %s\n", strerror(errno));
407                 return -1;
408         }
409
410         len = sizeof(addr);
411         if (getsockname(sk, &addr, &len) < 0) {
412                 log_err("fio: getsockname: %s\n", strerror(errno));
413                 return -1;
414         }
415
416         ret = accept_loop(sk);
417         close(sk);
418         return ret;
419 }
420
421 int fio_server_text_output(const char *buf, unsigned int len)
422 {
423         if (server_fd != -1)
424                 return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len);
425
426         return 0;
427 }
428
429 int fio_server_log(const char *format, ...)
430 {
431         char buffer[1024];
432         va_list args;
433         size_t len;
434
435         va_start(args, format);
436         len = vsnprintf(buffer, sizeof(buffer), format, args);
437         va_end(args);
438
439         return fio_server_text_output(buffer, len);
440 }
441
442 int fio_start_server(int daemonize)
443 {
444         pid_t pid;
445
446         if (!daemonize)
447                 return fio_server();
448
449         openlog("fio", LOG_NDELAY|LOG_NOWAIT|LOG_PID, LOG_USER);
450         pid = fork();
451         if (pid < 0) {
452                 syslog(LOG_ERR, "failed server fork");
453                 return 1;
454         } else if (pid)
455                 exit(0);
456
457         setsid();
458         close(STDIN_FILENO);
459         close(STDOUT_FILENO);
460         close(STDERR_FILENO);
461         f_out = NULL;
462         f_err = NULL;
463         log_syslog = 1;
464         return fio_server();
465 }