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

Contents of /lbbs/src/io.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.39 - (show annotations)
Thu Jun 5 05:24:56 2025 UTC (9 months, 1 week ago) by sysadm
Branch: MAIN
Changes since 1.38: +48 -3 lines
Content type: text/x-csrc
Add SSH2 support

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

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