server: initial support for daemonizing
[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 |= 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                 exit_backend = 1;
268                 return 1;
269         case FIO_NET_CMD_ACK:
270                 return 0;
271         case FIO_NET_CMD_NAK:
272                 return 1;
273         case FIO_NET_CMD_JOB:
274                 ret = handle_cur_job(cmd);
275                 break;
276         default:
277                 log_err("fio: unknown opcode: %d\n", cmd->opcode);
278                 ret = 1;
279         }
280
281         return ret;
282 }
283
284 static int handle_connection(int sk)
285 {
286         struct fio_net_cmd *cmd = NULL;
287         int ret = 0;
288
289         /* read forever */
290         while (!exit_backend) {
291                 cmd = fio_net_cmd_read(sk);
292                 if (!cmd) {
293                         ret = 1;
294                         break;
295                 }
296
297                 ret = ack_command(sk, cmd);
298                 if (ret)
299                         break;
300
301                 ret = handle_command(cmd);
302                 if (ret)
303                         break;
304
305                 free(cmd);
306                 cmd = NULL;
307         }
308
309         if (cmd)
310                 free(cmd);
311
312         return ret;
313 }
314
315 static int accept_loop(int listen_sk)
316 {
317         struct sockaddr addr;
318         unsigned int len = sizeof(addr);
319         struct pollfd pfd;
320         int ret, sk, flags, exitval = 0;
321
322         flags = fcntl(listen_sk, F_GETFL);
323         flags |= O_NONBLOCK;
324         fcntl(listen_sk, F_SETFL, flags);
325 again:
326         pfd.fd = listen_sk;
327         pfd.events = POLLIN;
328         do {
329                 ret = poll(&pfd, 1, 100);
330                 if (ret < 0) {
331                         if (errno == EINTR)
332                                 break;
333                         log_err("fio: poll: %s\n", strerror(errno));
334                         goto out;
335                 } else if (!ret)
336                         continue;
337
338                 if (pfd.revents & POLLIN)
339                         break;
340         } while (!exit_backend);
341
342         if (exit_backend)
343                 goto out;
344
345         sk = accept(listen_sk, &addr, &len);
346         if (sk < 0) {
347                 log_err("fio: accept: %s\n", strerror(errno));
348                 return -1;
349         }
350
351         dprint(FD_NET, "server got a connection\n");
352
353         server_fd = sk;
354
355         exitval = handle_connection(sk);
356
357         server_fd = -1;
358         close(sk);
359
360         if (!exit_backend)
361                 goto again;
362
363 out:
364         return exitval;
365 }
366
367 static int fio_server(void)
368 {
369         struct sockaddr_in saddr_in;
370         struct sockaddr addr;
371         unsigned int len;
372         int sk, opt, ret;
373
374         dprint(FD_NET, "starting server\n");
375
376         sk = socket(AF_INET, SOCK_STREAM, 0);
377         if (sk < 0) {
378                 log_err("fio: socket: %s\n", strerror(errno));
379                 return -1;
380         }
381
382         opt = 1;
383         if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
384                 log_err("fio: setsockopt: %s\n", strerror(errno));
385                 return -1;
386         }
387 #ifdef SO_REUSEPORT
388         if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
389                 log_err("fio: setsockopt: %s\n", strerror(errno));
390                 return 1;
391         }
392 #endif
393
394         saddr_in.sin_family = AF_INET;
395         saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
396         saddr_in.sin_port = htons(fio_net_port);
397
398         if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0) {
399                 log_err("fio: bind: %s\n", strerror(errno));
400                 return -1;
401         }
402
403         if (listen(sk, 1) < 0) {
404                 log_err("fio: listen: %s\n", strerror(errno));
405                 return -1;
406         }
407
408         len = sizeof(addr);
409         if (getsockname(sk, &addr, &len) < 0) {
410                 log_err("fio: getsockname: %s\n", strerror(errno));
411                 return -1;
412         }
413
414         ret = accept_loop(sk);
415         close(sk);
416         return ret;
417 }
418
419 int fio_server_text_output(const char *buf, unsigned int len)
420 {
421         if (server_fd != -1)
422                 return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len);
423
424         return 0;
425 }
426
427 int fio_server_log(const char *format, ...)
428 {
429         char buffer[1024];
430         va_list args;
431         size_t len;
432
433         va_start(args, format);
434         len = vsnprintf(buffer, sizeof(buffer), format, args);
435         va_end(args);
436
437         return fio_server_text_output(buffer, len);
438 }
439
440 int fio_start_server(int daemonize)
441 {
442         pid_t pid;
443
444         if (!daemonize)
445                 return fio_server();
446
447         openlog("fio", LOG_NDELAY|LOG_NOWAIT|LOG_PID, LOG_USER);
448         pid = fork();
449         if (pid < 0) {
450                 syslog(LOG_ERR, "failed server fork");
451                 return 1;
452         } else if (pid)
453                 exit(0);
454
455         setsid();
456         close(STDIN_FILENO);
457         close(STDOUT_FILENO);
458         close(STDERR_FILENO);
459         f_out = NULL;
460         f_err = NULL;
461         log_syslog = 1;
462         return fio_server();
463 }