/[LeafOK_CVS]/lbbs/src/io.c
ViewVC logotype

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.41 - (show annotations)
Mon Jun 9 15:39:05 2025 UTC (9 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.40: +18 -0 lines
Content type: text/x-csrc
Add expanded KEY

1 /***************************************************************************
2 io.c - description
3 -------------------
4 Copyright : (C) 2004-2025 by Leaflet
5 Email : leaflet@leafok.com
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 3 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "io.h"
18 #include "log.h"
19 #include "common.h"
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <time.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/select.h>
28 #include <sys/ioctl.h>
29 #include <sys/epoll.h>
30 #include <libssh/libssh.h>
31 #include <libssh/server.h>
32 #include <libssh/callbacks.h>
33
34 static char stdout_buf[BUFSIZ];
35 static int stdout_buf_len = 0;
36 static int stdout_buf_offset = 0;
37
38 int prints(const char *format, ...)
39 {
40 char buf[BUFSIZ];
41 va_list args;
42 int ret;
43
44 va_start(args, format);
45 ret = vsnprintf(buf, sizeof(buf), format, args);
46 va_end(args);
47
48 if (ret > 0)
49 {
50 if (stdout_buf_len + ret > BUFSIZ)
51 {
52 iflush();
53 }
54
55 if (stdout_buf_len + ret <= BUFSIZ)
56 {
57 memcpy(stdout_buf + stdout_buf_len, buf, (size_t)ret);
58 stdout_buf_len += ret;
59 }
60 else
61 {
62 errno = EAGAIN;
63 ret = (BUFSIZ - stdout_buf_len - ret);
64 log_error("Output buffer is full, additional %d is required\n", ret);
65 }
66 }
67
68 return ret;
69 }
70
71 int outc(char c)
72 {
73 int ret;
74
75 if (stdout_buf_len + 1 > BUFSIZ)
76 {
77 iflush();
78 }
79
80 if (stdout_buf_len + 1 <= BUFSIZ)
81 {
82 stdout_buf[stdout_buf_len] = c;
83 stdout_buf_len++;
84 }
85 else
86 {
87 errno = EAGAIN;
88 ret = -1;
89 }
90
91 return ret;
92 }
93
94 int iflush()
95 {
96 int flags;
97 struct epoll_event ev, events[MAX_EVENTS];
98 int nfds, epollfd;
99 int retry;
100 int ret = 0;
101
102 epollfd = epoll_create1(0);
103 if (epollfd < 0)
104 {
105 log_error("epoll_create1() error (%d)\n", errno);
106 return -1;
107 }
108
109 ev.events = EPOLLOUT;
110 ev.data.fd = STDOUT_FILENO;
111 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1)
112 {
113 log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno);
114 if (close(epollfd) < 0)
115 {
116 log_error("close(epoll) error (%d)\n");
117 }
118 return -1;
119 }
120
121 // Set STDOUT as non-blocking
122 flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
123 fcntl(STDOUT_FILENO, F_SETFL, flags | O_NONBLOCK);
124
125 // Retry wait / flush for at most 3 times
126 retry = 3;
127 while (retry > 0 && !SYS_server_exit)
128 {
129 retry--;
130
131 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second
132
133 if (nfds < 0)
134 {
135 if (errno != EINTR)
136 {
137 log_error("epoll_wait() error (%d)\n", errno);
138 break;
139 }
140 continue;
141 }
142 else if (nfds == 0) // timeout
143 {
144 continue;
145 }
146
147 for (int i = 0; i < nfds; i++)
148 {
149 if (events[i].data.fd == STDOUT_FILENO)
150 {
151 while (stdout_buf_offset < stdout_buf_len && !SYS_server_exit) // write until complete or error
152 {
153 if (SSH_v2)
154 {
155 ret = ssh_channel_write(SSH_channel, stdout_buf + stdout_buf_offset, (uint32_t)(stdout_buf_len - stdout_buf_offset));
156 if (ret == SSH_ERROR)
157 {
158 log_error("ssh_channel_write() error: %s\n", ssh_get_error(SSH_session));
159 retry = 0;
160 break;
161 }
162 }
163 else
164 {
165 ret = (int)write(STDOUT_FILENO, stdout_buf + stdout_buf_offset, (size_t)(stdout_buf_len - stdout_buf_offset));
166 }
167 if (ret < 0)
168 {
169 if (errno == EAGAIN || errno == EWOULDBLOCK)
170 {
171 break;
172 }
173 else if (errno == EINTR)
174 {
175 continue;
176 }
177 else
178 {
179 log_error("write(STDOUT) error (%d)\n", errno);
180 retry = 0;
181 break;
182 }
183 }
184 else if (ret == 0) // broken pipe
185 {
186 retry = 0;
187 break;
188 }
189 else
190 {
191 stdout_buf_offset += ret;
192 if (stdout_buf_offset >= stdout_buf_len) // flush buffer completely
193 {
194 ret = 0;
195 stdout_buf_offset = 0;
196 stdout_buf_len = 0;
197 retry = 0;
198 break;
199 }
200 continue;
201 }
202 }
203 }
204 }
205 }
206
207 // Restore STDOUT flags
208 fcntl(STDOUT_FILENO, F_SETFL, flags);
209
210 if (close(epollfd) < 0)
211 {
212 log_error("close(epoll) error (%d)\n");
213 }
214
215 return ret;
216 }
217
218 int igetch(int timeout)
219 {
220 // static input buffer
221 static unsigned char buf[LINE_BUFFER_LEN];
222 static int len = 0;
223 static int pos = 0;
224
225 struct epoll_event ev, events[MAX_EVENTS];
226 int nfds, epollfd;
227 int ret;
228 int loop;
229
230 unsigned char tmp[LINE_BUFFER_LEN];
231 int out = KEY_NULL;
232 int in_esc = 0;
233 int in_ascii = 0;
234 int in_control = 0;
235 int i = 0;
236 int flags;
237
238 epollfd = epoll_create1(0);
239 if (epollfd < 0)
240 {
241 log_error("epoll_create1() error (%d)\n", errno);
242 return -1;
243 }
244
245 ev.events = EPOLLIN;
246 ev.data.fd = STDIN_FILENO;
247 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
248 {
249 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
250
251 if (close(epollfd) < 0)
252 {
253 log_error("close(epoll) error (%d)\n");
254 }
255 return -1;
256 }
257
258 flags = fcntl(STDIN_FILENO, F_GETFL, 0);
259 fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
260
261 loop = 1;
262
263 while (loop && pos >= len && !SYS_server_exit)
264 {
265 len = 0;
266 pos = 0;
267
268 if (SSH_v2 && ssh_channel_is_closed(SSH_channel))
269 {
270 log_error("SSH channel is closed\n");
271 loop = 0;
272 break;
273 }
274
275 nfds = epoll_wait(epollfd, events, MAX_EVENTS, timeout);
276
277 if (nfds < 0)
278 {
279 if (errno != EINTR)
280 {
281 log_error("epoll_wait() error (%d)\n", errno);
282 break;
283 }
284 continue;
285 }
286 else if (nfds == 0) // timeout
287 {
288 out = KEY_TIMEOUT;
289 break;
290 }
291
292 for (int i = 0; i < nfds; i++)
293 {
294 if (events[i].data.fd == STDIN_FILENO)
295 {
296 while (len < sizeof(buf) && !SYS_server_exit) // read until complete or error
297 {
298 if (SSH_v2)
299 {
300 ret = ssh_channel_read_nonblocking(SSH_channel, buf + len, sizeof(buf) - (uint32_t)len, 0);
301 if (ret == SSH_ERROR)
302 {
303 log_error("ssh_channel_read_nonblocking() error: %s\n", ssh_get_error(SSH_session));
304 loop = 0;
305 break;
306 }
307 else if (ret == SSH_EOF)
308 {
309 loop = 0;
310 break;
311 }
312 else if (ret == 0)
313 {
314 out = 0;
315 break; // Check whether channel is still open
316 }
317 }
318 else
319 {
320 ret = (int)read(STDIN_FILENO, buf + len, sizeof(buf) - (size_t)len);
321 }
322 if (ret < 0)
323 {
324 if (errno == EAGAIN || errno == EWOULDBLOCK)
325 {
326 out = 0;
327 loop = 0;
328 break;
329 }
330 else if (errno == EINTR)
331 {
332 continue;
333 }
334 else
335 {
336 log_error("read(STDIN) error (%d)\n", errno);
337 loop = 0;
338 break;
339 }
340 }
341 else if (ret == 0) // broken pipe
342 {
343 loop = 0;
344 break;
345 }
346 else
347 {
348 len += ret;
349 continue;
350 }
351 }
352 }
353 }
354
355 // For debug
356 // for (int j = pos; j < len; j++)
357 // {
358 // log_common("Debug: <--[%u]\n", (buf[j] + 256) % 256);
359 // }
360 }
361
362 fcntl(STDIN_FILENO, F_SETFL, flags);
363
364 while (pos < len)
365 {
366 unsigned char c = buf[pos++];
367
368 if (c == KEY_CONTROL)
369 {
370 if (in_control == 0)
371 {
372 in_control = 1;
373 i = 0;
374 continue;
375 }
376 }
377
378 if (in_control)
379 {
380 tmp[i++] = c;
381 if (i >= 2)
382 {
383 out = (int)tmp[0] * 256 + tmp[1];
384 in_control = 0;
385 break;
386 }
387 continue;
388 }
389
390 if (c == KEY_ESC)
391 {
392 if (in_esc == 0)
393 {
394 in_esc = 1;
395 in_ascii = 1;
396 i = 0;
397 continue;
398 }
399 else
400 {
401 out = KEY_CSI;
402 in_esc = 0;
403 break;
404 }
405 }
406
407 in_esc = 0;
408
409 if (in_ascii)
410 {
411 tmp[i++] = c;
412 if (i == 2 && (tmp[0] == 79 || tmp[0] == 91))
413 {
414 in_ascii = 0;
415 switch (tmp[1])
416 {
417 case 65:
418 out = KEY_UP;
419 break;
420 case 66:
421 out = KEY_DOWN;
422 break;
423 case 67:
424 out = KEY_RIGHT;
425 break;
426 case 68:
427 out = KEY_LEFT;
428 break;
429 default:
430 in_ascii = 1;
431 }
432 if (!in_ascii)
433 {
434 break;
435 }
436 }
437 if (i == 2 && tmp[0] == 91) // Fterm
438 {
439 in_ascii = 0;
440 switch (tmp[1])
441 {
442 case 86:
443 out = KEY_SHIFT_F1;
444 break;
445 case 90:
446 out = KEY_SHIFT_F2;
447 break;
448 case 97:
449 out = KEY_SHIFT_F3;
450 break;
451 case 98:
452 out = KEY_SHIFT_F4;
453 break;
454 case 99:
455 out = KEY_SHIFT_F5;
456 break;
457 case 100:
458 out = KEY_SHIFT_F6;
459 break;
460 case 101:
461 out = KEY_SHIFT_F7;
462 break;
463 case 102:
464 out = KEY_SHIFT_F8;
465 break;
466 case 103:
467 out = KEY_SHIFT_F9;
468 break;
469 case 104:
470 out = KEY_SHIFT_F10;
471 break;
472 case 107:
473 out = KEY_CTRL_F1;
474 break;
475 case 108:
476 out = KEY_CTRL_F2;
477 break;
478 case 109:
479 out = KEY_CTRL_F3;
480 break;
481 case 112:
482 out = KEY_CTRL_F6;
483 break;
484 case 113:
485 out = KEY_CTRL_F7;
486 break;
487 case 114:
488 out = KEY_CTRL_F8;
489 break;
490 case 115:
491 out = KEY_CTRL_F9;
492 break;
493 case 116:
494 out = KEY_CTRL_F10;
495 break;
496 default:
497 in_ascii = 1;
498 }
499 if (!in_ascii)
500 {
501 break;
502 }
503 }
504 if (i == 2 && tmp[0] == 79) // Xterm
505 {
506 in_ascii = 0;
507 switch (tmp[1])
508 {
509 case 80:
510 out = KEY_F1;
511 break;
512 case 81:
513 out = KEY_F2;
514 break;
515 case 82:
516 out = KEY_F3;
517 break;
518 case 83:
519 out = KEY_F4;
520 break;
521 default:
522 in_ascii = 1;
523 }
524 if (!in_ascii)
525 {
526 break;
527 }
528 }
529 if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
530 {
531 in_ascii = 0;
532 switch (tmp[1])
533 {
534 case 49:
535 out = KEY_HOME;
536 break;
537 case 51:
538 out = KEY_DEL;
539 break;
540 case 52:
541 out = KEY_END;
542 break;
543 case 53:
544 out = KEY_PGUP;
545 break;
546 case 54:
547 out = KEY_PGDN;
548 break;
549 default:
550 in_ascii = 1;
551 }
552 if (!in_ascii)
553 {
554 break;
555 }
556 }
557 if (i == 4 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 126) // Fterm
558 {
559 in_ascii = 0;
560 switch (tmp[2])
561 {
562 case 49:
563 out = KEY_F1;
564 break;
565 case 50:
566 out = KEY_F2;
567 break;
568 case 51:
569 out = KEY_F3;
570 break;
571 case 52:
572 out = KEY_F4;
573 break;
574 case 53:
575 out = KEY_F5;
576 break;
577 case 55:
578 out = KEY_F6;
579 break;
580 case 56:
581 out = KEY_F7;
582 break;
583 case 57:
584 out = KEY_F8;
585 break;
586 default:
587 in_ascii = 1;
588 }
589 if (!in_ascii)
590 {
591 break;
592 }
593 }
594 if (i == 4 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 126) // Fterm
595 {
596 in_ascii = 0;
597 switch (tmp[2])
598 {
599 case 48:
600 out = KEY_F9;
601 break;
602 case 49:
603 out = KEY_F10;
604 break;
605 case 50:
606 out = KEY_F11; // Fterm
607 break;
608 case 51:
609 out = KEY_F11; // Xterm
610 break;
611 case 52:
612 out = KEY_F12; // Xterm
613 break;
614 default:
615 in_ascii = 1;
616 }
617 if (!in_ascii)
618 {
619 break;
620 }
621 }
622 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 53) // Xterm
623 {
624 in_ascii = 0;
625 switch (tmp[4])
626 {
627 case 65:
628 out = KEY_CTRL_UP;
629 break;
630 case 66:
631 out = KEY_CTRL_DOWN;
632 break;
633 case 67:
634 out = KEY_CTRL_RIGHT;
635 break;
636 case 68:
637 out = KEY_CTRL_LEFT;
638 break;
639 case 70:
640 out = KEY_CTRL_END;
641 break;
642 case 72:
643 out = KEY_CTRL_HOME;
644 break;
645 case 80:
646 out = KEY_CTRL_F1;
647 break;
648 case 81:
649 out = KEY_CTRL_F2;
650 break;
651 case 82:
652 out = KEY_CTRL_F3;
653 break;
654 default:
655 in_ascii = 1;
656 }
657 if (!in_ascii)
658 {
659 break;
660 }
661 }
662 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
663 {
664 in_ascii = 0;
665 switch (tmp[2])
666 {
667 case 53:
668 out = KEY_CTRL_F5;
669 break;
670 case 55:
671 out = KEY_CTRL_F6;
672 break;
673 case 56:
674 out = KEY_CTRL_F7;
675 break;
676 case 57:
677 out = KEY_CTRL_F8;
678 break;
679 default:
680 in_ascii = 1;
681 }
682 if (!in_ascii)
683 {
684 break;
685 }
686 }
687 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
688 {
689 in_ascii = 0;
690 switch (tmp[2])
691 {
692 case 48:
693 out = KEY_CTRL_F9;
694 break;
695 case 49:
696 out = KEY_CTRL_F10;
697 break;
698 case 51:
699 out = KEY_CTRL_F11;
700 break;
701 case 52:
702 out = KEY_CTRL_F12;
703 break;
704 default:
705 in_ascii = 1;
706 }
707 if (!in_ascii)
708 {
709 break;
710 }
711 }
712 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 50) // Xterm
713 {
714 in_ascii = 0;
715 switch (tmp[4])
716 {
717 case 80:
718 out = KEY_SHIFT_F1;
719 break;
720 case 81:
721 out = KEY_SHIFT_F2;
722 break;
723 case 82:
724 out = KEY_SHIFT_F3;
725 break;
726 case 83:
727 out = KEY_SHIFT_F4;
728 break;
729 default:
730 in_ascii = 1;
731 }
732 if (!in_ascii)
733 {
734 break;
735 }
736 }
737 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
738 {
739 in_ascii = 0;
740 switch (tmp[2])
741 {
742 case 53:
743 out = KEY_SHIFT_F5;
744 break;
745 case 55:
746 out = KEY_SHIFT_F6;
747 break;
748 case 56:
749 out = KEY_SHIFT_F7;
750 break;
751 case 57:
752 out = KEY_SHIFT_F8;
753 break;
754 default:
755 in_ascii = 1;
756 }
757 if (!in_ascii)
758 {
759 break;
760 }
761 }
762 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
763 {
764 in_ascii = 0;
765 switch (tmp[2])
766 {
767 case 48:
768 out = KEY_SHIFT_F9;
769 break;
770 case 49:
771 out = KEY_SHIFT_F10;
772 break;
773 case 51:
774 out = KEY_SHIFT_F11;
775 break;
776 case 52:
777 out = KEY_SHIFT_F12;
778 break;
779 default:
780 in_ascii = 1;
781 }
782 if (!in_ascii)
783 {
784 break;
785 }
786 }
787
788 if (c == 'm')
789 {
790 in_ascii = 0;
791 }
792 continue;
793 }
794
795 out = ((int)c + 256) % 256;
796 break;
797 }
798
799 if (close(epollfd) < 0)
800 {
801 log_error("close(epoll) error (%d)\n");
802 }
803
804 // For ESC key
805 if (out == 0 && in_esc)
806 {
807 out = KEY_ESC;
808 }
809
810 // for debug
811 // if (out != KEY_TIMEOUT && out != KEY_NULL)
812 // {
813 // log_common("Debug: -->[0x %x]\n", out);
814 // }
815
816 return out;
817 }
818
819 int igetch_t(int sec)
820 {
821 int ch;
822 time_t t_begin = time(0);
823
824 do
825 {
826 ch = igetch(100);
827 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(0) - t_begin < sec));
828
829 return ch;
830 }
831
832 void igetch_reset()
833 {
834 int ch;
835 do
836 {
837 ch = igetch(0);
838 } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
839 }

webmaster@leafok.com
ViewVC Help
Powered by ViewVC 1.3.0-beta1