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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.49 - (show annotations)
Sat Jun 28 01:16:00 2025 UTC (8 months, 2 weeks ago) by sysadm
Branch: MAIN
Changes since 1.48: +1 -1 lines
Content type: text/x-csrc
Fix bug

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

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