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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.47 - (show annotations)
Sat Jun 21 02:15:18 2025 UTC (8 months, 3 weeks ago) by sysadm
Branch: MAIN
Changes since 1.46: +6 -6 lines
Content type: text/x-csrc
Re-order included order files

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 epollfd = epoll_create1(0);
241 if (epollfd < 0)
242 {
243 log_error("epoll_create1() error (%d)\n", errno);
244 return -1;
245 }
246
247 ev.events = EPOLLIN;
248 ev.data.fd = STDIN_FILENO;
249 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1)
250 {
251 log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno);
252
253 if (close(epollfd) < 0)
254 {
255 log_error("close(epoll) error (%d)\n");
256 }
257 return -1;
258 }
259
260 flags = fcntl(STDIN_FILENO, F_GETFL, 0);
261 fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
262
263 loop = 1;
264
265 while (loop && pos >= len && !SYS_server_exit)
266 {
267 len = 0;
268 pos = 0;
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 while (pos < len)
371 {
372 unsigned char c = buf[pos++];
373
374 if (c == KEY_CONTROL)
375 {
376 if (in_control == 0)
377 {
378 in_control = 1;
379 i = 0;
380 continue;
381 }
382 }
383
384 if (in_control)
385 {
386 tmp[i++] = c;
387 if (i >= 2)
388 {
389 out = (int)tmp[0] * 256 + tmp[1];
390 in_control = 0;
391 break;
392 }
393 continue;
394 }
395
396 if (c == KEY_ESC)
397 {
398 if (in_esc == 0)
399 {
400 in_esc = 1;
401 in_ascii = 1;
402 i = 0;
403 continue;
404 }
405 else
406 {
407 out = KEY_CSI;
408 in_esc = 0;
409 break;
410 }
411 }
412
413 in_esc = 0;
414
415 if (in_ascii)
416 {
417 tmp[i++] = c;
418 if (i == 2 && (tmp[0] == 79 || tmp[0] == 91))
419 {
420 in_ascii = 0;
421 switch (tmp[1])
422 {
423 case 65:
424 out = KEY_UP;
425 break;
426 case 66:
427 out = KEY_DOWN;
428 break;
429 case 67:
430 out = KEY_RIGHT;
431 break;
432 case 68:
433 out = KEY_LEFT;
434 break;
435 default:
436 in_ascii = 1;
437 }
438 if (!in_ascii)
439 {
440 break;
441 }
442 }
443 if (i == 2 && tmp[0] == 91) // Fterm
444 {
445 in_ascii = 0;
446 switch (tmp[1])
447 {
448 case 86:
449 out = KEY_SHIFT_F1;
450 break;
451 case 90:
452 out = KEY_SHIFT_F2;
453 break;
454 case 97:
455 out = KEY_SHIFT_F3;
456 break;
457 case 98:
458 out = KEY_SHIFT_F4;
459 break;
460 case 99:
461 out = KEY_SHIFT_F5;
462 break;
463 case 100:
464 out = KEY_SHIFT_F6;
465 break;
466 case 101:
467 out = KEY_SHIFT_F7;
468 break;
469 case 102:
470 out = KEY_SHIFT_F8;
471 break;
472 case 103:
473 out = KEY_SHIFT_F9;
474 break;
475 case 104:
476 out = KEY_SHIFT_F10;
477 break;
478 case 107:
479 out = KEY_CTRL_F1;
480 break;
481 case 108:
482 out = KEY_CTRL_F2;
483 break;
484 case 109:
485 out = KEY_CTRL_F3;
486 break;
487 case 112:
488 out = KEY_CTRL_F6;
489 break;
490 case 113:
491 out = KEY_CTRL_F7;
492 break;
493 case 114:
494 out = KEY_CTRL_F8;
495 break;
496 case 115:
497 out = KEY_CTRL_F9;
498 break;
499 case 116:
500 out = KEY_CTRL_F10;
501 break;
502 default:
503 in_ascii = 1;
504 }
505 if (!in_ascii)
506 {
507 break;
508 }
509 }
510 if (i == 2 && tmp[0] == 79) // Xterm
511 {
512 in_ascii = 0;
513 switch (tmp[1])
514 {
515 case 80:
516 out = KEY_F1;
517 break;
518 case 81:
519 out = KEY_F2;
520 break;
521 case 82:
522 out = KEY_F3;
523 break;
524 case 83:
525 out = KEY_F4;
526 break;
527 default:
528 in_ascii = 1;
529 }
530 if (!in_ascii)
531 {
532 break;
533 }
534 }
535 if (i == 3 && tmp[0] == 91 && tmp[2] == 126)
536 {
537 in_ascii = 0;
538 switch (tmp[1])
539 {
540 case 49:
541 out = KEY_HOME;
542 break;
543 case 50:
544 out = KEY_INS;
545 break;
546 case 51:
547 out = KEY_DEL;
548 break;
549 case 52:
550 out = KEY_END;
551 break;
552 case 53:
553 out = KEY_PGUP;
554 break;
555 case 54:
556 out = KEY_PGDN;
557 break;
558 default:
559 in_ascii = 1;
560 }
561 if (!in_ascii)
562 {
563 break;
564 }
565 }
566 if (i == 4 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 126) // Fterm
567 {
568 in_ascii = 0;
569 switch (tmp[2])
570 {
571 case 49:
572 out = KEY_F1;
573 break;
574 case 50:
575 out = KEY_F2;
576 break;
577 case 51:
578 out = KEY_F3;
579 break;
580 case 52:
581 out = KEY_F4;
582 break;
583 case 53:
584 out = KEY_F5;
585 break;
586 case 55:
587 out = KEY_F6;
588 break;
589 case 56:
590 out = KEY_F7;
591 break;
592 case 57:
593 out = KEY_F8;
594 break;
595 default:
596 in_ascii = 1;
597 }
598 if (!in_ascii)
599 {
600 break;
601 }
602 }
603 if (i == 4 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 126) // Fterm
604 {
605 in_ascii = 0;
606 switch (tmp[2])
607 {
608 case 48:
609 out = KEY_F9;
610 break;
611 case 49:
612 out = KEY_F10;
613 break;
614 case 50:
615 out = KEY_F11; // Fterm
616 break;
617 case 51:
618 out = KEY_F11; // Xterm
619 break;
620 case 52:
621 out = KEY_F12; // Xterm
622 break;
623 default:
624 in_ascii = 1;
625 }
626 if (!in_ascii)
627 {
628 break;
629 }
630 }
631 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 53) // Xterm
632 {
633 in_ascii = 0;
634 switch (tmp[4])
635 {
636 case 65:
637 out = KEY_CTRL_UP;
638 break;
639 case 66:
640 out = KEY_CTRL_DOWN;
641 break;
642 case 67:
643 out = KEY_CTRL_RIGHT;
644 break;
645 case 68:
646 out = KEY_CTRL_LEFT;
647 break;
648 case 70:
649 out = KEY_CTRL_END;
650 break;
651 case 72:
652 out = KEY_CTRL_HOME;
653 break;
654 case 80:
655 out = KEY_CTRL_F1;
656 break;
657 case 81:
658 out = KEY_CTRL_F2;
659 break;
660 case 82:
661 out = KEY_CTRL_F3;
662 break;
663 default:
664 in_ascii = 1;
665 }
666 if (!in_ascii)
667 {
668 break;
669 }
670 }
671 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
672 {
673 in_ascii = 0;
674 switch (tmp[2])
675 {
676 case 53:
677 out = KEY_CTRL_F5;
678 break;
679 case 55:
680 out = KEY_CTRL_F6;
681 break;
682 case 56:
683 out = KEY_CTRL_F7;
684 break;
685 case 57:
686 out = KEY_CTRL_F8;
687 break;
688 default:
689 in_ascii = 1;
690 }
691 if (!in_ascii)
692 {
693 break;
694 }
695 }
696 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 53 && tmp[5] == 126) // Xterm
697 {
698 in_ascii = 0;
699 switch (tmp[2])
700 {
701 case 48:
702 out = KEY_CTRL_F9;
703 break;
704 case 49:
705 out = KEY_CTRL_F10;
706 break;
707 case 51:
708 out = KEY_CTRL_F11;
709 break;
710 case 52:
711 out = KEY_CTRL_F12;
712 break;
713 default:
714 in_ascii = 1;
715 }
716 if (!in_ascii)
717 {
718 break;
719 }
720 }
721 if (i == 5 && tmp[0] == 91 && tmp[1] == 49 && tmp[2] == 59 && tmp[3] == 50) // Xterm
722 {
723 in_ascii = 0;
724 switch (tmp[4])
725 {
726 case 80:
727 out = KEY_SHIFT_F1;
728 break;
729 case 81:
730 out = KEY_SHIFT_F2;
731 break;
732 case 82:
733 out = KEY_SHIFT_F3;
734 break;
735 case 83:
736 out = KEY_SHIFT_F4;
737 break;
738 default:
739 in_ascii = 1;
740 }
741 if (!in_ascii)
742 {
743 break;
744 }
745 }
746 if (i == 6 && tmp[0] == 91 && tmp[1] == 49 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
747 {
748 in_ascii = 0;
749 switch (tmp[2])
750 {
751 case 53:
752 out = KEY_SHIFT_F5;
753 break;
754 case 55:
755 out = KEY_SHIFT_F6;
756 break;
757 case 56:
758 out = KEY_SHIFT_F7;
759 break;
760 case 57:
761 out = KEY_SHIFT_F8;
762 break;
763 default:
764 in_ascii = 1;
765 }
766 if (!in_ascii)
767 {
768 break;
769 }
770 }
771 if (i == 6 && tmp[0] == 91 && tmp[1] == 50 && tmp[3] == 59 && tmp[4] == 50 && tmp[5] == 126) // Xterm
772 {
773 in_ascii = 0;
774 switch (tmp[2])
775 {
776 case 48:
777 out = KEY_SHIFT_F9;
778 break;
779 case 49:
780 out = KEY_SHIFT_F10;
781 break;
782 case 51:
783 out = KEY_SHIFT_F11;
784 break;
785 case 52:
786 out = KEY_SHIFT_F12;
787 break;
788 default:
789 in_ascii = 1;
790 }
791 if (!in_ascii)
792 {
793 break;
794 }
795 }
796
797 if (c == 'm')
798 {
799 in_ascii = 0;
800 }
801 continue;
802 }
803
804 out = ((int)c + 256) % 256;
805 break;
806 }
807
808 if (close(epollfd) < 0)
809 {
810 log_error("close(epoll) error (%d)\n");
811 }
812
813 // For ESC key
814 if (out == 0 && in_esc)
815 {
816 out = KEY_ESC;
817 }
818
819 // for debug
820 #ifdef _DEBUG
821 if (out != KEY_TIMEOUT && out != KEY_NULL)
822 {
823 log_common("Debug: -->[0x %x]\n", out);
824 }
825 #endif
826
827 return out;
828 }
829
830 int igetch_t(int sec)
831 {
832 int ch;
833 time_t t_begin = time(NULL);
834
835 do
836 {
837 ch = igetch(100);
838 } while (!SYS_server_exit && ch == KEY_TIMEOUT && (time(NULL) - t_begin < sec));
839
840 return ch;
841 }
842
843 void igetch_reset()
844 {
845 int ch;
846 do
847 {
848 ch = igetch(0);
849 } while (ch != KEY_NULL && ch != KEY_TIMEOUT);
850 }

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