///////////////////////////////////////////////////////////////////////////// // CygTerm - yet another Cygwin console // Copyright (C) 2000-2004 NSym. //--------------------------------------------------------------------------- // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License (GPL) as published by // the Free Software Foundation; either version 2 of the License, or (at // your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// // CygTerm - yet another Cygwin console // // Using Cygwin with a terminal emulator. // Written by NSym. // *** Web Pages *** // (English) http://www.dd.iij4u.or.jp/~nsym/cygwin/cygterm/index-e.html // (Japanese) http://www.dd.iij4u.or.jp/~nsym/cygwin/cygterm/index.html // // //modified by Sergey A. Belous in March 2006 to work as proxy32 to cygwin interface // static char Program[] = "ProxyCygTerm"; static char Version[] = "version 1.06 (2004/01/24) - modified by Sergey A. Belous in March 2006 to work as proxy32 to cygwin interface"; #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include // PTY device name //---------------- #define DEVPTY "/dev/ptmx" // command line of a shell //------------------------------------------------- char cmd_shell[4096] = ""; // terminal type & size //--------------------- char term_type[41] = "VT100"; struct winsize win_size = { 0,0,0,0}; // additional env vars given to a shell //------------------------------------- struct sh_env_t { struct sh_env_t* next; char env[1]; } sh_env = {NULL, ""}; sh_env_t* sh_envp = &sh_env; //================// // message output // //----------------// void msg_print(char* msg) { MessageBox(NULL, msg, Program, MB_OK | MB_ICONINFORMATION | MB_TOPMOST); } //=========================// // C-runtime error message // //-------------------------// void c_error(char* string = "") { char msg[1024]; char *ptr = msg; if (string != NULL) ptr += sprintf(ptr, "%s\n\n", string); sprintf(ptr, "%s\n", strerror(errno)); msg_print(msg); } //====================// // load configuration // //--------------------// void load_cfg() { char cfg[MAX_PATH]; // configuration file (.cfg) path // get cfg path from exe path if (GetModuleFileName(NULL, cfg, MAX_PATH) <= 0) { return; } char* bs = strrchr(cfg, '\\'); if (bs == NULL) { return; } char* dot = strrchr(bs, '.'); if (dot == NULL) { strcat(bs, ".cfg"); } else { strcpy(dot, ".cfg"); } // read each setting parameter FILE* fp; if ((fp = fopen(cfg, "r")) == NULL) { return; } char buf[BUFSIZ]; while (fgets(buf, sizeof(buf), fp) != NULL) { // "KEY = VALUE" format in each line. // skip leading/trailing blanks. KEY is not case-sensitive. char* p1; for (p1 = buf; isspace(*p1); ++p1) ; if (!isalpha(*p1)) { continue; // comment line with non-alphabet 1st char } char* name = p1; for (++p1; isalnum(*p1) || *p1 == '_'; ++p1) ; char* p2; for (p2 = p1; isspace(*p2); ++p2) ; if (*p2 != '=') { continue; } for (++p2; isspace(*p2); ++p2) ; char* val = p2; for (p2 += strlen(p2); isspace(*(p2-1)); --p2) ; *p1 = *p2 = 0; if (!strcasecmp(name, "SHELL")) { // shell command line strncpy(cmd_shell, val, sizeof(cmd_shell)-1); cmd_shell[sizeof(cmd_shell)-1] = 0; } else if (!strncasecmp(name, "ENV_", 4)) { // additional env vars given to a shell sh_env_t* e = (sh_env_t*)malloc(sizeof(sh_env_t)+strlen(val)); if (e != NULL) { strcpy(e->env, val); e->next = NULL; sh_envp = (sh_envp->next = e); } } } fclose(fp); } //=======================// // commandline arguments // //-----------------------// void get_args(int argc, char** argv) { for (++argv; *argv != NULL; ++argv) { if (!strcmp(*argv, "-s")) { // -s if (*(argv+1) != NULL) { ++argv, strcpy(cmd_shell, *argv); } } else if (!strcmp(*argv, "-v")) { // -v if (*(argv+1) != NULL) { ++argv; sh_env_t* e = (sh_env_t*)malloc(sizeof(sh_env_t)+strlen(*argv)); if (e != NULL) { strcpy(e->env, *argv); e->next = NULL; sh_envp = (sh_envp->next = e); } } } } } //========================================// // setup *argv[] from a string for exec() // //----------------------------------------// void get_argv(char **argv, int maxc, char *s) { int esc, sq, dq; // recognize (\) (') (") and tokenize int c, argc; char *p; esc = sq = dq = 0; for (argc = 0; argc < maxc-1; ++argc) { for ( ; isascii(*s) && isspace(*s); ++s) ; if (*s == 0) { break; } argv[argc] = p = s; while ((c = *s) != 0) { ++s; if (isspace(c) && !esc && !sq && !dq) { break; } if (c == '\'' && !esc && !dq) { sq ^= 1; } else if (c == '"' && !esc && !sq) { dq ^= 1; } else if (c == '\\' && !esc) { esc = 1; } else { esc = 0; *p++ = c; } } *p = 0; } // not to judge syntax errors // if (dq || sq || esc) { syntax error } // if (argc == maxc) { overflow } argv[argc] = NULL; } //=================// // shell execution // //-----------------// int exec_shell(int* sh_pid) { // open pty master int master; if ((master = open(DEVPTY, O_RDWR)) < 0) { return -1; } int pid; if ((pid = fork()) < 0) { return -1; } if (pid == 0) { // detach from control tty setsid(); // open pty slave int slave; if ((slave = open(ptsname(master), O_RDWR)) < 0) { exit(0); } // stdio redirection while (slave <= 2) { if ((slave = dup(slave)) < 0) { exit(0); } } int fd; for (fd = 0; fd < 3; ++fd) { close(fd); dup(slave); fcntl(fd, F_SETFD, 0); } for (fd = 3; fd < getdtablesize(); ++fd) { if (fcntl(fd, F_GETFD) == 0) { close(fd); } } // set env vars // set terminal type to VT100 char env_term[64]; sprintf(env_term, "TERM=VT100"); putenv(env_term); // set other additional env vars sh_env_t* e; for (e = sh_env.next; e != NULL; e = e->next) { putenv(e->env); } // execute a shell char *argv[32]; get_argv(argv, 32, cmd_shell); execv(argv[0], argv); // no error, exec() doesn't return c_error(argv[0]); exit(0); } *sh_pid = pid; return master; } //==================// // i/o buffer class IOBufferShell // //------------------// class IOBufferShell { private: int fd; u_char i_buf[4096]; u_char o_buf[4096]; int i_pos, i_len, o_pos; public: IOBufferShell(int channel) : fd(channel), i_pos(0), i_len(0), o_pos(0) { } operator int() { return fd; } void ungetc() { --i_pos; } bool flush_in(); bool getc(u_char*); bool nextc(u_char*); bool putc(u_char); bool flush_out(); }; // read bytes into input buffer //----------------------------- bool IOBufferShell::flush_in() { if ((i_len = read(fd, i_buf, sizeof(i_buf))) <= 0) return false; i_pos = 0; return true; } // get 1 char from input buffer //----------------------------- inline bool IOBufferShell::getc(u_char* c) { if (i_pos == i_len) return false; *c = i_buf[i_pos++]; return true; } // get next 1 char from input buffer //---------------------------------- inline bool IOBufferShell::nextc(u_char* c) { if (i_pos == i_len) if (!flush_in()) return false; *c = i_buf[i_pos++]; return true; } // put 1 char to output buffer //---------------------------- inline bool IOBufferShell::putc(u_char c) { if (o_pos == sizeof(o_buf)) if (!flush_out()) return false; o_buf[o_pos++] = c; return true; } // write bytes from output buffer //------------------------------- bool IOBufferShell::flush_out() { int n; for (int i = 0; i < o_pos; i += n) { if ((n = write(fd, o_buf+i, o_pos-i)) <= 0) return false; } o_pos = 0; return true; } //==================// // i/o buffer class IOBufferTerm // //------------------// class IOBufferTerm { private: u_char i_buf[4096]; u_char o_buf[4096]; int i_pos, i_len, o_pos; public: IOBufferTerm() : i_pos(0), i_len(0), o_pos(0) { } void ungetc() { --i_pos; } bool flush_in(); bool getc(u_char*); bool nextc(u_char*); bool putc(u_char); bool flush_out(); }; // read bytes into input buffer //----------------------------- bool IOBufferTerm::flush_in() { if ((i_len = read(0, i_buf, sizeof(i_buf))) <= 0) return false; i_pos = 0; return true; } // get 1 char from input buffer //----------------------------- inline bool IOBufferTerm::getc(u_char* c) { if (i_pos == i_len) return false; *c = i_buf[i_pos++]; return true; } // get next 1 char from input buffer //---------------------------------- inline bool IOBufferTerm::nextc(u_char* c) { if (i_pos == i_len) if (!flush_in()) return false; *c = i_buf[i_pos++]; return true; } // put 1 char to output buffer //---------------------------- inline bool IOBufferTerm::putc(u_char c) { if (o_pos == sizeof(o_buf)) if (!flush_out()) return false; o_buf[o_pos++] = c; return true; } // write bytes from output buffer //------------------------------- bool IOBufferTerm::flush_out() { int n; for (int i = 0; i < o_pos; i += n) { if ((n = write(1, o_buf+i, o_pos-i)) <= 0) return false; } o_pos = 0; return true; } //=========================// // TELNET command handling // (see RFC854 TELNET PROTOCOL SPECIFICATION) //-------------------------// enum { nIAC=255, nWILL=251, nWONT=252, nDO=253, nDONT=254 }; enum { sSEND=1, sIS=0, sSB=250, sSE=240 }; enum { oTERM=24, oNAWS=31 }; u_char telnet_cmd(IOBufferTerm* te) { u_char cmd, c; te->nextc(&cmd); if (cmd == sSB) { te->nextc(&c); // accept terminal size request if (c == oNAWS) { // "SB NAWS u_short col, row; te->nextc((u_char*)&col); te->nextc((u_char*)&col+1); // 00 00 (cols) te->nextc((u_char*)&row); te->nextc((u_char*)&row+1); // 00 00 (rows) te->nextc(&c); te->nextc(&c); // TAC SE" win_size.ws_col = ntohs(col); win_size.ws_row = ntohs(row); return (u_char)oNAWS; } while (c != nIAC) te->nextc(&c); // "... IAC SE" te->nextc(&c); } return cmd; } //=============================================// // relaying of a terminal emulator and a shell // //---------------------------------------------// void telnet_session(int sh_pty) { IOBufferTerm te; IOBufferShell sh = sh_pty; fd_set rtmp, rbits; FD_ZERO(&rtmp); FD_SET(0, &rtmp); FD_SET(sh, &rtmp); u_char c; int cr = 0; for (;;) { rbits = rtmp; if (select(FD_SETSIZE, &rbits, 0, 0, 0) <= 0) { break; } if (FD_ISSET(sh, &rbits)) { // send data from a shell to a terminal if (sh.flush_in() == false) { break; } while (sh.getc(&c) == true) { te.putc(c); } if (te.flush_out() == false) { break; } continue; // give priority to data from a shell } if (FD_ISSET(0, &rbits)) { // send data from a terminal to a shell if (te.flush_in() == false) { break; } while (te.getc(&c) == true) { if (c == nIAC) { u_char cmd = telnet_cmd(&te) ; if (cmd == oNAWS) { // resize pty by terminal size change notice ioctl(sh_pty, TIOCSWINSZ, &win_size); continue; } if (cmd != nIAC) { continue; } } else if (c == '\r') { cr = 1; } else if (c == '\n' || c == '\0') { if (cr) { // do not send LF or NUL just after CR cr = 0; continue; } } else { cr = 0; } sh.putc(c); } if (sh.flush_out() == false) { break; } } } } //=========================================================// // connection of terminal emulator and Cygwin shell // //---------------------------------------------------------// int main(int argc, char** argv) { int sh_pty = -1; int sh_pid; // load configuration load_cfg(); // read commandline arguments get_args(argc, argv); if (cmd_shell[0] == 0) { msg_print("missing shell"); return 0; } // execute a shell if ((sh_pty = exec_shell(&sh_pid)) < 0) { goto cleanup; } // relay the terminal emulator and the shell telnet_session(sh_pty); cleanup: if (sh_pty >= 0) { close(sh_pty); kill(sh_pid, SIGKILL); wait((int*)NULL); } return 0; } // This program is a Win32 application but, start as Cygwin main(). //----------------------------------------------------------------- extern "C" { void mainCRTStartup(void); void WinMainCRTStartup(void) { mainCRTStartup(); } }; //EOF