]> git.pond.sub.org Git - empserver/blob - src/client/play.c
b66f7fdc394e6ee1b028559a9df122631caadfdc
[empserver] / src / client / play.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2020, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  play.c: Playing the game
28  *
29  *  Known contributors to this file:
30  *     Markus Armbruster, 2007-2017
31  *     Ron Koenderink, 2007-2009
32  *     Martin Haukeli, 2015
33  */
34
35 #include <config.h>
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #ifdef _WIN32
44 #include <process.h>
45 #include <sys/socket.h>
46 #else
47 #include <sys/select.h>
48 #endif
49 #include <unistd.h>
50 #include "linebuf.h"
51 #include "misc.h"
52 #include "proto.h"
53 #include "ringbuf.h"
54 #include "secure.h"
55
56 #ifdef HAVE_LIBREADLINE
57 #include <readline/readline.h>
58 #include <readline/history.h>
59 #endif
60
61 #define EOF_COOKIE "ctld\n"
62 #define INTR_COOKIE "aborted\n"
63
64 /*
65  * Player input file descriptor
66  * 0 while reading interactive input
67  * >0 while reading a batch file
68  * <0 during error handling
69  */
70 static int input_fd;
71
72 static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */
73
74 #ifdef _WIN32
75 static CRITICAL_SECTION signal_critical_section;
76 static LPCRITICAL_SECTION signal_critical_section_ptr = NULL;
77
78 static unsigned char bounce_buf[RING_SIZE];
79 /*
80  * Set bounce_empty to indicate bounce_buf is available for the stdin thread
81  * to use.
82  */
83 static HANDLE bounce_empty;
84 /*
85  * Set bounce_full to indicate bounce_buf is contains data from the
86  * stdin thread and is available for recv_input
87  */
88 static HANDLE bounce_full;
89 /* Ctrl-C (SIGINT) was detected, generate EINTR for the w32_select() */
90 static HANDLE ctrl_c_event;
91 static int bounce_status, bounce_error;
92
93 struct sigaction {
94     int sa_flags;
95     void (*sa_handler)(int sig);
96 };
97
98 #define SIGPIPE -1
99 static void (*ctrl_handler)(int sig) = { SIG_DFL };
100
101 /*
102  * Ctrl-C handler for emulating the SIGINT in WIN32
103  */
104 static BOOL WINAPI
105 w32_signal_handler(DWORD ctrl_type)
106 {
107     if (ctrl_type == CTRL_C_EVENT) {
108         EnterCriticalSection(signal_critical_section_ptr);
109         if (ctrl_handler != SIG_DFL) {
110             ctrl_handler(SIGINT);
111             LeaveCriticalSection(signal_critical_section_ptr);
112             SetEvent(ctrl_c_event);
113             return TRUE;
114         } else
115             LeaveCriticalSection(signal_critical_section_ptr);
116     }
117     return FALSE;
118 }
119
120 /*
121  * WIN32 equivalent for sigaction supports the following:
122  * set handler for SIGINT using WIN32 Ctrl-C handler
123  * reset handler SIGINT to SIG_DFL
124  * ignore SIGPIPE
125  */
126 static int
127 sigaction(int signal, struct sigaction *action, struct sigaction *oaction)
128 {
129     assert(!oaction);
130     assert(action);
131
132     if (signal == SIGPIPE)
133         assert(action->sa_handler == SIG_IGN);
134     else {
135         assert(signal == SIGINT && action->sa_handler != SIG_IGN);
136         if (ctrl_handler == action->sa_handler)
137             return 0;
138         if (signal_critical_section_ptr == NULL) {
139             signal_critical_section_ptr = &signal_critical_section;
140             InitializeCriticalSection(signal_critical_section_ptr);
141         }
142         EnterCriticalSection(signal_critical_section_ptr);
143         if (!SetConsoleCtrlHandler(w32_signal_handler,
144                                    action->sa_handler != SIG_DFL)) {
145             errno = GetLastError();
146             LeaveCriticalSection(signal_critical_section_ptr);
147             return -1;
148         }
149         ctrl_handler = action->sa_handler;
150         LeaveCriticalSection(signal_critical_section_ptr);
151     }
152     return 0;
153 }
154
155 /*
156  * Read the stdin in WIN32 environment
157  * WIN32 does not support select type function on console input
158  * so the client uses a separate thread to read input
159  */
160 static void
161 stdin_read_thread(void *dummy)
162 {
163     for (;;) {
164         switch (WaitForSingleObject(bounce_empty, INFINITE)) {
165         case WAIT_FAILED:
166             bounce_status = -1;
167             bounce_error = GetLastError();
168             break;
169         case WAIT_OBJECT_0:
170             bounce_status = _read(0, bounce_buf, sizeof(bounce_buf));
171             bounce_error = errno;
172             break;
173         case WAIT_ABANDONED:
174             return;
175         default:
176             assert(0);
177         }
178         SetEvent(bounce_full);
179     }
180 }
181
182 /*
183  * Initialize and start the stdin reading thread for WIN32
184  */
185 static void
186 sysdep_stdin_init(void)
187 {
188     bounce_empty = CreateEvent(NULL, FALSE, TRUE, NULL);
189     bounce_full = CreateEvent(NULL, TRUE, FALSE, NULL);
190     ctrl_c_event = CreateEvent(NULL, FALSE, FALSE, NULL);
191     _beginthread(stdin_read_thread, 0, NULL);
192 }
193
194 /*
195  * This function uses to WaitForMultipleObjects to wait for both
196  * stdin and socket reading or writing.
197  * Stdin is treated special in WIN32.  Waiting for stdin is done
198  * via a bounce_full event which is set in the stdin thread.
199  * Execute command file reading is done via handle.
200  * WaitForMultipleObjects will only respond with one object,
201  * so an additonal select is also done to determine
202  * which individual events are active.
203  */
204 static int
205 w32_select(int nfds, fd_set *rdfd, fd_set *wrfd, fd_set *errfd,
206            struct timeval *time)
207 {
208     HANDLE handles[3];
209     SOCKET sock;
210     int inp, sockfd, result, s_result, num_handles;
211     struct timeval tv_time = {0, 0};
212     fd_set rdsock, wrsock;
213
214     switch (rdfd->fd_count) {
215     case 1:
216         inp = -1;
217         sockfd = rdfd->fd_array[0];
218         break;
219     case 2:
220         inp = rdfd->fd_array[0];
221         sockfd = rdfd->fd_array[1];
222         break;
223     default:
224         assert(0);
225     }
226     sock = w32_fd2socket(sockfd);
227
228     assert(wrfd->fd_count == 0
229            || (wrfd->fd_count == 1 && wrfd->fd_array[0] == (SOCKET)sockfd));
230     assert(inp < 0 || inp == input_fd);
231
232     num_handles = 0;
233     handles[num_handles++] = ctrl_c_event;
234     if (inp >= 0)
235         handles[num_handles++]
236             = inp ? (HANDLE)_get_osfhandle(inp) : bounce_full;
237     /* always wait on the socket */
238     handles[num_handles++] = WSACreateEvent();
239
240     if (wrfd->fd_count > 0)
241         WSAEventSelect(sock, handles[num_handles - 1],
242                        FD_READ | FD_WRITE | FD_CLOSE);
243     else
244         WSAEventSelect(sock, handles[num_handles - 1],
245                        FD_READ | FD_CLOSE);
246
247     result = WaitForMultipleObjects(num_handles, handles, 0, INFINITE);
248     if (result < 0) {
249         errno = GetLastError();
250         WSACloseEvent(handles[num_handles - 1]);
251         return -1;
252     }
253     WSACloseEvent(handles[num_handles - 1]);
254
255     if (result == WAIT_OBJECT_0) {
256         errno = EINTR;
257         return -1;
258     }
259
260     FD_ZERO(&rdsock);
261     FD_ZERO(&wrsock);
262     FD_SET(sock, &rdsock);
263     if (wrfd->fd_count)
264         FD_SET(sock, &wrsock);
265     s_result = select(sock + 1, &rdsock, &wrsock, NULL, &tv_time);
266
267     if (s_result < 0) {
268         w32_set_winsock_errno();
269         return s_result;
270     }
271
272     if (!FD_ISSET(sock, &rdsock))
273         FD_CLR((SOCKET)sockfd, rdfd);
274     if (!FD_ISSET(sock, &wrsock))
275         FD_CLR((SOCKET)sockfd, wrfd);
276     if (inp >= 0 && result == WAIT_OBJECT_0 + 1)
277         s_result++;
278     else
279         FD_CLR((SOCKET)inp, rdfd);
280
281     return s_result;
282 }
283
284 /*
285  * Read input from the user either stdin or from file.
286  * For stdin, read from bounce_buf which filled by the stdin thread
287  * otherwise use the regular ring_from_file.
288  */
289 static int
290 w32_ring_from_file_or_bounce_buf(struct ring *r, int fd)
291 {
292     int i, res;
293
294     if (fd)
295         return ring_from_file(r, fd);
296
297     if (bounce_status < 0) {
298         errno = bounce_error;
299         res = bounce_status;
300     } else {
301         for (i = 0; i < bounce_status; i++) {
302             if (ring_putc(r, bounce_buf[i]) == EOF) {
303                 /* more work to do, hold on to bounce_buf */
304                 memmove(bounce_buf, bounce_buf + i, bounce_status - i);
305                 bounce_status -= i;
306                 return i;
307             }
308         }
309         res = i;
310     }
311
312     ResetEvent(bounce_full);
313     SetEvent(bounce_empty);
314     return res;
315 }
316 #define ring_from_file w32_ring_from_file_or_bounce_buf
317 #define select(nfds, rd, wr, error, time) \
318         w32_select((nfds), (rd), (wr), (error), (time))
319 #define sigemptyset(mask) ((void)0)
320 #else
321 #define sysdep_stdin_init() ((void)0)
322 #endif
323
324 /*
325  * Receive and process server output from @sock.
326  * Return number of characters received on success, -1 on error.
327  */
328 static int
329 recv_output(int sock)
330 {
331     /*
332      * Read a chunk of server output and feed its characters into a
333      * simple state machine.
334      * Initial state is SCANNING_ID.
335      * In state SCANNING_ID, buffer the character.  If it's a space,
336      * decode the ID that has been buffered, and enter state BUFFERING
337      * or COPYING depending on its value.
338      * In state BUFFERING, buffer the character.  If it's newline,
339      * pass ID and buffered text to servercmd(), then enter state
340      * SCANNING_ID.
341      * In state COPYING, pass the character to outch().  If it's
342      * newline, enter state SCANNING_ID.
343      */
344     static enum {
345         SCANNING_ID, BUFFERING, COPYING
346     } state = SCANNING_ID;
347     static int id;
348     static struct lbuf lbuf;
349     char buf[4096];
350     ssize_t n;
351     int i, ch, len, fd;
352     char *line;
353
354     n = read(sock, buf, sizeof(buf));
355     if (n < 0)
356         return -1;
357
358     for (i = 0; i < n; i++) {
359         ch = buf[i];
360         switch (state) {
361         case SCANNING_ID:
362             if (ch == '\n') {
363                 /* FIXME gripe unexpected! */
364                 lbuf_init(&lbuf);
365                 break;
366             }
367             lbuf_putc(&lbuf, ch);
368             if (ch != ' ')
369                 break;
370             line = lbuf_line(&lbuf);
371             id = parseid(line);
372             lbuf_init(&lbuf);
373
374             switch (id) {
375             case C_PROMPT:
376             case C_FLUSH:
377             case C_EXECUTE:
378             case C_EXIT:
379             case C_FLASH:
380             case C_INFORM:
381             case C_PIPE:
382             case C_REDIR:
383                 state = BUFFERING;
384                 break;
385             default:
386                 /* unknown or unexpected ID, treat like C_DATA */
387             case C_DATA:
388                 state = COPYING;
389                 break;
390             }
391             break;
392
393         case BUFFERING:
394             len = lbuf_putc(&lbuf, ch);
395             if (len) {
396                 line = lbuf_line(&lbuf);
397                 fd = servercmd(id, line, len);
398                 if (fd < 0) {
399                     /* failed execute */
400                     if (input_fd)
401                         close(input_fd);
402                     input_fd = 0;
403                     send_intr = 1;
404                 } else if (fd > 0) {
405                     /* successful execute, switch to batch file */
406                     assert(!input_fd);
407                     input_fd = fd;
408                 }
409                 lbuf_init(&lbuf);
410                 state = SCANNING_ID;
411             }
412             break;
413
414         case COPYING:
415             outch(ch);
416             if (ch == '\n')
417                 state = SCANNING_ID;
418         }
419     }
420
421     return n;
422 }
423
424 #ifdef HAVE_LIBREADLINE
425 static int use_readline;
426 static char *input_from_rl;
427 static int has_rl_input;
428
429 static void
430 input_handler(char *line)
431 {
432     input_from_rl = line;
433     has_rl_input = 1;
434     if (line && *line)
435         add_history(line);
436 }
437
438 static int
439 ring_from_rl(struct ring *inbuf)
440 {
441     size_t len;
442     int n;
443
444     assert(has_rl_input && input_from_rl);
445
446     len = strlen(input_from_rl);
447     n = ring_space(inbuf);
448     assert(n);
449
450     if (len >= (size_t)n) {
451         ring_putm(inbuf, input_from_rl, n);
452         memmove(input_from_rl, input_from_rl + n, len - n + 1);
453     } else {
454         ring_putm(inbuf, input_from_rl, len);
455         ring_putc(inbuf, '\n');
456         free(input_from_rl);
457         has_rl_input = 0;
458         n = len + 1;
459     }
460
461     return n;
462 }
463 #endif /* HAVE_LIBREADLINE */
464
465 /*
466  * Receive player input from @fd into @inbuf.
467  * Return 1 on receipt of input, zero on EOF, -1 on error.
468  */
469 static int
470 recv_input(int fd, struct ring *inbuf)
471 {
472     int n;
473     int res = 1;
474
475 #ifdef HAVE_LIBREADLINE
476     if (fd == 0 && use_readline) {
477         if (!has_rl_input)
478             rl_callback_read_char();
479         if (!has_rl_input)
480             return 1;
481         if (input_from_rl) {
482             n = ring_from_rl(inbuf);
483         } else
484             n = 0;
485     } else
486 #endif
487         n = ring_from_file(inbuf, fd);
488     if (n < 0)
489         return -1;
490     if (n == 0) {
491         /*
492          * Can't put EOF cookie into @inbuf here, it may not fit.
493          * Leave it to caller.
494          */
495         res = 0;
496     }
497
498     return res;
499 }
500
501 static int
502 send_input(int fd, struct ring *inbuf)
503 {
504     struct iovec iov[2];
505     int cnt, i, ch;
506     ssize_t res;
507
508     cnt = ring_to_iovec(inbuf, iov);
509     res = writev(fd, iov, cnt);
510     if (res < 0)
511         return res;
512
513     /* Copy input to @auxfp etc. */
514     for (i = 0; i < res; i++) {
515         ch = ring_getc(inbuf);
516         assert(ch != EOF);
517         if (ch != '\r')
518             save_input(ch);
519         if (auxfp)
520             putc(ch, auxfp);
521     }
522
523 #ifdef HAVE_LIBREADLINE
524     if (fd == 0 && use_readline && has_rl_input && input_from_rl)
525         ring_from_rl(inbuf);
526 #endif
527
528     return res;
529 }
530
531 static void
532 intr(int sig)
533 {
534     send_intr = 1;
535 }
536
537 /*
538  * Play on @sock.
539  * @history_file is the name of the history file, or null.
540  * The session must be in the playing phase.
541  * Return 0 when the session ended, -1 on error.
542  */
543 int
544 play(int sock, char *history_file)
545 {
546     /*
547      * Player input flows from @input_fd through recv_input() into
548      * ring buffer @inbuf, which drains into @sock.  This must not
549      * block.  Server output flows from @sock into recv_output().
550      * Reading @sock must not block.
551      */
552     struct sigaction sa;
553     struct ring inbuf;          /* input buffer, draining to SOCK */
554     int eof_fd0;                /* read fd 0 hit EOF? */
555     int partial_line_sent;      /* partial input line sent? */
556     int send_eof;               /* need to send EOF_COOKIE */
557     fd_set rdfd, wrfd;
558     int n;
559     int ret = -1;
560
561     sa.sa_flags = 0;
562     sigemptyset(&sa.sa_mask);
563     sa.sa_handler = intr;
564     sigaction(SIGINT, &sa, NULL);
565     sa.sa_handler = SIG_IGN;
566     sigaction(SIGPIPE, &sa, NULL);
567 #ifdef HAVE_LIBREADLINE
568     if (isatty(0)) {
569         use_readline = 1;
570         rl_already_prompted = 1;
571         rl_readline_name = "Empire";
572         if (history_file)
573             read_history(history_file);
574         rl_bind_key('\t', rl_insert);  /* Disable tab completion */
575         rl_callback_handler_install("", input_handler);
576     }
577 #endif /* HAVE_LIBREADLINE */
578
579     ring_init(&inbuf);
580     eof_fd0 = partial_line_sent = send_eof = send_intr = 0;
581     input_fd = 0;
582     sysdep_stdin_init();
583
584     for (;;) {
585         FD_ZERO(&rdfd);
586         FD_ZERO(&wrfd);
587
588         /*
589          * Want to read player input only when we don't need to send
590          * cookies, haven't reached EOF on fd 0, and @inbuf can accept
591          * some.
592          */
593         if (!send_intr && !send_eof && (input_fd || !eof_fd0)
594             && ring_space(&inbuf))
595             FD_SET(input_fd, &rdfd);
596         /* Want to send player input only when we have something */
597         if (send_intr || send_eof || ring_len(&inbuf))
598             FD_SET(sock, &wrfd);
599         /* Always want to read server output */
600         FD_SET(sock, &rdfd);
601
602         n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL);
603         if (n < 0) {
604             if (errno != EINTR) {
605                 perror("select");
606                 break;
607             }
608         }
609
610         if ((send_eof || send_intr) && partial_line_sent
611             && ring_putc(&inbuf, '\n') != EOF)
612             partial_line_sent = 0;
613         if (send_eof && !partial_line_sent
614             && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0)
615             send_eof = 0;
616         if (send_intr && !partial_line_sent
617             && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0) {
618             send_intr = 0;
619             if (input_fd) {
620                 /* execute aborted, switch back to fd 0 */
621                 close(input_fd);
622                 input_fd = 0;
623             }
624         }
625         if (n < 0)
626             continue;
627
628         /* read player input */
629         if (FD_ISSET(input_fd, &rdfd) && ring_space(&inbuf)) {
630             n = recv_input(input_fd, &inbuf);
631             if (n <= 0) {
632                 if (input_fd) {
633                     /* execute done, switch back to fd 0 */
634                     if (n < 0) {
635                         perror("read batch file");
636                         send_intr = 1;
637                     } else
638                         send_eof = 1;
639                     close(input_fd);
640                     input_fd = 0;
641                 } else {
642                     /* stop reading input, drain socket ring buffers */
643                     if (n < 0)
644                         perror("read stdin");
645                     send_eof = 1;
646                     eof_fd0 = 1;
647                     sa.sa_handler = SIG_DFL;
648                     sigaction(SIGINT, &sa, NULL);
649                     send_intr = 0;
650                 }
651             } else if (ring_len(&inbuf) > 0)
652                 partial_line_sent = ring_peek(&inbuf, -1) != '\n';
653         }
654
655         /* send it to the server */
656         if (FD_ISSET(sock, &wrfd)) {
657             n = send_input(sock, &inbuf);
658             if (n < 0) {
659                 perror("write socket");
660                 break;
661             }
662         }
663
664         /* read server output and print it */
665         if (FD_ISSET(sock, &rdfd)) {
666             n = recv_output(sock);
667             if (n < 0) {
668                 perror("read socket");
669                 break;
670             }
671             if (n == 0) {
672                 ret = 0;
673                 break;
674             }
675         }
676     }
677
678 #ifdef HAVE_LIBREADLINE
679     if (use_readline) {
680         rl_callback_handler_remove();
681         if (history_file)
682             write_history(history_file);
683     }
684 #endif
685     return ret;
686 }
687
688 void
689 prompt(int code, char *prompt, char *teles)
690 {
691     char pr[1024];
692
693     snprintf(pr, sizeof(pr), "%s%s", teles, prompt);
694 #ifdef HAVE_LIBREADLINE
695     if (use_readline) {
696         rl_set_prompt(pr);
697         rl_forced_update_display();
698     } else
699 #endif /* HAVE_LIBREADLINE */
700     {
701         printf("%s", pr);
702         fflush(stdout);
703     }
704     if (auxfp) {
705         fprintf(auxfp, "%s%s", teles, prompt);
706         fflush(auxfp);
707     }
708 }